1 /++ 2 Provides LZMA (aka .xz), gzip (.gz) and .tar file read-only support. 3 Combine to read .tar.xz and .tar.gz files, or use in conjunction with 4 other libraries to read other kinds of files. 5 6 Also has a custom archive called arcz read and write support. 7 It is designed to efficiently pack and randomly access large 8 numbers of similar files. Unlike .zip files, it will do 9 cross-file compression (meaning it can significantly shrink 10 archives with several small but similar files), and unlike 11 tar.gz files, it supports random access without decompressing 12 the whole archive to get an individual file. It is designed 13 for large numbers of small, similar files. 14 15 History: 16 tar code (and arsd module formed) originally written December 2019 to support my d_android library downloader. It was added to dub in March 2020 (dub v7.0). 17 18 The LZMA code is a D port of Igor Pavlov's LzmaDec.h, written in 2017 with contributions by Lasse Collin. It was ported to D by ketmar some time after that and included in the original version of `arsd.archive` in the first December 2019 release. 19 20 The arcz code was written by ketmar in 2016 and added to arsd.archive in March 2020. 21 22 A number of improvements were made with the help of Steven Schveighoffer on March 22, 2023. 23 24 `arsd.archive` was changed to require [arsd.core] on March 23, 2023 (dub v11.0). Previously, it was a standalone module. It uses arsd.core's exception helpers only at this time and you could turn them back into plain (though uninformative) D base `Exception` instances to remove the dependency if you wanted to keep the file independent. 25 +/ 26 module arsd.archive; 27 28 import arsd.core; 29 30 version(WithoutLzmaDecoder) {} else 31 version=WithLzmaDecoder; 32 33 version(WithoutArczCode) {} else 34 version=WithArczCode; 35 36 /+ 37 /++ 38 Reads a tar file and passes the chunks to your handler. Use it like: 39 40 TarFile f = TarFile("filename.tar"); 41 foreach(part; f) { 42 if(part.isNewFile) { 43 44 } 45 } 46 47 FIXME not implemented 48 +/ 49 struct TarFile { 50 this(string filename) { 51 52 } 53 } 54 +/ 55 56 inout(char)[] upToZero(inout(char)[] a) { 57 int i = 0; 58 while(i < a.length && a[i]) i++; 59 return a[0 .. i]; 60 } 61 62 63 /++ 64 A header of a file in the archive. This represents the 65 binary format of the header block. 66 +/ 67 align(512) 68 struct TarFileHeader { 69 align(1): 70 char[100] fileName_ = 0; 71 char[8] fileMode_ = 0; 72 char[8] ownerUid_ = 0; 73 char[8] ownerGid_ = 0; 74 char[12] size_ = 0; // in octal 75 char[12] mtime_ = 0; // octal unix timestamp 76 char[8] checksum_ = 0; // right????? 77 char[1] fileType_ = 0; // hard link, soft link, etc 78 char[100] linkFileName_ = 0; 79 char[6] ustarMagic_ = 0; // if "ustar\0", remaining fields are set 80 char[2] ustarVersion_ = 0; 81 char[32] ownerName_ = 0; 82 char[32] groupName_ = 0; 83 char[8] deviceMajorNumber_ = 0; 84 char[8] deviceMinorNumber_ = 0; 85 char[155] filenamePrefix_ = 0; 86 87 /// Returns the filename. You should cache the return value as long as TarFileHeader is in scope (it returns a slice after calling strlen) 88 const(char)[] filename() { 89 import core.stdc.string; 90 if(filenamePrefix_[0]) 91 return upToZero(filenamePrefix_[]) ~ upToZero(fileName_[]); 92 return upToZero(fileName_[]); 93 } 94 95 /++ 96 Returns the target of a symlink or hardlink. Remember, this returns a slice of the TarFileHeader structure, so once it goes out of scope, this slice will be dangling! 97 98 History: 99 Added March 24, 2023 (dub v11.0) 100 +/ 101 const(char)[] linkFileName() { 102 return upToZero(linkFileName_[]); 103 } 104 105 /// 106 ulong size() { 107 import core.stdc.stdlib; 108 return strtoul(size_.ptr, null, 8); 109 } 110 111 /// 112 TarFileType type() { 113 if(fileType_[0] == 0) 114 return TarFileType.normal; 115 else 116 return cast(TarFileType) (fileType_[0] - '0'); 117 } 118 119 /// 120 uint mode() { 121 import core.stdc.stdlib; 122 return cast(uint) strtoul(fileMode_.ptr, null, 8); 123 } 124 } 125 126 /// There's other types but this is all I care about. You can still detect the char by `((cast(char) type) + '0')` 127 enum TarFileType { 128 normal = 0, /// 129 hardLink = 1, /// 130 symLink = 2, /// 131 characterSpecial = 3, /// 132 blockSpecial = 4, /// 133 directory = 5, /// 134 fifo = 6 /// 135 } 136 137 138 139 140 /++ 141 Low level tar file processor. You must pass it a 142 TarFileHeader buffer as well as a size_t for context. 143 Both must be initialized to all zeroes on first call, 144 then not modified in between calls. 145 146 Each call must populate the dataBuffer with 512 bytes. 147 148 returns true if still work to do. 149 150 Please note that it currently only passes regular files, hard and soft links, and directories to your handler. 151 152 History: 153 [TarFileType.symLink] and [TarFileType.hardLink] used to be skipped by this function. On March 24, 2023, it was changed to send them to your `handleData` delegate too. The `data` argument to your handler will have the target of the link. Check `header.type` to know if it is a hard link, symbolic link, directory, normal file, or other special type (which are still currently skipped, but future proof yourself by either skipping or handling them now). 154 +/ 155 bool processTar( 156 TarFileHeader* header, 157 long* bytesRemainingOnCurrentFile, 158 const(ubyte)[] dataBuffer, 159 scope void delegate(TarFileHeader* header, bool isNewFile, bool fileFinished, const(ubyte)[] data) handleData 160 ) 161 { 162 assert(dataBuffer.length == 512); 163 assert(bytesRemainingOnCurrentFile !is null); 164 assert(header !is null); 165 166 if(*bytesRemainingOnCurrentFile) { 167 bool isNew = *bytesRemainingOnCurrentFile == header.size(); 168 if(*bytesRemainingOnCurrentFile <= 512) { 169 handleData(header, isNew, true, dataBuffer[0 .. cast(size_t) *bytesRemainingOnCurrentFile]); 170 *bytesRemainingOnCurrentFile = 0; 171 } else { 172 handleData(header, isNew, false, dataBuffer[]); 173 *bytesRemainingOnCurrentFile -= 512; 174 } 175 } else { 176 *header = *(cast(TarFileHeader*) dataBuffer.ptr); 177 auto s = header.size(); 178 *bytesRemainingOnCurrentFile = s; 179 if(header.type() == TarFileType.directory) 180 handleData(header, true, false, null); 181 if(header.type() == TarFileType.hardLink || header.type() == TarFileType.symLink) 182 handleData(header, true, true, cast(ubyte[]) header.linkFileName()); 183 if(s == 0 && header.type == TarFileType.normal) 184 return false; 185 } 186 187 return true; 188 } 189 190 /// 191 unittest { 192 /+ 193 void main() { 194 TarFileHeader tfh; 195 long size; 196 197 import std.stdio; 198 ubyte[512] buffer; 199 foreach(chunk; File("/home/me/test/pl.tar", "r").byChunk(buffer[])) { 200 processTar(&tfh, &size, buffer[], 201 (header, isNewFile, fileFinished, data) { 202 if(isNewFile) 203 writeln("**** " , header.filename, " ", header.size); 204 write(cast(string) data); 205 if(fileFinished) 206 writeln("+++++++++++++++"); 207 208 }); 209 } 210 } 211 212 main(); 213 +/ 214 } 215 216 217 // Advances data up to the end of the vla 218 ulong readVla(ref const(ubyte)[] data) { 219 ulong n; 220 221 n = data[0] & 0x7f; 222 if(!(data[0] & 0x80)) 223 data = data[1 .. $]; 224 225 int i = 0; 226 while(data[0] & 0x80) { 227 i++; 228 data = data[1 .. $]; 229 230 ubyte b = data[0]; 231 if(b == 0) return 0; 232 233 234 n |= cast(ulong) (b & 0x7F) << (i * 7); 235 } 236 return n; 237 } 238 239 /++ 240 decompressLzma lzma (.xz file) decoder/decompressor that works by passed functions. Can be used as a higher-level alternative to [XzDecoder]. decompressGzip is gzip (.gz file) decoder/decompresser) that works by passed functions. Can be used as an alternative to [std.zip], while using the same underlying zlib library. 241 242 Params: 243 chunkReceiver = a function that receives chunks of uncompressed data and processes them. Note that the chunk you receive will be overwritten once your function returns, so make sure you write it to a file or copy it to an outside array if you want to keep the data 244 245 bufferFiller = a function that fills the provided buffer as much as you can, then returns the slice of the buffer you actually filled. 246 247 chunkBuffer = an optional parameter providing memory that will be used to buffer uncompressed data chunks. If you pass `null`, it will allocate one for you. Any data in the buffer will be immediately overwritten. 248 249 inputBuffer = an optional parameter providing memory that will hold compressed input data. If you pass `null`, it will allocate one for you. You should NOT populate this buffer with any data; it will be immediately overwritten upon calling this function. The `inputBuffer` must be at least 32 bytes in size. 250 251 allowPartialChunks = can be set to true if you want `chunkReceiver` to be called as soon as possible, even if it is only partially full before the end of the input stream. The default is to fill the input buffer for every call to `chunkReceiver` except the last which has remainder data from the input stream. 252 253 History: 254 Added March 24, 2023 (dub v11.0) 255 +/ 256 version(WithLzmaDecoder) 257 void decompressLzma(scope void delegate(in ubyte[] chunk) chunkReceiver, scope ubyte[] delegate(ubyte[] buffer) bufferFiller, ubyte[] chunkBuffer = null, ubyte[] inputBuffer = null, bool allowPartialChunks = false) @trusted { 258 if(chunkBuffer is null) 259 chunkBuffer = new ubyte[](1024 * 32); 260 if(inputBuffer is null) 261 inputBuffer = new ubyte[](1024 * 32); 262 263 const(ubyte)[] compressedData = bufferFiller(inputBuffer[]); 264 265 XzDecoder decoder = XzDecoder(compressedData); 266 267 compressedData = decoder.unprocessed; 268 269 auto usableChunkBuffer = chunkBuffer; 270 271 while(!decoder.finished) { 272 auto newChunk = decoder.processData(usableChunkBuffer, compressedData); 273 274 auto chunk = chunkBuffer[0 .. (newChunk.ptr - chunkBuffer.ptr) + newChunk.length]; 275 276 if(chunk.length && (decoder.finished || allowPartialChunks || chunk.length == chunkBuffer.length)) { 277 chunkReceiver(chunk); 278 usableChunkBuffer = chunkBuffer; 279 } else if(!decoder.finished) { 280 // if we're here we got a partial chunk 281 usableChunkBuffer = chunkBuffer[chunk.length .. $]; 282 } 283 284 if(decoder.needsMoreData) { 285 import core.stdc.string; 286 memmove(inputBuffer.ptr, decoder.unprocessed.ptr, decoder.unprocessed.length); 287 288 auto newlyRead = bufferFiller(inputBuffer[decoder.unprocessed.length .. $]); 289 assert(newlyRead.ptr >= inputBuffer.ptr && newlyRead.ptr < inputBuffer.ptr + inputBuffer.length); 290 291 compressedData = inputBuffer[0 .. decoder.unprocessed.length + newlyRead.length]; 292 } else { 293 compressedData = decoder.unprocessed; 294 } 295 } 296 } 297 298 /// ditto 299 void decompressGzip(scope void delegate(in ubyte[] chunk) chunkReceiver, scope ubyte[] delegate(ubyte[] buffer) bufferFiller, ubyte[] chunkBuffer = null, ubyte[] inputBuffer = null, bool allowPartialChunks = false) @trusted { 300 301 import etc.c.zlib; 302 303 if(chunkBuffer is null) 304 chunkBuffer = new ubyte[](1024 * 32); 305 if(inputBuffer is null) 306 inputBuffer = new ubyte[](1024 * 32); 307 308 const(ubyte)[] compressedData = bufferFiller(inputBuffer[]); 309 310 z_stream zs; 311 312 scope(exit) 313 inflateEnd(&zs); // can return Z_STREAM_ERROR if state inconsistent 314 315 int windowBits = 15 + 32; // determine header from data 316 317 int err = inflateInit2(&zs, 15 + 32); // determine header from data 318 if(err) 319 throw ArsdException!"zlib"(err, zs.msg[0 .. 80].upToZero.idup); // FIXME: the 80 limit is arbitrary 320 // zs.msg is also an error message string 321 322 zs.next_in = compressedData.ptr; 323 zs.avail_in = cast(uint) compressedData.length; 324 325 while(true) { 326 zs.next_out = chunkBuffer.ptr; 327 zs.avail_out = cast(uint) chunkBuffer.length; 328 329 fill_more_chunk: 330 331 err = inflate(&zs, Z_NO_FLUSH); 332 333 if(err == Z_OK || err == Z_STREAM_END || err == Z_BUF_ERROR) { 334 import core.stdc.string; 335 336 auto decompressed = chunkBuffer[0 .. chunkBuffer.length - zs.avail_out]; 337 338 // if the buffer is full, we always send a chunk. 339 // partial chunks can be enabled, but we still will never send an empty chunk 340 // if we're at the end of a stream, we always send the final chunk 341 if(zs.avail_out == 0 || ((err == Z_STREAM_END || allowPartialChunks) && decompressed.length)) { 342 chunkReceiver(decompressed); 343 } else if(err != Z_STREAM_END) { 344 // need more data to fill the next chunk 345 if(zs.avail_in) { 346 memmove(inputBuffer.ptr, zs.next_in, zs.avail_in); 347 } 348 auto newlyRead = bufferFiller(inputBuffer[zs.avail_in .. $ - zs.avail_in]); 349 350 assert(newlyRead.ptr >= inputBuffer.ptr && newlyRead.ptr < inputBuffer.ptr + inputBuffer.length); 351 352 zs.next_in = inputBuffer.ptr; 353 zs.avail_in = cast(int) (zs.avail_in + newlyRead.length); 354 355 if(zs.avail_out) 356 goto fill_more_chunk; 357 } else { 358 assert(0, "progress impossible; your input buffer of compressed data might be too small"); 359 } 360 361 if(err == Z_STREAM_END) 362 break; 363 } else { 364 throw ArsdException!"zlib"(err, zs.msg[0 .. 80].upToZero.idup); // FIXME: the 80 limit is arbitrary 365 } 366 } 367 } 368 369 370 371 /// [decompressLzma] and [processTar] can be used together like this: 372 unittest { 373 /+ 374 import arsd.archive; 375 376 void main() { 377 import std.stdio; 378 auto file = File("test.tar.xz"); 379 380 TarFileHeader tfh; 381 long size; 382 ubyte[512] tarBuffer; 383 384 decompressLzma( 385 (in ubyte[] chunk) => cast(void) processTar(&tfh, &size, chunk, 386 (header, isNewFile, fileFinished, data) { 387 if(isNewFile) 388 writeln("**** " , header.filename, " ", header.size); 389 //write(cast(string) data); 390 if(fileFinished) 391 writeln("+++++++++++++++"); 392 }), 393 (ubyte[] buffer) => file.rawRead(buffer), 394 tarBuffer[] 395 ); 396 } 397 +/ 398 } 399 400 /++ 401 A simple .xz file decoder. 402 403 See the constructor and [processData] docs for details. 404 405 You might prefer using [decompressLzma] for a higher-level api. 406 407 FIXME: it doesn't implement very many checks, instead 408 assuming things are what it expects. Don't use this without 409 assertions enabled! 410 +/ 411 version(WithLzmaDecoder) 412 struct XzDecoder { 413 /++ 414 Start decoding by feeding it some initial data. You must 415 send it at least enough bytes for the header (> 16 bytes prolly); 416 try to send it a reasonably sized chunk. 417 418 It sets `this.unprocessed` to be a slice of the *tail* of the `initialData` 419 member, indicating leftover data after parsing the header. You will need to 420 pass this to [processData] at least once to start decoding the data left over 421 after the header. See [processData] for more information. 422 +/ 423 this(const(ubyte)[] initialData) { 424 425 ubyte[6] magic; 426 427 magic[] = initialData[0 .. magic.length]; 428 initialData = initialData[magic.length .. $]; 429 430 if(cast(string) magic != "\xFD7zXZ\0") 431 throw new Exception("not an xz file"); 432 433 ubyte[2] streamFlags = initialData[0 .. 2]; 434 initialData = initialData[2 .. $]; 435 436 // size of the check at the end in the footer. im just ignoring tbh 437 checkSize = streamFlags[1] == 0 ? 0 : (4 << ((streamFlags[1]-1) / 3)); 438 439 //uint crc32 = initialData[0 .. 4]; // FIXME just cast it. this is the crc of the flags. 440 initialData = initialData[4 .. $]; 441 442 443 // now we are into an xz block... 444 445 int blockHeaderSize = (initialData[0] + 1) * 4; 446 447 auto srcPostHeader = initialData[blockHeaderSize .. $]; 448 449 initialData = initialData[1 .. $]; 450 451 ubyte blockFlags = initialData[0]; 452 initialData = initialData[1 .. $]; 453 454 if(blockFlags & 0x40) { 455 compressedSize = readVla(initialData); 456 } 457 458 if(blockFlags & 0x80) { 459 uncompressedSize = readVla(initialData); 460 } 461 462 auto filterCount = (blockFlags & 0b11) + 1; 463 464 ubyte props; 465 466 foreach(f; 0 .. filterCount) { 467 auto fid = readVla(initialData); 468 auto sz = readVla(initialData); 469 470 assert(fid == 0x21); 471 assert(sz == 1); 472 473 props = initialData[0]; 474 initialData = initialData[1 .. $]; 475 } 476 477 //writeln(src.ptr); 478 //writeln(srcPostHeader.ptr); 479 480 // there should be some padding to a multiple of 4... 481 // three bytes of zeroes given the assumptions here 482 483 initialData = initialData[3 .. $]; 484 485 // and then a header crc 486 487 initialData = initialData[4 .. $]; // skip header crc 488 489 assert(initialData.ptr is srcPostHeader.ptr); 490 491 // skip unknown header bytes 492 while(initialData.ptr < srcPostHeader.ptr) { 493 initialData = initialData[1 .. $]; 494 } 495 496 // should finally be at compressed data... 497 498 //writeln(compressedSize); 499 //writeln(uncompressedSize); 500 501 if(Lzma2Dec_Allocate(&lzmaDecoder, props) != SRes.OK) { 502 assert(0); 503 } 504 505 Lzma2Dec_Init(&lzmaDecoder); 506 507 unprocessed = initialData; 508 } 509 510 ~this() { 511 LzmaDec_FreeProbs(&lzmaDecoder.decoder); 512 } 513 514 /++ 515 Continues an in-progress decompression of the `src` data, putting it into the `dest` buffer. 516 The `src` data must be at least 20 bytes long, but I'd recommend making it at larger. 517 518 Returns the slice of the head of the `dest` buffer actually filled, then updates the following 519 member variables of `XzDecoder`: 520 521 $(LIST 522 * [finished] will be `true` if the compressed data has been completely decompressed. 523 * [needsMoreData] will be `true` if more src data was needed to fill the `dest` buffer. Note that you can also check this from the return value; if the return value's length is less than the destination buffer's length and the decoder is not finished, that means you needed more data to fill it. 524 * And very importantly, [unprocessed] will contain a slice of the $(I tail of the `src` buffer) holding thus-far unprocessed data from the source buffer. This will almost always be set unless `dest` was big enough to hold the entire remaining uncompressed data. 525 ) 526 527 This function will not modify the `src` buffer in any way. This is why the [unprocessed] member holds a slice to its tail - it is your own responsibility to decide how to proceed. 528 529 If your `src` buffer contains the entire compressed file, you can pass `unprocessed` in a loop until finished: 530 531 --- 532 static import std.file; 533 auto compressedData = cast(immutable(ubyte)[]) std.file.read("file.xz"); // load it all into memory at once 534 XzDecoder decoder = XzDecoder(compressedData); 535 auto buffer = new ubyte[](4096); // to hold chunks of uncompressed data 536 ubyte[] wholeUncompressedFile; 537 while(!decoder.finished) { 538 // it returns the slice of buffer with new data, so we can append that 539 // to reconstruct the whole file. and then it sets `decoded.unprocessed` to 540 // a slice of what is left out of the source file to continue processing in 541 // the next iteration of the loop. 542 wholeUncompressedFile ~= decoder.processData(buffer, decoder.unprocessed); 543 } 544 // wholeUncompressedFile is now fully populated 545 --- 546 547 If you are reading from a file or some other streaming source, you may need to either move the unprocessed data back to the beginning of the buffer then load more into it, or copy it to a new, larger buffer and append more data then. 548 549 --- 550 import std.stdio; 551 auto file = File("file.xz"); 552 ubyte[] compressedDataBuffer = new ubyte[](1024 * 32); 553 554 // read the first chunk. all modifications will be done through `compressedDataBuffer` 555 // so we can make this part const for easier assignment to the slices `decoder.unprocessed` will hold 556 const(ubyte)[] compressedData = file.rawRead(compressedDataBuffer); 557 558 XzDecoder decoder = XzDecoder(compressedData); 559 560 // need to keep what was unprocessed after construction 561 compressedData = decoder.unprocessed; 562 563 auto buffer = new ubyte[](4096); // to hold chunks of uncompressed data 564 ubyte[] wholeUncompressedFile; 565 while(!decoder.finished) { 566 wholeUncompressedFile ~= decoder.processData(buffer, compressedData); 567 568 if(decoder.needsMoreData) { 569 // it needed more data to fill the buffer 570 571 // first, move the unprocessed data bask to the head 572 // you cannot necessarily use a D slice assign operator 573 // because the `unprocessed` is a slice of the `compressedDataBuffer`, 574 // meaning they might overlap. Instead, we'll use C's `memmove` 575 import core.stdc.string; 576 memmove(compressedDataBuffer.ptr, decoder.unprocessed.ptr, decoder.unprocessed.length); 577 578 579 // now we can read more data to fill in the tail of the buffer again 580 auto newlyRead = file.rawRead(compressedDataBuffer[decoder.unprocessed.length .. $]); 581 582 // the new compressed data ready to process is what we moved from before, 583 // now at the head of the buffer, plus what was just read, at the end of 584 // the same buffer 585 compressedData = compressedDataBuffer[0 .. decoder.unprocessed.length + newlyRead.length]; 586 } else { 587 // otherwise, the output buffer was full, but there's probably 588 // still more unprocessed data. Set it to be used on the next 589 // loop iteration. 590 compressedData = decoder.unprocessed; 591 } 592 } 593 // wholeUncompressedFile is now fully populated 594 --- 595 596 597 +/ 598 ubyte[] processData(ubyte[] dest, const ubyte[] src) { 599 600 size_t destLen = dest.length; 601 size_t srcLen = src.length; 602 603 ELzmaStatus status; 604 605 auto res = Lzma2Dec_DecodeToBuf( 606 &lzmaDecoder, 607 dest.ptr, 608 &destLen, 609 src.ptr, 610 &srcLen, 611 LZMA_FINISH_ANY, 612 &status 613 ); 614 615 if(res != 0) { 616 throw ArsdException!"Lzma2Dec_DecodeToBuf"(res); 617 } 618 619 /+ 620 import std.stdio; 621 writeln(res, " ", status); 622 writeln(srcLen); 623 writeln(destLen, ": ", cast(string) dest[0 .. destLen]); 624 +/ 625 626 if(status == LZMA_STATUS_NEEDS_MORE_INPUT) { 627 unprocessed = src[srcLen .. $]; 628 finished_ = false; 629 needsMoreData_ = true; 630 } else if(status == LZMA_STATUS_FINISHED_WITH_MARK || status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) { 631 unprocessed = null; 632 finished_ = true; 633 needsMoreData_ = false; 634 } else if(status == LZMA_STATUS_NOT_FINISHED) { 635 unprocessed = src[srcLen .. $]; 636 finished_ = false; 637 needsMoreData_ = false; 638 } else { 639 // wtf 640 throw ArsdException!"Unhandled LZMA_STATUS"(status); 641 } 642 643 return dest[0 .. destLen]; 644 } 645 646 /++ 647 Returns true after [processData] has finished decoding the compressed data. 648 +/ 649 bool finished() { 650 return finished_; 651 } 652 653 /++ 654 After calling [processData], this will return `true` if more data is required to fill 655 the destination buffer. 656 657 Please note that `needsMoreData` can return `false` before decompression is completely 658 [finished]; this would simply mean it satisfied the request to fill that one buffer. 659 660 In this case, you will want to concatenate [unprocessed] with new data, then call [processData] 661 again. Remember that [unprocessed] is a slice of the tail of the source buffer you passed to 662 `processData`, so if you want to reuse the same buffer, you may want to `memmove` it to the 663 head, then fill he tail again. 664 +/ 665 bool needsMoreData() { 666 return needsMoreData_; 667 } 668 669 private bool finished_; 670 private bool needsMoreData_; 671 672 CLzma2Dec lzmaDecoder; 673 int checkSize; 674 675 ulong compressedSize; /// 676 ulong uncompressedSize; /// 677 678 const(ubyte)[] unprocessed; /// 679 } 680 681 /// 682 /+ 683 version(WithLzmaDecoder) 684 unittest { 685 686 void main() { 687 ubyte[512] dest; // into tar size chunks! 688 ubyte[1024] src; 689 690 import std.stdio; 691 692 //auto file = File("/home/me/test/amazing.txt.xz", "rb"); 693 auto file = File("/home/me/Android/ldcdl/test.tar.xz", "rb"); 694 auto bfr = file.rawRead(src[]); 695 696 XzDecoder xzd = XzDecoder(bfr); 697 698 // not necessarily set, don't rely on them 699 writeln(xzd.compressedSize, " / ", xzd.uncompressedSize); 700 701 // for tar 702 TarFileHeader tfh; 703 long size; 704 705 long sum = 0; 706 while(!xzd.finished) { 707 // as long as your are not finished, there is more work to do. But it doesn't 708 // necessarily need more data, so that is a separate check. 709 if(xzd.needsMoreData) { 710 // if it needs more data, append new stuff to the end of the buffer, after 711 // the existing unprocessed stuff. If your buffer is too small, you may be 712 // forced to grow it here, but anything >= 1 KB seems OK in my tests. 713 bfr = file.rawRead(src[bfr.length - xzd.unprocessed.length .. $]); 714 } else { 715 // otherwise, you want to continue working with existing unprocessed data 716 bfr = cast(ubyte[]) xzd.unprocessed; 717 } 718 //write(cast(string) xzd.processData(dest[], bfr)); 719 720 auto buffer = xzd.processData(dest[], bfr); 721 722 // if the buffer is empty we are probably done 723 // or need more data, so continue the loop to evaluate. 724 if(buffer.length == 0) 725 continue; 726 727 // our tar code requires specifically 512 byte pieces 728 while(!xzd.finished && buffer.length != 512) { 729 // need more data hopefully 730 assert(xzd.needsMoreData); 731 // using the existing buffer... 732 bfr = file.rawRead(src[bfr.length - xzd.unprocessed.length .. $]); 733 auto nbuffer = xzd.processData(dest[buffer.length .. $], bfr); 734 buffer = dest[0 .. buffer.length + nbuffer.length]; 735 } 736 737 sum += buffer.length; 738 739 // process the buffer through the tar file handler 740 processTar(&tfh, &size, buffer[], 741 (header, isNewFile, fileFinished, data) { 742 if(isNewFile) 743 writeln("**** " , header.filename, " ", header.size); 744 //write(cast(string) data); 745 if(fileFinished) 746 writeln("+++++++++++++++"); 747 748 }); 749 } 750 751 writeln(sum); 752 } 753 754 main(); 755 } 756 +/ 757 758 version(WithArczCode) { 759 /* The code in this section was originally written by Ketmar Dark for his arcz.d module. I modified it afterward. */ 760 761 /** ARZ chunked archive format processor. 762 * 763 * This module provides `std.stdio.File`-like interface to ARZ archives. 764 * 765 * Copyright: Copyright Ketmar Dark, 2016 766 * 767 * License: Boost License 1.0 768 */ 769 // module iv.arcz; 770 771 // use Balz compressor if available 772 static if (__traits(compiles, { import iv.balz; })) enum arcz_has_balz = true; else enum arcz_has_balz = false; 773 static if (__traits(compiles, { import iv.zopfli; })) enum arcz_has_zopfli = true; else enum arcz_has_zopfli = false; 774 static if (arcz_has_balz) import iv.balz; 775 static if (arcz_has_zopfli) import iv.zopfli; 776 777 // comment this to free pakced chunk buffer right after using 778 // i.e. `AZFile` will allocate new block for each new chunk 779 //version = arcz_use_more_memory; 780 781 public import core.stdc.stdio : SEEK_SET, SEEK_CUR, SEEK_END; 782 783 784 // ////////////////////////////////////////////////////////////////////////// // 785 /// ARZ archive accessor. Use this to open ARZ archives, and open packed files from ARZ archives. 786 public struct ArzArchive { 787 private: 788 static assert(size_t.sizeof >= (void*).sizeof); 789 private import core.stdc.stdio : FILE, fopen, fclose, fread, fseek; 790 private import etc.c.zlib; 791 792 static struct ChunkInfo { 793 uint ofs; // offset in file 794 uint pksize; // packed chunk size (same as chunk size: chunk is unpacked) 795 } 796 797 static struct FileInfo { 798 string name; 799 uint chunk; 800 uint chunkofs; // offset of first file byte in unpacked chunk 801 uint size; // unpacked file size 802 } 803 804 static struct Nfo { 805 uint rc = 1; // refcounter 806 ChunkInfo[] chunks; 807 FileInfo[string] files; 808 uint chunkSize; 809 uint lastChunkSize; 810 bool useBalz; 811 FILE* afl; // archive file, we'll keep it opened 812 813 @disable this (this); // no copies! 814 815 static void decRef (size_t me) { 816 if (me) { 817 auto nfo = cast(Nfo*)me; 818 assert(nfo.rc); 819 if (--nfo.rc == 0) { 820 import core.memory : GC; 821 import core.stdc.stdlib : free; 822 if (nfo.afl !is null) fclose(nfo.afl); 823 nfo.chunks.destroy; 824 nfo.files.destroy; 825 nfo.afl = null; 826 GC.removeRange(cast(void*)nfo/*, Nfo.sizeof*/); 827 free(nfo); 828 debug(arcz_rc) { import core.stdc.stdio : printf; printf("Nfo %p freed\n", nfo); } 829 } 830 } 831 } 832 } 833 834 size_t nfop; // hide it from GC 835 836 private @property Nfo* nfo () { pragma(inline, true); return cast(Nfo*)nfop; } 837 void decRef () { pragma(inline, true); Nfo.decRef(nfop); nfop = 0; } 838 839 static uint readUint (FILE* fl) { 840 if (fl is null) throw new Exception("cannot read from closed file"); 841 uint v; 842 if (fread(&v, 1, v.sizeof, fl) != v.sizeof) throw new Exception("file reading error"); 843 version(BigEndian) { 844 import core.bitop : bswap; 845 v = bswap(v); 846 } else version(LittleEndian) { 847 // nothing to do 848 } else { 849 static assert(0, "wtf?!"); 850 } 851 return v; 852 } 853 854 static uint readUbyte (FILE* fl) { 855 if (fl is null) throw new Exception("cannot read from closed file"); 856 ubyte v; 857 if (fread(&v, 1, v.sizeof, fl) != v.sizeof) throw new Exception("file reading error"); 858 return v; 859 } 860 861 static void readBuf (FILE* fl, void[] buf) { 862 if (buf.length > 0) { 863 if (fl is null) throw new Exception("cannot read from closed file"); 864 if (fread(buf.ptr, 1, buf.length, fl) != buf.length) throw new Exception("file reading error"); 865 } 866 } 867 868 static T* xalloc(T, bool clear=true) (uint mem) if (T.sizeof > 0) { 869 import core.exception : onOutOfMemoryError; 870 assert(mem != 0); 871 static if (clear) { 872 import core.stdc.stdlib : calloc; 873 auto res = calloc(mem, T.sizeof); 874 if (res is null) onOutOfMemoryError(); 875 static if (is(T == struct)) { 876 import core.stdc.string : memcpy; 877 static immutable T i = T.init; 878 foreach (immutable idx; 0..mem) memcpy(res+idx, &i, T.sizeof); 879 } 880 debug(arcz_alloc) { import core.stdc.stdio : printf; printf("allocated %u bytes at %p\n", cast(uint)(mem*T.sizeof), res); } 881 return cast(T*)res; 882 } else { 883 import core.stdc.stdlib : malloc; 884 auto res = malloc(mem*T.sizeof); 885 if (res is null) onOutOfMemoryError(); 886 static if (is(T == struct)) { 887 import core.stdc.string : memcpy; 888 static immutable T i = T.init; 889 foreach (immutable idx; 0..mem) memcpy(res+idx, &i, T.sizeof); 890 } 891 debug(arcz_alloc) { import core.stdc.stdio : printf; printf("allocated %u bytes at %p\n", cast(uint)(mem*T.sizeof), res); } 892 return cast(T*)res; 893 } 894 } 895 896 static void xfree(T) (T* ptr) { 897 if (ptr !is null) { 898 import core.stdc.stdlib : free; 899 debug(arcz_alloc) { import core.stdc.stdio : printf; printf("freing at %p\n", ptr); } 900 free(ptr); 901 } 902 } 903 904 static if (arcz_has_balz) static ubyte balzDictSize (uint blockSize) { 905 foreach (ubyte bits; Balz.MinDictBits..Balz.MaxDictBits+1) { 906 if ((1U<<bits) >= blockSize) return bits; 907 } 908 return Balz.MaxDictBits; 909 } 910 911 // unpack exactly `destlen` bytes 912 static if (arcz_has_balz) static void unpackBlockBalz (void* dest, uint destlen, const(void)* src, uint srclen, uint blocksize) { 913 Unbalz bz; 914 bz.reinit(balzDictSize(blocksize)); 915 int ipos, opos; 916 auto dc = bz.decompress( 917 // reader 918 (buf) { 919 import core.stdc.string : memcpy; 920 if (ipos >= srclen) return 0; 921 uint rd = destlen-ipos; 922 if (rd > buf.length) rd = cast(uint)buf.length; 923 memcpy(buf.ptr, src+ipos, rd); 924 ipos += rd; 925 return rd; 926 }, 927 // writer 928 (buf) { 929 //if (opos+buf.length > destlen) throw new Exception("error unpacking archive"); 930 uint wr = destlen-opos; 931 if (wr > buf.length) wr = cast(uint)buf.length; 932 if (wr > 0) { 933 import core.stdc.string : memcpy; 934 memcpy(dest+opos, buf.ptr, wr); 935 opos += wr; 936 } 937 }, 938 // unpack length 939 destlen 940 ); 941 if (opos != destlen) throw new Exception("error unpacking archive"); 942 } 943 944 static void unpackBlockZLib (void* dest, uint destlen, const(void)* src, uint srclen, uint blocksize) { 945 z_stream zs; 946 zs.avail_in = 0; 947 zs.avail_out = 0; 948 // initialize unpacker 949 if (inflateInit2(&zs, 15) != Z_OK) throw new Exception("can't initialize zlib"); 950 scope(exit) inflateEnd(&zs); 951 zs.next_in = cast(typeof(zs.next_in))src; 952 zs.avail_in = srclen; 953 zs.next_out = cast(typeof(zs.next_out))dest; 954 zs.avail_out = destlen; 955 while (zs.avail_out > 0) { 956 auto err = inflate(&zs, Z_SYNC_FLUSH); 957 if (err != Z_STREAM_END && err != Z_OK) throw new Exception("error unpacking archive"); 958 if (err == Z_STREAM_END) break; 959 } 960 if (zs.avail_out != 0) throw new Exception("error unpacking archive"); 961 } 962 963 static void unpackBlock (void* dest, uint destlen, const(void)* src, uint srclen, uint blocksize, bool useBalz) { 964 if (useBalz) { 965 static if (arcz_has_balz) { 966 unpackBlockBalz(dest, destlen, src, srclen, blocksize); 967 } else { 968 throw new Exception("no Balz support was compiled in ArcZ"); 969 } 970 } else { 971 unpackBlockZLib(dest, destlen, src, srclen, blocksize); 972 } 973 } 974 975 public: 976 this (in ArzArchive arc) { 977 assert(nfop == 0); 978 nfop = arc.nfop; 979 if (nfop) ++nfo.rc; 980 } 981 982 this (this) { 983 if (nfop) ++nfo.rc; 984 } 985 986 ~this () { close(); } 987 988 void opAssign (in ArzArchive arc) { 989 if (arc.nfop) { 990 auto n = cast(Nfo*)arc.nfop; 991 ++n.rc; 992 } 993 decRef(); 994 nfop = arc.nfop; 995 } 996 997 void close () { decRef(); } 998 999 @property FileInfo[string] files () { return (nfop ? nfo.files : null); } 1000 1001 void openArchive (const(char)[] filename) { 1002 debug/*(arcz)*/ import core.stdc.stdio : printf; 1003 FILE* fl = null; 1004 scope(exit) if (fl !is null) fclose(fl); 1005 close(); 1006 if (filename.length == 0) throw new Exception("cannot open unnamed archive file"); 1007 if (false && filename.length < 2048) { // FIXME the alloca fails on win64 for some reason 1008 import core.stdc.stdlib : alloca; 1009 auto tfn = (cast(char*)alloca(filename.length+1))[0..filename.length+1]; 1010 tfn[0..filename.length] = filename[]; 1011 tfn[filename.length] = 0; 1012 fl = fopen(tfn.ptr, "rb"); 1013 } else { 1014 import core.stdc.stdlib : malloc, free; 1015 auto tfn = (cast(char*)malloc(filename.length+1))[0..filename.length+1]; 1016 if (tfn !is null) { 1017 tfn[0 .. filename.length] = filename[]; 1018 tfn[filename.length] = 0; 1019 scope(exit) free(tfn.ptr); 1020 fl = fopen(tfn.ptr, "rb"); 1021 } 1022 } 1023 if (fl is null) throw new Exception("cannot open archive file '"~filename.idup~"'"); 1024 char[4] sign; 1025 bool useBalz; 1026 readBuf(fl, sign[]); 1027 if (sign != "CZA2") throw new Exception("invalid archive file '"~filename.idup~"'"); 1028 switch (readUbyte(fl)) { 1029 case 0: useBalz = false; break; 1030 case 1: useBalz = true; break; 1031 default: throw new Exception("invalid version of archive file '"~filename.idup~"'"); 1032 } 1033 uint indexofs = readUint(fl); // index offset in file 1034 uint pkidxsize = readUint(fl); // packed index size 1035 uint idxsize = readUint(fl); // unpacked index size 1036 if (pkidxsize == 0 || idxsize == 0 || indexofs == 0) throw new Exception("invalid archive file '"~filename.idup~"'"); 1037 // now read index 1038 ubyte* idxbuf = null; 1039 scope(exit) xfree(idxbuf); 1040 { 1041 auto pib = xalloc!ubyte(pkidxsize); 1042 scope(exit) xfree(pib); 1043 if (fseek(fl, indexofs, 0) < 0) throw new Exception("seek error in archive file '"~filename.idup~"'"); 1044 readBuf(fl, pib[0..pkidxsize]); 1045 idxbuf = xalloc!ubyte(idxsize); 1046 unpackBlock(idxbuf, idxsize, pib, pkidxsize, idxsize, useBalz); 1047 } 1048 1049 // parse index and build structures 1050 uint idxbufpos = 0; 1051 1052 ubyte getUbyte () { 1053 if (idxsize-idxbufpos < ubyte.sizeof) throw new Exception("invalid index for archive file '"~filename.idup~"'"); 1054 return idxbuf[idxbufpos++]; 1055 } 1056 1057 uint getUint () { 1058 if (idxsize-idxbufpos < uint.sizeof) throw new Exception("invalid index for archive file '"~filename.idup~"'"); 1059 version(BigEndian) { 1060 import core.bitop : bswap; 1061 uint v = *cast(uint*)(idxbuf+idxbufpos); 1062 idxbufpos += 4; 1063 return bswap(v); 1064 } else version(LittleEndian) { 1065 uint v = *cast(uint*)(idxbuf+idxbufpos); 1066 idxbufpos += 4; 1067 return v; 1068 } else { 1069 static assert(0, "wtf?!"); 1070 } 1071 } 1072 1073 void getBuf (void[] buf) { 1074 if (buf.length > 0) { 1075 import core.stdc.string : memcpy; 1076 if (idxsize-idxbufpos < buf.length) throw new Exception("invalid index for archive file '"~filename.idup~"'"); 1077 memcpy(buf.ptr, idxbuf+idxbufpos, buf.length); 1078 idxbufpos += buf.length; 1079 } 1080 } 1081 1082 // allocate shared info struct 1083 Nfo* nfo = xalloc!Nfo(1); 1084 assert(nfo.rc == 1); 1085 debug(arcz_rc) { import core.stdc.stdio : printf; printf("Nfo %p allocated\n", nfo); } 1086 scope(failure) decRef(); 1087 nfop = cast(size_t)nfo; 1088 { 1089 import core.memory : GC; 1090 GC.addRange(nfo, Nfo.sizeof); 1091 } 1092 1093 // read chunk info and data 1094 nfo.useBalz = useBalz; 1095 nfo.chunkSize = getUint; 1096 auto ccount = getUint; // chunk count 1097 nfo.lastChunkSize = getUint; 1098 debug(arcz_dirread) printf("chunk size: %u\nchunk count: %u\nlast chunk size:%u\n", nfo.chunkSize, ccount, nfo.lastChunkSize); 1099 if (ccount == 0 || nfo.chunkSize < 1 || nfo.lastChunkSize < 1 || nfo.lastChunkSize > nfo.chunkSize) throw new Exception("invalid archive file '"~filename.idup~"'"); 1100 nfo.chunks.length = ccount; 1101 // chunk offsets and sizes 1102 foreach (ref ci; nfo.chunks) { 1103 ci.ofs = getUint; 1104 ci.pksize = getUint; 1105 } 1106 // read file count and info 1107 auto fcount = getUint; 1108 if (fcount == 0) throw new Exception("empty archive file '"~filename.idup~"'"); 1109 // calc name buffer position and size 1110 //immutable uint nbofs = idxbufpos+fcount*(5*4); 1111 //if (nbofs >= idxsize) throw new Exception("invalid index in archive file '"~filename.idup~"'"); 1112 //immutable uint nbsize = idxsize-nbofs; 1113 debug(arcz_dirread) printf("file count: %u\n", fcount); 1114 foreach (immutable _; 0..fcount) { 1115 uint nameofs = getUint; 1116 uint namelen = getUint; 1117 if (namelen == 0) { 1118 // skip unnamed file 1119 //throw new Exception("invalid archive file '"~filename.idup~"'"); 1120 getUint; // chunk number 1121 getUint; // offset in chunk 1122 getUint; // unpacked size 1123 debug(arcz_dirread) printf("skipped empty file\n"); 1124 } else { 1125 //if (nameofs >= nbsize || namelen > nbsize || nameofs+namelen > nbsize) throw new Exception("invalid index in archive file '"~filename.idup~"'"); 1126 if (nameofs >= idxsize || namelen > idxsize || nameofs+namelen > idxsize) throw new Exception("invalid index in archive file '"~filename.idup~"'"); 1127 FileInfo fi; 1128 auto nb = new char[](namelen); 1129 nb[0..namelen] = (cast(char*)idxbuf)[nameofs..nameofs+namelen]; 1130 fi.name = cast(string)(nb); // it is safe here 1131 fi.chunk = getUint; // chunk number 1132 fi.chunkofs = getUint; // offset in chunk 1133 fi.size = getUint; // unpacked size 1134 debug(arcz_dirread) printf("file size: %u\nfile chunk: %u\noffset in chunk:%u; name: [%.*s]\n", fi.size, fi.chunk, fi.chunkofs, cast(uint)fi.name.length, fi.name.ptr); 1135 nfo.files[fi.name] = fi; 1136 } 1137 } 1138 // transfer achive file ownership 1139 nfo.afl = fl; 1140 fl = null; 1141 } 1142 1143 bool exists (const(char)[] name) { if (nfop) return ((name in nfo.files) !is null); else return false; } 1144 1145 AZFile open (const(char)[] name) { 1146 if (!nfop) throw new Exception("can't open file from non-opened archive"); 1147 if (auto fi = name in nfo.files) { 1148 auto zl = xalloc!LowLevelPackedRO(1); 1149 scope(failure) xfree(zl); 1150 debug(arcz_rc) { import core.stdc.stdio : printf; printf("Zl %p allocated\n", zl); } 1151 zl.setup(nfo, fi.chunk, fi.chunkofs, fi.size); 1152 AZFile fl; 1153 fl.zlp = cast(size_t)zl; 1154 return fl; 1155 } 1156 throw new Exception("can't open file '"~name.idup~"' from archive"); 1157 } 1158 1159 private: 1160 static struct LowLevelPackedRO { 1161 private import etc.c.zlib; 1162 1163 uint rc = 1; 1164 size_t nfop; // hide it from GC 1165 1166 private @property inout(Nfo*) nfo () inout pure nothrow @trusted @nogc { pragma(inline, true); return cast(typeof(return))nfop; } 1167 static void decRef (size_t me) { 1168 if (me) { 1169 auto zl = cast(LowLevelPackedRO*)me; 1170 assert(zl.rc); 1171 if (--zl.rc == 0) { 1172 import core.stdc.stdlib : free; 1173 if (zl.chunkData !is null) free(zl.chunkData); 1174 version(arcz_use_more_memory) if (zl.pkdata !is null) free(zl.pkdata); 1175 Nfo.decRef(zl.nfop); 1176 free(zl); 1177 debug(arcz_rc) { import core.stdc.stdio : printf; printf("Zl %p freed\n", zl); } 1178 } else { 1179 //debug(arcz_rc) { import core.stdc.stdio : printf; printf("Zl %p; rc after decRef is %u\n", zl, zl.rc); } 1180 } 1181 } 1182 } 1183 1184 uint nextchunk; // next chunk to read 1185 uint curcpos; // position in current chunk 1186 uint curcsize; // number of valid bytes in `chunkData` 1187 uint stchunk; // starting chunk 1188 uint stofs; // offset in starting chunk 1189 uint totalsize; // total file size 1190 uint pos; // current file position 1191 uint lastrdpos; // last actual read position 1192 z_stream zs; 1193 ubyte* chunkData; // can be null 1194 version(arcz_use_more_memory) { 1195 ubyte* pkdata; 1196 uint pkdatasize; 1197 } 1198 1199 @disable this (this); 1200 1201 void setup (Nfo* anfo, uint astchunk, uint astofs, uint asize) { 1202 assert(anfo !is null); 1203 assert(rc == 1); 1204 nfop = cast(size_t)anfo; 1205 ++anfo.rc; 1206 nextchunk = stchunk = astchunk; 1207 //curcpos = 0; 1208 stofs = astofs; 1209 totalsize = asize; 1210 } 1211 1212 @property bool eof () { pragma(inline, true); return (pos >= totalsize); } 1213 1214 // return less than chunk size if our file fits in one non-full chunk completely 1215 uint justEnoughMemory () pure const nothrow @safe @nogc { 1216 pragma(inline, true); 1217 version(none) { 1218 return nfo.chunkSize; 1219 } else { 1220 return (totalsize < nfo.chunkSize && stofs+totalsize < nfo.chunkSize ? stofs+totalsize : nfo.chunkSize); 1221 } 1222 } 1223 1224 void unpackNextChunk () @system { 1225 if (nfop == 0) assert(0, "wtf?!"); 1226 //scope(failure) if (chunkData !is null) { xfree(chunkData); chunkData = null; } 1227 debug(arcz_unp) { import core.stdc.stdio : printf; printf("unpacking chunk %u\n", nextchunk); } 1228 // allocate buffer for unpacked data 1229 if (chunkData is null) { 1230 // optimize things a little: if our file fits in less then one chunk, allocate "just enough" memory 1231 chunkData = xalloc!(ubyte, false)(justEnoughMemory); 1232 } 1233 auto chunk = &nfo.chunks[nextchunk]; 1234 if (chunk.pksize == nfo.chunkSize) { 1235 // unpacked chunk, just read it 1236 debug(arcz_unp) { import core.stdc.stdio : printf; printf(" chunk is not packed\n"); } 1237 if (fseek(nfo.afl, chunk.ofs, 0) < 0) throw new Exception("ARCZ reading error"); 1238 if (fread(chunkData, 1, nfo.chunkSize, nfo.afl) != nfo.chunkSize) throw new Exception("ARCZ reading error"); 1239 curcsize = nfo.chunkSize; 1240 } else { 1241 // packed chunk, unpack it 1242 // allocate buffer for packed data 1243 version(arcz_use_more_memory) { 1244 import core.stdc.stdlib : realloc; 1245 if (pkdatasize < chunk.pksize) { 1246 import core.exception : onOutOfMemoryError; 1247 auto newpk = realloc(pkdata, chunk.pksize); 1248 if (newpk is null) onOutOfMemoryError(); 1249 debug(arcz_alloc) { import core.stdc.stdio : printf; printf("reallocated from %u to %u bytes; %p -> %p\n", cast(uint)pkdatasize, cast(uint)chunk.pksize, pkdata, newpk); } 1250 pkdata = cast(ubyte*)newpk; 1251 pkdatasize = chunk.pksize; 1252 } 1253 alias pkd = pkdata; 1254 } else { 1255 auto pkd = xalloc!(ubyte, false)(chunk.pksize); 1256 scope(exit) xfree(pkd); 1257 } 1258 if (fseek(nfo.afl, chunk.ofs, 0) < 0) throw new Exception("ARCZ reading error"); 1259 if (fread(pkd, 1, chunk.pksize, nfo.afl) != chunk.pksize) throw new Exception("ARCZ reading error"); 1260 uint upsize = (nextchunk == nfo.chunks.length-1 ? nfo.lastChunkSize : nfo.chunkSize); // unpacked chunk size 1261 immutable uint cksz = upsize; 1262 immutable uint jem = justEnoughMemory; 1263 if (upsize > jem) upsize = jem; 1264 debug(arcz_unp) { import core.stdc.stdio : printf; printf(" unpacking %u bytes to %u bytes\n", chunk.pksize, upsize); } 1265 ArzArchive.unpackBlock(chunkData, upsize, pkd, chunk.pksize, cksz, nfo.useBalz); 1266 curcsize = upsize; 1267 } 1268 curcpos = 0; 1269 // fix first chunk offset if necessary 1270 if (nextchunk == stchunk && stofs > 0) { 1271 // it's easier to just memmove it 1272 import core.stdc.string : memmove; 1273 assert(stofs < curcsize); 1274 memmove(chunkData, chunkData+stofs, curcsize-stofs); 1275 curcsize -= stofs; 1276 } 1277 ++nextchunk; // advance to next chunk 1278 } 1279 1280 void syncReadPos () { 1281 if (pos >= totalsize || pos == lastrdpos) return; 1282 immutable uint fcdata = nfo.chunkSize-stofs; // number of our bytes in the first chunk 1283 // does our pos lie in the first chunk? 1284 if (pos < fcdata) { 1285 // yep, just read it 1286 if (nextchunk != stchunk+1) { 1287 nextchunk = stchunk; 1288 unpackNextChunk(); // we'll need it anyway 1289 } else { 1290 // just rewind 1291 curcpos = 0; 1292 } 1293 curcpos += pos; 1294 lastrdpos = pos; 1295 return; 1296 } 1297 // find the chunk we want 1298 uint npos = pos-fcdata; 1299 uint xblock = stchunk+1+npos/nfo.chunkSize; 1300 uint curcstart = (xblock-(stchunk+1))*nfo.chunkSize+fcdata; 1301 if (xblock != nextchunk-1) { 1302 // read and unpack this chunk 1303 nextchunk = xblock; 1304 unpackNextChunk(); 1305 } else { 1306 // just rewind 1307 curcpos = 0; 1308 } 1309 assert(pos >= curcstart && pos < curcstart+nfo.chunkSize); 1310 uint skip = pos-curcstart; 1311 lastrdpos = pos; 1312 curcpos += skip; 1313 } 1314 1315 int read (void* buf, uint count) @system { 1316 if (buf is null) return -1; 1317 if (count == 0 || totalsize == 0) return 0; 1318 if (totalsize >= 0 && pos >= totalsize) return 0; // EOF 1319 syncReadPos(); 1320 assert(lastrdpos == pos); 1321 if (cast(long)pos+count > totalsize) count = totalsize-pos; 1322 auto res = count; 1323 while (count > 0) { 1324 debug(arcz_read) { import core.stdc.stdio : printf; printf("reading %u bytes; pos=%u; lastrdpos=%u; curcpos=%u; curcsize=%u\n", count, pos, lastrdpos, curcpos, curcsize); } 1325 import core.stdc.string : memcpy; 1326 if (curcpos >= curcsize) { 1327 unpackNextChunk(); // we want next chunk! 1328 debug(arcz_read) { import core.stdc.stdio : printf; printf(" *reading %u bytes; pos=%u; lastrdpos=%u; curcpos=%u; curcsize=%u\n", count, pos, lastrdpos, curcpos, curcsize); } 1329 } 1330 assert(curcpos < curcsize && curcsize != 0); 1331 int rd = (curcsize-curcpos >= count ? count : curcsize-curcpos); 1332 assert(rd > 0); 1333 memcpy(buf, chunkData+curcpos, rd); 1334 curcpos += rd; 1335 pos += rd; 1336 lastrdpos += rd; 1337 buf += rd; 1338 count -= rd; 1339 } 1340 assert(pos == lastrdpos); 1341 return res; 1342 } 1343 1344 long lseek (long ofs, int origin) { 1345 //TODO: overflow checks 1346 switch (origin) { 1347 case SEEK_SET: break; 1348 case SEEK_CUR: ofs += pos; break; 1349 case SEEK_END: 1350 if (ofs > 0) ofs = 0; 1351 if (-ofs > totalsize) ofs = -cast(long)totalsize; 1352 ofs += totalsize; 1353 break; 1354 default: 1355 return -1; 1356 } 1357 if (ofs < 0) return -1; 1358 if (totalsize >= 0 && ofs > totalsize) ofs = totalsize; 1359 pos = cast(uint)ofs; 1360 return pos; 1361 } 1362 } 1363 } 1364 1365 1366 // ////////////////////////////////////////////////////////////////////////// // 1367 /// Opened file. 1368 public struct AZFile { 1369 private: 1370 size_t zlp; 1371 1372 private @property inout(ArzArchive.LowLevelPackedRO)* zl () inout pure nothrow @trusted @nogc { pragma(inline, true); return cast(typeof(return))zlp; } 1373 private void decRef () { pragma(inline, true); ArzArchive.LowLevelPackedRO.decRef(zlp); zlp = 0; } 1374 1375 public: 1376 this (in AZFile afl) { 1377 assert(zlp == 0); 1378 zlp = afl.zlp; 1379 if (zlp) ++zl.rc; 1380 } 1381 1382 this (this) { 1383 if (zlp) ++zl.rc; 1384 } 1385 1386 ~this () { close(); } 1387 1388 void opAssign (in AZFile afl) { 1389 if (afl.zlp) { 1390 auto n = cast(ArzArchive.LowLevelPackedRO*)afl.zlp; 1391 ++n.rc; 1392 } 1393 decRef(); 1394 zlp = afl.zlp; 1395 } 1396 1397 void close () { decRef(); } 1398 1399 @property bool isOpen () const pure nothrow @safe @nogc { pragma(inline, true); return (zlp != 0); } 1400 @property uint size () const pure nothrow @safe @nogc { pragma(inline, true); return (zlp ? zl.totalsize : 0); } 1401 @property uint tell () const pure nothrow @safe @nogc { pragma(inline, true); return (zlp ? zl.pos : 0); } 1402 1403 void seek (long ofs, int origin=SEEK_SET) { 1404 if (!zlp) throw new Exception("can't seek in closed file"); 1405 auto res = zl.lseek(ofs, origin); 1406 if (res < 0) throw new Exception("seek error"); 1407 } 1408 1409 //TODO: overflow check 1410 T[] rawRead(T) (T[] buf) { 1411 if (!zlp) throw new Exception("can't read from closed file"); 1412 if (buf.length > 0) { 1413 auto res = zl.read(buf.ptr, cast(int) (buf.length*T.sizeof)); 1414 if (res == -1 || res%T.sizeof != 0) throw new Exception("read error"); 1415 return buf[0..res/T.sizeof]; 1416 } else { 1417 return buf[0..0]; 1418 } 1419 } 1420 } 1421 1422 1423 // ////////////////////////////////////////////////////////////////////////// // 1424 /** this class can be used to create archive file. 1425 * 1426 * Example: 1427 * -------------------- 1428 * import std.file, std.path, std.stdio : File; 1429 * 1430 * enum ArcName = "z00.arz"; 1431 * enum DirName = "experimental-docs"; 1432 * 1433 * ubyte[] rdbuf; 1434 * rdbuf.length = 65536; 1435 * 1436 * auto arcz = new ArzCreator(ArcName); 1437 * long total = 0; 1438 * foreach (DirEntry e; dirEntries(DirName, SpanMode.breadth)) { 1439 * if (e.isFile) { 1440 * assert(e.size < uint.max); 1441 * //writeln(e.name); 1442 * total += e.size; 1443 * string fname = e.name[DirName.length+1..$]; 1444 * arcz.newFile(fname, cast(uint)e.size); 1445 * auto fi = File(e.name); 1446 * for (;;) { 1447 * auto rd = fi.rawRead(rdbuf[]); 1448 * if (rd.length == 0) break; 1449 * arcz.rawWrite(rd[]); 1450 * } 1451 * } 1452 * } 1453 * arcz.close(); 1454 * writeln(total, " bytes packed to ", getSize(ArcName), " (", arcz.chunksWritten, " chunks, ", arcz.filesWritten, " files)"); 1455 * -------------------- 1456 */ 1457 final class ArzCreator { 1458 private import etc.c.zlib; 1459 private import core.stdc.stdio : FILE, fopen, fclose, ftell, fseek, fwrite; 1460 1461 public: 1462 //WARNING! don't change the order! 1463 enum Compressor { 1464 ZLib, // default 1465 Balz, 1466 BalzMax, // Balz, maximum compression 1467 Zopfli, // this will fallback to zlib if no zopfli support was compiled in 1468 } 1469 1470 private: 1471 static struct ChunkInfo { 1472 uint ofs; // offset in file 1473 uint pksize; // packed chunk size 1474 } 1475 1476 static struct FileInfo { 1477 string name; 1478 uint chunk; 1479 uint chunkofs; // offset of first file byte in unpacked chunk 1480 uint size; // unpacked file size 1481 } 1482 1483 private: 1484 ubyte[] chunkdata; 1485 uint cdpos; 1486 FILE* arcfl; 1487 ChunkInfo[] chunks; 1488 FileInfo[] files; 1489 uint lastChunkSize; 1490 uint statChunks, statFiles; 1491 Compressor cpr = Compressor.ZLib; 1492 1493 private: 1494 void writeUint (uint v) { 1495 if (arcfl is null) throw new Exception("write error"); 1496 version(BigEndian) { 1497 import core.bitop : bswap; 1498 v = bswap(v); 1499 } else version(LittleEndian) { 1500 // nothing to do 1501 } else { 1502 static assert(0, "wtf?!"); 1503 } 1504 if (fwrite(&v, 1, v.sizeof, arcfl) != v.sizeof) throw new Exception("write error"); // signature 1505 } 1506 1507 void writeUbyte (ubyte v) { 1508 if (arcfl is null) throw new Exception("write error"); 1509 if (fwrite(&v, 1, v.sizeof, arcfl) != v.sizeof) throw new Exception("write error"); // signature 1510 } 1511 1512 void writeBuf (const(void)[] buf) { 1513 if (buf.length > 0) { 1514 if (arcfl is null) throw new Exception("write error"); 1515 if (fwrite(buf.ptr, 1, buf.length, arcfl) != buf.length) throw new Exception("write error"); // signature 1516 } 1517 } 1518 1519 static if (arcz_has_balz) long writePackedBalz (const(void)[] upbuf) { 1520 assert(upbuf.length > 0 && upbuf.length < int.max); 1521 long res = 0; 1522 Balz bz; 1523 int ipos, opos; 1524 bz.reinit(ArzArchive.balzDictSize(cast(uint)upbuf.length)); 1525 bz.compress( 1526 // reader 1527 (buf) { 1528 import core.stdc.string : memcpy; 1529 if (ipos >= upbuf.length) return 0; 1530 uint rd = cast(uint)upbuf.length-ipos; 1531 if (rd > buf.length) rd = cast(uint)buf.length; 1532 memcpy(buf.ptr, upbuf.ptr+ipos, rd); 1533 ipos += rd; 1534 return rd; 1535 }, 1536 // writer 1537 (buf) { 1538 res += buf.length; 1539 writeBuf(buf[]); 1540 }, 1541 // max mode 1542 (cpr == Compressor.BalzMax) 1543 ); 1544 return res; 1545 } 1546 1547 static if (arcz_has_zopfli) long writePackedZopfli (const(void)[] upbuf) { 1548 ubyte[] indata; 1549 void* odata; 1550 size_t osize; 1551 ZopfliOptions opts; 1552 ZopfliCompress(opts, ZOPFLI_FORMAT_ZLIB, upbuf.ptr, upbuf.length, &odata, &osize); 1553 writeBuf(odata[0..osize]); 1554 ZopfliFree(odata); 1555 return cast(long)osize; 1556 } 1557 1558 long writePackedZLib (const(void)[] upbuf) { 1559 assert(upbuf.length > 0 && upbuf.length < int.max); 1560 long res = 0; 1561 z_stream zs; 1562 ubyte[2048] obuf; 1563 zs.next_out = obuf.ptr; 1564 zs.avail_out = cast(uint)obuf.length; 1565 zs.next_in = null; 1566 zs.avail_in = 0; 1567 // initialize packer 1568 if (deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED, 15, 9, 0) != Z_OK) throw new Exception("can't write packed data"); 1569 scope(exit) deflateEnd(&zs); 1570 zs.next_in = cast(typeof(zs.next_in))upbuf.ptr; 1571 zs.avail_in = cast(uint)upbuf.length; 1572 while (zs.avail_in > 0) { 1573 if (zs.avail_out == 0) { 1574 res += cast(uint)obuf.length; 1575 writeBuf(obuf[]); 1576 zs.next_out = obuf.ptr; 1577 zs.avail_out = cast(uint)obuf.length; 1578 } 1579 auto err = deflate(&zs, Z_NO_FLUSH); 1580 if (err != Z_OK) throw new Exception("zlib compression error"); 1581 } 1582 while (zs.avail_out != obuf.length) { 1583 res += cast(uint)obuf.length-zs.avail_out; 1584 writeBuf(obuf[0..$-zs.avail_out]); 1585 zs.next_out = obuf.ptr; 1586 zs.avail_out = cast(uint)obuf.length; 1587 auto err = deflate(&zs, Z_FINISH); 1588 if (err != Z_OK && err != Z_STREAM_END) throw new Exception("zlib compression error"); 1589 // succesfully flushed? 1590 //if (err != Z_STREAM_END) throw new VFSException("zlib compression error"); 1591 } 1592 return res; 1593 } 1594 1595 // return size of packed data written 1596 uint writePackedBuf (const(void)[] upbuf) { 1597 assert(upbuf.length > 0 && upbuf.length < int.max); 1598 long res = 0; 1599 final switch (cpr) { 1600 case Compressor.ZLib: 1601 res = writePackedZLib(upbuf); 1602 break; 1603 case Compressor.Balz: 1604 case Compressor.BalzMax: 1605 static if (arcz_has_balz) { 1606 res = writePackedBalz(upbuf); 1607 break; 1608 } else { 1609 throw new Exception("no Balz support was compiled in ArcZ"); 1610 } 1611 case Compressor.Zopfli: 1612 static if (arcz_has_zopfli) { 1613 res = writePackedZopfli(upbuf); 1614 //break; 1615 } else { 1616 //new Exception("no Zopfli support was compiled in ArcZ"); 1617 res = writePackedZLib(upbuf); 1618 } 1619 break; 1620 } 1621 if (res > uint.max) throw new Exception("output archive too big"); 1622 return cast(uint)res; 1623 } 1624 1625 void flushData () { 1626 if (cdpos > 0) { 1627 ChunkInfo ci; 1628 auto pos = ftell(arcfl); 1629 if (pos < 0 || pos >= uint.max) throw new Exception("output archive too big"); 1630 ci.ofs = cast(uint)pos; 1631 auto wlen = writePackedBuf(chunkdata[0..cdpos]); 1632 ci.pksize = wlen; 1633 if (cdpos == chunkdata.length && ci.pksize >= chunkdata.length) { 1634 // wow, this chunk is unpackable 1635 //{ import std.stdio; writeln("unpackable chunk found!"); } 1636 if (fseek(arcfl, pos, 0) < 0) throw new Exception("can't seek in output file"); 1637 writeBuf(chunkdata[0..cdpos]); 1638 version(Posix) { 1639 import core.stdc.stdio : fileno; 1640 import core.sys.posix.unistd : ftruncate; 1641 pos = ftell(arcfl); 1642 if (pos < 0 || pos >= uint.max) throw new Exception("output archive too big"); 1643 if (ftruncate(fileno(arcfl), cast(uint)pos) < 0) throw new Exception("error truncating output file"); 1644 } 1645 ci.pksize = cdpos; 1646 } 1647 if (cdpos < chunkdata.length) lastChunkSize = cast(uint)cdpos; 1648 cdpos = 0; 1649 chunks ~= ci; 1650 } else { 1651 lastChunkSize = cast(uint)chunkdata.length; 1652 } 1653 } 1654 1655 void closeArc () { 1656 flushData(); 1657 // write index 1658 //assert(ftell(arcfl) > 0 && ftell(arcfl) < uint.max); 1659 assert(chunkdata.length < uint.max); 1660 assert(chunks.length < uint.max); 1661 assert(files.length < uint.max); 1662 // create index in memory 1663 ubyte[] index; 1664 1665 void putUint (uint v) { 1666 index ~= v&0xff; 1667 index ~= (v>>8)&0xff; 1668 index ~= (v>>16)&0xff; 1669 index ~= (v>>24)&0xff; 1670 } 1671 1672 void putUbyte (ubyte v) { 1673 index ~= v; 1674 } 1675 1676 void putBuf (const(void)[] buf) { 1677 assert(buf.length > 0); 1678 index ~= (cast(const(ubyte)[])buf)[]; 1679 } 1680 1681 // create index in memory 1682 { 1683 // chunk size 1684 putUint(cast(uint)chunkdata.length); 1685 // chunk count 1686 putUint(cast(uint)chunks.length); 1687 // last chunk size 1688 putUint(lastChunkSize); // 0: last chunk is full 1689 // chunk offsets and sizes 1690 foreach (ref ci; chunks) { 1691 putUint(ci.ofs); 1692 putUint(ci.pksize); 1693 } 1694 // file count 1695 putUint(cast(uint)files.length); 1696 uint nbofs = cast(uint)index.length+cast(uint)files.length*(5*4); 1697 //uint nbofs = 0; 1698 // files 1699 foreach (ref fi; files) { 1700 // name: length(byte), chars 1701 assert(fi.name.length > 0 && fi.name.length <= 16384); 1702 putUint(nbofs); 1703 putUint(cast(uint)fi.name.length); 1704 nbofs += cast(uint)fi.name.length+1; // put zero byte there to ease C interfacing 1705 //putBuf(fi.name[]); 1706 // chunk number 1707 putUint(fi.chunk); 1708 // offset in unpacked chunk 1709 putUint(fi.chunkofs); 1710 // unpacked size 1711 putUint(fi.size); 1712 } 1713 // names 1714 foreach (ref fi; files) { 1715 putBuf(fi.name[]); 1716 putUbyte(0); // this means nothing, it is here just for convenience (hello, C!) 1717 } 1718 assert(index.length < uint.max); 1719 } 1720 auto cpos = ftell(arcfl); 1721 if (cpos < 0 || cpos > uint.max) throw new Exception("output archive too big"); 1722 // write packed index 1723 debug(arcz_writer) { import core.stdc.stdio : pinrtf; printf("index size: %u\n", cast(uint)index.length); } 1724 auto pkisz = writePackedBuf(index[]); 1725 debug(arcz_writer) { import core.stdc.stdio : pinrtf; printf("packed index size: %u\n", cast(uint)pkisz); } 1726 // write index info 1727 if (fseek(arcfl, 5, 0) < 0) throw new Exception("seek error"); 1728 // index offset in file 1729 writeUint(cast(uint) cpos); 1730 // packed index size 1731 writeUint(pkisz); 1732 // unpacked index size 1733 writeUint(cast(uint)index.length); 1734 // done 1735 statChunks = cast(uint)chunks.length; 1736 statFiles = cast(uint)files.length; 1737 } 1738 1739 public: 1740 this (const(char)[] fname, uint chunkSize=256*1024, Compressor acpr=Compressor.ZLib) { 1741 assert(chunkSize > 0 && chunkSize < 32*1024*1024); // arbitrary limit 1742 static if (!arcz_has_balz) { 1743 if (acpr == Compressor.Balz || acpr == Compressor.BalzMax) throw new Exception("no Balz support was compiled in ArcZ"); 1744 } 1745 static if (!arcz_has_zopfli) { 1746 //if (acpr == Compressor.Zopfli) throw new Exception("no Zopfli support was compiled in ArcZ"); 1747 } 1748 cpr = acpr; 1749 arcfl = fopen((fname ~ "\0").ptr, "wb"); 1750 if (arcfl is null) throw new Exception("can't create output file '"~fname.idup~"'"); 1751 cdpos = 0; 1752 chunkdata.length = chunkSize; 1753 scope(failure) { fclose(arcfl); arcfl = null; } 1754 writeBuf("CZA2"); // signature 1755 if (cpr == Compressor.Balz || cpr == Compressor.BalzMax) { 1756 writeUbyte(1); // version 1757 } else { 1758 writeUbyte(0); // version 1759 } 1760 writeUint(0); // offset to index 1761 writeUint(0); // packed index size 1762 writeUint(0); // unpacked index size 1763 } 1764 1765 ~this () { close(); } 1766 1767 void close () { 1768 if (arcfl !is null) { 1769 scope(exit) { fclose(arcfl); arcfl = null; } 1770 closeArc(); 1771 } 1772 chunkdata = null; 1773 chunks = null; 1774 files = null; 1775 lastChunkSize = 0; 1776 cdpos = 0; 1777 } 1778 1779 // valid after closing 1780 @property uint chunksWritten () const pure nothrow @safe @nogc { pragma(inline, true); return statChunks; } 1781 @property uint filesWritten () const pure nothrow @safe @nogc { pragma(inline, true); return statFiles; } 1782 1783 void newFile (string name, uint size) { 1784 FileInfo fi; 1785 assert(name.length <= 255); 1786 fi.name = name; 1787 fi.chunk = cast(uint)chunks.length; 1788 fi.chunkofs = cast(uint)cdpos; 1789 fi.size = size; 1790 files ~= fi; 1791 } 1792 1793 void rawWrite(T) (const(T)[] buffer) { 1794 if (buffer.length > 0) { 1795 auto src = cast(const(ubyte)*)buffer.ptr; 1796 auto len = buffer.length*T.sizeof; 1797 while (len > 0) { 1798 if (cdpos == chunkdata.length) flushData(); 1799 if (cdpos < chunkdata.length) { 1800 auto wr = chunkdata.length-cdpos; 1801 if (wr > len) wr = len; 1802 chunkdata[cdpos..cdpos+wr] = src[0..wr]; 1803 cdpos += wr; 1804 len -= wr; 1805 src += wr; 1806 } 1807 } 1808 } 1809 } 1810 } 1811 1812 1813 // ////////////////////////////////////////////////////////////////////////// // 1814 /* arcz file format: 1815 header 1816 ====== 1817 db 'CZA2' ; signature 1818 db version ; 0: zlib; 1: balz 1819 dd indexofs ; offset to packed index 1820 dd pkindexsz ; size of packed index 1821 dd upindexsz ; size of unpacked index 1822 1823 1824 index 1825 ===== 1826 dd chunksize ; unpacked chunk size in bytes 1827 dd chunkcount ; number of chunks in file 1828 dd lastchunksz ; size of last chunk (it may be incomplete); 0: last chunk is completely used (all `chunksize` bytes) 1829 1830 then chunk offsets and sizes follows: 1831 dd chunkofs ; from file start 1832 dd pkchunksz ; size of (possibly packed) chunk data; if it equals to `chunksize`, this chunk is not packed 1833 1834 then file list follows: 1835 dd filecount ; number of files in archive 1836 1837 then file info follows: 1838 dd nameofs ; (in index) 1839 dd namelen ; length of name (can't be 0) 1840 dd firstchunk ; chunk where file starts 1841 dd firstofs ; offset in first chunk (unpacked) where file starts 1842 dd filesize ; unpacked file size 1843 1844 then name buffer follows -- just bytes 1845 */ 1846 1847 } 1848 1849 version(WithLzmaDecoder) { 1850 1851 /* *************************************************** */ 1852 /* The rest of the file is copy/paste of external code */ 1853 /* *************************************************** */ 1854 1855 /* LzmaDec.h -- LZMA Decoder 1856 2017-04-03 : Igor Pavlov : Public domain */ 1857 // also by Lasse Collin 1858 /* ported to D by ketmar */ 1859 private nothrow @trusted @nogc: 1860 1861 //version = _LZMA_PROB32; 1862 /* _LZMA_PROB32 can increase the speed on some CPUs, 1863 but memory usage for CLzmaDec::probs will be doubled in that case */ 1864 1865 //version = _LZMA_SIZE_OPT; 1866 1867 alias Byte = ubyte; 1868 alias UInt16 = ushort; 1869 alias UInt32 = uint; 1870 alias SizeT = size_t; 1871 1872 version(_LZMA_PROB32) { 1873 alias CLzmaProb = UInt32; 1874 } else { 1875 alias CLzmaProb = UInt16; 1876 } 1877 1878 public enum SRes { 1879 OK, 1880 ERROR_UNSUPPORTED, 1881 ERROR_MEM, 1882 ERROR_DATA, 1883 ERROR_INPUT_EOF, 1884 ERROR_FAIL, 1885 } 1886 1887 /* ---------- LZMA Properties ---------- */ 1888 1889 public enum LZMA_PROPS_SIZE = 5; 1890 1891 public struct CLzmaProps { 1892 uint lc, lp, pb; 1893 UInt32 dicSize; 1894 } 1895 1896 /* LzmaProps_Decode - decodes properties 1897 Returns: 1898 SRes.OK 1899 SRes.ERROR_UNSUPPORTED - Unsupported properties 1900 */ 1901 1902 //!SRes LzmaProps_Decode(CLzmaProps *p, const(Byte)* data, uint size); 1903 1904 1905 /* ---------- LZMA Decoder state ---------- */ 1906 1907 /* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. 1908 Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ 1909 1910 enum LZMA_REQUIRED_INPUT_MAX = 20; 1911 1912 public struct CLzmaDec { 1913 private: 1914 CLzmaProps prop; 1915 CLzmaProb* probs; 1916 public Byte* dic; 1917 const(Byte)* buf; 1918 UInt32 range, code; 1919 public SizeT dicPos; 1920 public SizeT dicBufSize; 1921 UInt32 processedPos; 1922 UInt32 checkDicSize; 1923 uint state; 1924 UInt32[4] reps; 1925 uint remainLen; 1926 int needFlush; 1927 int needInitState; 1928 UInt32 numProbs; 1929 uint tempBufSize; 1930 Byte[LZMA_REQUIRED_INPUT_MAX] tempBuf; 1931 } 1932 1933 //#define LzmaDec_Construct(p) { (p).dic = 0; (p).probs = 0; } 1934 1935 //void LzmaDec_Init(CLzmaDec *p); 1936 1937 /* There are two types of LZMA streams: 1938 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. 1939 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ 1940 1941 public alias ELzmaFinishMode = int; 1942 public enum /*ELzmaFinishMode*/ { 1943 LZMA_FINISH_ANY, /* finish at any point */ 1944 LZMA_FINISH_END /* block must be finished at the end */ 1945 } 1946 1947 /* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! 1948 1949 You must use LZMA_FINISH_END, when you know that current output buffer 1950 covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. 1951 1952 If LZMA decoder sees end marker before reaching output limit, it returns SRes.OK, 1953 and output value of destLen will be less than output buffer size limit. 1954 You can check status result also. 1955 1956 You can use multiple checks to test data integrity after full decompression: 1957 1) Check Result and "status" variable. 1958 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. 1959 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. 1960 You must use correct finish mode in that case. */ 1961 1962 public alias ELzmaStatus = int; 1963 public enum /*ELzmaStatus*/ { 1964 LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ 1965 LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ 1966 LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ 1967 LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ 1968 LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ 1969 } 1970 1971 /* ELzmaStatus is used only as output value for function call */ 1972 1973 1974 /* ---------- Interfaces ---------- */ 1975 1976 /* There are 3 levels of interfaces: 1977 1) Dictionary Interface 1978 2) Buffer Interface 1979 3) One Call Interface 1980 You can select any of these interfaces, but don't mix functions from different 1981 groups for same object. */ 1982 1983 1984 /* There are two variants to allocate state for Dictionary Interface: 1985 1) LzmaDec_Allocate / LzmaDec_Free 1986 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs 1987 You can use variant 2, if you set dictionary buffer manually. 1988 For Buffer Interface you must always use variant 1. 1989 1990 LzmaDec_Allocate* can return: 1991 SRes.OK 1992 SRes.ERROR_MEM - Memory allocation error 1993 SRes.ERROR_UNSUPPORTED - Unsupported properties 1994 */ 1995 1996 /* 1997 SRes LzmaDec_AllocateProbs(CLzmaDec *p, const(Byte)* props, uint propsSize); 1998 void LzmaDec_FreeProbs(CLzmaDec *p); 1999 2000 SRes LzmaDec_Allocate(CLzmaDec *state, const(Byte)* prop, uint propsSize); 2001 void LzmaDec_Free(CLzmaDec *state); 2002 */ 2003 2004 /* ---------- Dictionary Interface ---------- */ 2005 2006 /* You can use it, if you want to eliminate the overhead for data copying from 2007 dictionary to some other external buffer. 2008 You must work with CLzmaDec variables directly in this interface. 2009 2010 STEPS: 2011 LzmaDec_Constr() 2012 LzmaDec_Allocate() 2013 for (each new stream) 2014 { 2015 LzmaDec_Init() 2016 while (it needs more decompression) 2017 { 2018 LzmaDec_DecodeToDic() 2019 use data from CLzmaDec::dic and update CLzmaDec::dicPos 2020 } 2021 } 2022 LzmaDec_Free() 2023 */ 2024 2025 /* LzmaDec_DecodeToDic 2026 2027 The decoding to internal dictionary buffer (CLzmaDec::dic). 2028 You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! 2029 2030 finishMode: 2031 It has meaning only if the decoding reaches output limit (dicLimit). 2032 LZMA_FINISH_ANY - Decode just dicLimit bytes. 2033 LZMA_FINISH_END - Stream must be finished after dicLimit. 2034 2035 Returns: 2036 SRes.OK 2037 status: 2038 LZMA_STATUS_FINISHED_WITH_MARK 2039 LZMA_STATUS_NOT_FINISHED 2040 LZMA_STATUS_NEEDS_MORE_INPUT 2041 LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK 2042 SRes.ERROR_DATA - Data error 2043 */ 2044 2045 //SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const(Byte)* src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); 2046 2047 2048 /* ---------- Buffer Interface ---------- */ 2049 2050 /* It's zlib-like interface. 2051 See LzmaDec_DecodeToDic description for information about STEPS and return results, 2052 but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need 2053 to work with CLzmaDec variables manually. 2054 2055 finishMode: 2056 It has meaning only if the decoding reaches output limit (*destLen). 2057 LZMA_FINISH_ANY - Decode just destLen bytes. 2058 LZMA_FINISH_END - Stream must be finished after (*destLen). 2059 */ 2060 2061 //SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const(Byte)* src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); 2062 2063 2064 /* ---------- One Call Interface ---------- */ 2065 2066 /* LzmaDecode 2067 2068 finishMode: 2069 It has meaning only if the decoding reaches output limit (*destLen). 2070 LZMA_FINISH_ANY - Decode just destLen bytes. 2071 LZMA_FINISH_END - Stream must be finished after (*destLen). 2072 2073 Returns: 2074 SRes.OK 2075 status: 2076 LZMA_STATUS_FINISHED_WITH_MARK 2077 LZMA_STATUS_NOT_FINISHED 2078 LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK 2079 SRes.ERROR_DATA - Data error 2080 SRes.ERROR_MEM - Memory allocation error 2081 SRes.ERROR_UNSUPPORTED - Unsupported properties 2082 SRes.ERROR_INPUT_EOF - It needs more bytes in input buffer (src). 2083 */ 2084 2085 /* 2086 SRes LzmaDecode(Byte *dest, SizeT *destLen, const(Byte)* src, SizeT *srcLen, 2087 const(Byte)* propData, uint propSize, ELzmaFinishMode finishMode, 2088 ELzmaStatus *status, ISzAllocPtr alloc); 2089 */ 2090 2091 // ////////////////////////////////////////////////////////////////////////// // 2092 private: 2093 2094 enum kNumTopBits = 24; 2095 enum kTopValue = 1U<<kNumTopBits; 2096 2097 enum kNumBitModelTotalBits = 11; 2098 enum kBitModelTotal = 1<<kNumBitModelTotalBits; 2099 enum kNumMoveBits = 5; 2100 2101 enum RC_INIT_SIZE = 5; 2102 2103 //enum NORMALIZE = "if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); }"; 2104 2105 //#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) 2106 //#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); 2107 //#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); 2108 enum GET_BIT2(string p, string i, string A0, string A1) = 2109 "ttt = *("~p~"); if (range < kTopValue) { range <<= 8; code = (code<<8)|(*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound)\n"~ 2110 "{ range = bound; *("~p~") = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); "~i~" = ("~i~"+"~i~"); "~A0~" } else\n"~ 2111 "{ range -= bound; code -= bound; *("~p~") = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); "~i~" = ("~i~"+"~i~")+1; "~A1~" }"; 2112 //#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) 2113 enum GET_BIT(string p, string i) = GET_BIT2!(p, i, "", ""); 2114 2115 enum TREE_GET_BIT(string probs, string i) = "{"~GET_BIT!("("~probs~"+"~i~")", i)~"}"; 2116 2117 enum TREE_DECODE(string probs, string limit, string i) = 2118 "{ "~i~" = 1; do { "~TREE_GET_BIT!(probs, i)~" } while ("~i~" < "~limit~"); "~i~" -= "~limit~"; }"; 2119 2120 2121 version(_LZMA_SIZE_OPT) { 2122 enum TREE_6_DECODE(string probs, string i) = TREE_DECODE!(probs, "(1<<6)", i); 2123 } else { 2124 enum TREE_6_DECODE(string probs, string i) = 2125 "{ "~i~" = 1;\n"~ 2126 TREE_GET_BIT!(probs, i)~ 2127 TREE_GET_BIT!(probs, i)~ 2128 TREE_GET_BIT!(probs, i)~ 2129 TREE_GET_BIT!(probs, i)~ 2130 TREE_GET_BIT!(probs, i)~ 2131 TREE_GET_BIT!(probs, i)~ 2132 i~" -= 0x40; }"; 2133 } 2134 2135 enum NORMAL_LITER_DEC = GET_BIT!("prob + symbol", "symbol"); 2136 enum MATCHED_LITER_DEC = 2137 "matchByte <<= 1;\n"~ 2138 "bit = (matchByte & offs);\n"~ 2139 "probLit = prob + offs + bit + symbol;\n"~ 2140 GET_BIT2!("probLit", "symbol", "offs &= ~bit;", "offs &= bit;"); 2141 2142 enum NORMALIZE_CHECK = "if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); }"; 2143 2144 //#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) 2145 //#define UPDATE_0_CHECK range = bound; 2146 //#define UPDATE_1_CHECK range -= bound; code -= bound; 2147 enum GET_BIT2_CHECK(string p, string i, string A0, string A1) = 2148 "ttt = *("~p~"); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)\n"~ 2149 "{ range = bound; "~i~" = ("~i~" + "~i~"); "~A0~" } else\n"~ 2150 "{ range -= bound; code -= bound; "~i~" = ("~i~" + "~i~") + 1; "~A1~" }"; 2151 enum GET_BIT_CHECK(string p, string i) = GET_BIT2_CHECK!(p, i, "{}", "{}"); 2152 enum TREE_DECODE_CHECK(string probs, string limit, string i) = 2153 "{ "~i~" = 1; do { "~GET_BIT_CHECK!(probs~"+"~i, i)~" } while ("~i~" < "~limit~"); "~i~" -= "~limit~"; }"; 2154 2155 2156 enum kNumPosBitsMax = 4; 2157 enum kNumPosStatesMax = (1 << kNumPosBitsMax); 2158 2159 enum kLenNumLowBits = 3; 2160 enum kLenNumLowSymbols = (1 << kLenNumLowBits); 2161 enum kLenNumMidBits = 3; 2162 enum kLenNumMidSymbols = (1 << kLenNumMidBits); 2163 enum kLenNumHighBits = 8; 2164 enum kLenNumHighSymbols = (1 << kLenNumHighBits); 2165 2166 enum LenChoice = 0; 2167 enum LenChoice2 = (LenChoice + 1); 2168 enum LenLow = (LenChoice2 + 1); 2169 enum LenMid = (LenLow + (kNumPosStatesMax << kLenNumLowBits)); 2170 enum LenHigh = (LenMid + (kNumPosStatesMax << kLenNumMidBits)); 2171 enum kNumLenProbs = (LenHigh + kLenNumHighSymbols); 2172 2173 2174 enum kNumStates = 12; 2175 enum kNumLitStates = 7; 2176 2177 enum kStartPosModelIndex = 4; 2178 enum kEndPosModelIndex = 14; 2179 enum kNumFullDistances = (1 << (kEndPosModelIndex >> 1)); 2180 2181 enum kNumPosSlotBits = 6; 2182 enum kNumLenToPosStates = 4; 2183 2184 enum kNumAlignBits = 4; 2185 enum kAlignTableSize = (1 << kNumAlignBits); 2186 2187 enum kMatchMinLen = 2; 2188 enum kMatchSpecLenStart = (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols); 2189 2190 enum IsMatch = 0; 2191 enum IsRep = (IsMatch + (kNumStates << kNumPosBitsMax)); 2192 enum IsRepG0 = (IsRep + kNumStates); 2193 enum IsRepG1 = (IsRepG0 + kNumStates); 2194 enum IsRepG2 = (IsRepG1 + kNumStates); 2195 enum IsRep0Long = (IsRepG2 + kNumStates); 2196 enum PosSlot = (IsRep0Long + (kNumStates << kNumPosBitsMax)); 2197 enum SpecPos = (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)); 2198 enum Align = (SpecPos + kNumFullDistances - kEndPosModelIndex); 2199 enum LenCoder = (Align + kAlignTableSize); 2200 enum RepLenCoder = (LenCoder + kNumLenProbs); 2201 enum Literal = (RepLenCoder + kNumLenProbs); 2202 2203 enum LZMA_BASE_SIZE = 1846; 2204 enum LZMA_LIT_SIZE = 0x300; 2205 2206 static assert(Literal == LZMA_BASE_SIZE); 2207 2208 //#define LzmaProps_GetNumProbs(p) (Literal + ((UInt32)LZMA_LIT_SIZE << ((p).lc + (p).lp))) 2209 2210 enum LZMA_DIC_MIN = (1 << 12); 2211 2212 /* First LZMA-symbol is always decoded. 2213 And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization 2214 Out: 2215 Result: 2216 SRes.OK - OK 2217 SRes.ERROR_DATA - Error 2218 p->remainLen: 2219 < kMatchSpecLenStart : normal remain 2220 = kMatchSpecLenStart : finished 2221 = kMatchSpecLenStart + 1 : Flush marker (unused now) 2222 = kMatchSpecLenStart + 2 : State Init Marker (unused now) 2223 */ 2224 2225 private SRes LzmaDec_DecodeReal (CLzmaDec* p, SizeT limit, const(Byte)* bufLimit) { 2226 CLzmaProb* probs = p.probs; 2227 2228 uint state = p.state; 2229 UInt32 rep0 = p.reps.ptr[0], rep1 = p.reps.ptr[1], rep2 = p.reps.ptr[2], rep3 = p.reps.ptr[3]; 2230 uint pbMask = (1U<<(p.prop.pb))-1; 2231 uint lpMask = (1U<<(p.prop.lp))-1; 2232 uint lc = p.prop.lc; 2233 2234 Byte* dic = p.dic; 2235 SizeT dicBufSize = p.dicBufSize; 2236 SizeT dicPos = p.dicPos; 2237 2238 UInt32 processedPos = p.processedPos; 2239 UInt32 checkDicSize = p.checkDicSize; 2240 uint len = 0; 2241 2242 const(Byte)* buf = p.buf; 2243 UInt32 range = p.range; 2244 UInt32 code = p.code; 2245 2246 do { 2247 CLzmaProb *prob; 2248 UInt32 bound; 2249 uint ttt; 2250 uint posState = processedPos & pbMask; 2251 2252 prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; 2253 ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound) 2254 { 2255 uint symbol; 2256 range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); 2257 prob = probs + Literal; 2258 if (processedPos != 0 || checkDicSize != 0) 2259 prob += (cast(UInt32)LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + 2260 (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); 2261 processedPos++; 2262 2263 if (state < kNumLitStates) 2264 { 2265 state -= (state < 4) ? state : 3; 2266 symbol = 1; 2267 version(_LZMA_SIZE_OPT) { 2268 do { mixin(NORMAL_LITER_DEC); } while (symbol < 0x100); 2269 } else { 2270 mixin(NORMAL_LITER_DEC); 2271 mixin(NORMAL_LITER_DEC); 2272 mixin(NORMAL_LITER_DEC); 2273 mixin(NORMAL_LITER_DEC); 2274 mixin(NORMAL_LITER_DEC); 2275 mixin(NORMAL_LITER_DEC); 2276 mixin(NORMAL_LITER_DEC); 2277 mixin(NORMAL_LITER_DEC); 2278 } 2279 } 2280 else 2281 { 2282 uint matchByte = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; 2283 uint offs = 0x100; 2284 state -= (state < 10) ? 3 : 6; 2285 symbol = 1; 2286 version(_LZMA_SIZE_OPT) { 2287 do 2288 { 2289 uint bit; 2290 CLzmaProb *probLit; 2291 mixin(MATCHED_LITER_DEC); 2292 } 2293 while (symbol < 0x100); 2294 } else { 2295 { 2296 uint bit; 2297 CLzmaProb *probLit; 2298 mixin(MATCHED_LITER_DEC); 2299 mixin(MATCHED_LITER_DEC); 2300 mixin(MATCHED_LITER_DEC); 2301 mixin(MATCHED_LITER_DEC); 2302 mixin(MATCHED_LITER_DEC); 2303 mixin(MATCHED_LITER_DEC); 2304 mixin(MATCHED_LITER_DEC); 2305 mixin(MATCHED_LITER_DEC); 2306 } 2307 } 2308 } 2309 2310 dic[dicPos++] = cast(Byte)symbol; 2311 continue; 2312 } 2313 2314 { 2315 range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); 2316 prob = probs + IsRep + state; 2317 ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound) 2318 { 2319 range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); 2320 state += kNumStates; 2321 prob = probs + LenCoder; 2322 } 2323 else 2324 { 2325 range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); 2326 if (checkDicSize == 0 && processedPos == 0) 2327 return SRes.ERROR_DATA; 2328 prob = probs + IsRepG0 + state; 2329 ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound) 2330 { 2331 range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); 2332 prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; 2333 ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound) 2334 { 2335 range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); 2336 dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; 2337 dicPos++; 2338 processedPos++; 2339 state = state < kNumLitStates ? 9 : 11; 2340 continue; 2341 } 2342 range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); 2343 } 2344 else 2345 { 2346 UInt32 distance; 2347 range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); 2348 prob = probs + IsRepG1 + state; 2349 ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound) 2350 { 2351 range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); 2352 distance = rep1; 2353 } 2354 else 2355 { 2356 range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); 2357 prob = probs + IsRepG2 + state; 2358 ttt = *(prob); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound) 2359 { 2360 range = bound; *(prob) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); 2361 distance = rep2; 2362 } 2363 else 2364 { 2365 range -= bound; code -= bound; *(prob) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); 2366 distance = rep3; 2367 rep3 = rep2; 2368 } 2369 rep2 = rep1; 2370 } 2371 rep1 = rep0; 2372 rep0 = distance; 2373 } 2374 state = state < kNumLitStates ? 8 : 11; 2375 prob = probs + RepLenCoder; 2376 } 2377 2378 version(_LZMA_SIZE_OPT) { 2379 { 2380 uint lim, offset; 2381 CLzmaProb *probLen = prob + LenChoice; 2382 ttt = *(probLen); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound) 2383 { 2384 range = bound; *(probLen) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); 2385 probLen = prob + LenLow + (posState << kLenNumLowBits); 2386 offset = 0; 2387 lim = (1 << kLenNumLowBits); 2388 } 2389 else 2390 { 2391 range -= bound; code -= bound; *(probLen) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); 2392 probLen = prob + LenChoice2; 2393 ttt = *(probLen); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound) 2394 { 2395 range = bound; *(probLen) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); 2396 probLen = prob + LenMid + (posState << kLenNumMidBits); 2397 offset = kLenNumLowSymbols; 2398 lim = (1 << kLenNumMidBits); 2399 } 2400 else 2401 { 2402 range -= bound; code -= bound; *(probLen) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); 2403 probLen = prob + LenHigh; 2404 offset = kLenNumLowSymbols + kLenNumMidSymbols; 2405 lim = (1 << kLenNumHighBits); 2406 } 2407 } 2408 mixin(TREE_DECODE!("probLen", "lim", "len")); 2409 len += offset; 2410 } 2411 } else { 2412 { 2413 CLzmaProb *probLen = prob + LenChoice; 2414 ttt = *(probLen); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound) 2415 { 2416 range = bound; *(probLen) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); 2417 probLen = prob + LenLow + (posState << kLenNumLowBits); 2418 len = 1; 2419 mixin(TREE_GET_BIT!("probLen", "len")); 2420 mixin(TREE_GET_BIT!("probLen", "len")); 2421 mixin(TREE_GET_BIT!("probLen", "len")); 2422 len -= 8; 2423 } 2424 else 2425 { 2426 range -= bound; code -= bound; *(probLen) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); 2427 probLen = prob + LenChoice2; 2428 ttt = *(probLen); if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } bound = (range>>kNumBitModelTotalBits)*ttt; if (code < bound) 2429 { 2430 range = bound; *(probLen) = cast(CLzmaProb)(ttt+((kBitModelTotal-ttt)>>kNumMoveBits)); 2431 probLen = prob + LenMid + (posState << kLenNumMidBits); 2432 len = 1; 2433 mixin(TREE_GET_BIT!("probLen", "len")); 2434 mixin(TREE_GET_BIT!("probLen", "len")); 2435 mixin(TREE_GET_BIT!("probLen", "len")); 2436 } 2437 else 2438 { 2439 range -= bound; code -= bound; *(probLen) = cast(CLzmaProb)(ttt-(ttt>>kNumMoveBits)); 2440 probLen = prob + LenHigh; 2441 mixin(TREE_DECODE!("probLen", "(1 << kLenNumHighBits)", "len")); 2442 len += kLenNumLowSymbols + kLenNumMidSymbols; 2443 } 2444 } 2445 } 2446 } 2447 2448 if (state >= kNumStates) 2449 { 2450 UInt32 distance; 2451 prob = probs + PosSlot + 2452 ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); 2453 mixin(TREE_6_DECODE!("prob", "distance")); 2454 if (distance >= kStartPosModelIndex) 2455 { 2456 uint posSlot = cast(uint)distance; 2457 uint numDirectBits = cast(uint)(((distance >> 1) - 1)); 2458 distance = (2 | (distance & 1)); 2459 if (posSlot < kEndPosModelIndex) 2460 { 2461 distance <<= numDirectBits; 2462 prob = probs + SpecPos + distance - posSlot - 1; 2463 { 2464 UInt32 mask = 1; 2465 uint i = 1; 2466 do 2467 { 2468 mixin(GET_BIT2!("prob + i", "i", "{}" , "distance |= mask;")); 2469 mask <<= 1; 2470 } 2471 while (--numDirectBits != 0); 2472 } 2473 } 2474 else 2475 { 2476 numDirectBits -= kNumAlignBits; 2477 do 2478 { 2479 if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } 2480 range >>= 1; 2481 2482 { 2483 UInt32 t; 2484 code -= range; 2485 t = (0 - (cast(UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ 2486 distance = (distance << 1) + (t + 1); 2487 code += range & t; 2488 } 2489 /* 2490 distance <<= 1; 2491 if (code >= range) 2492 { 2493 code -= range; 2494 distance |= 1; 2495 } 2496 */ 2497 } 2498 while (--numDirectBits != 0); 2499 prob = probs + Align; 2500 distance <<= kNumAlignBits; 2501 { 2502 uint i = 1; 2503 mixin(GET_BIT2!("prob + i", "i", "", "distance |= 1;")); 2504 mixin(GET_BIT2!("prob + i", "i", "", "distance |= 2;")); 2505 mixin(GET_BIT2!("prob + i", "i", "", "distance |= 4;")); 2506 mixin(GET_BIT2!("prob + i", "i", "", "distance |= 8;")); 2507 } 2508 if (distance == cast(UInt32)0xFFFFFFFF) 2509 { 2510 len += kMatchSpecLenStart; 2511 state -= kNumStates; 2512 break; 2513 } 2514 } 2515 } 2516 2517 rep3 = rep2; 2518 rep2 = rep1; 2519 rep1 = rep0; 2520 rep0 = distance + 1; 2521 if (checkDicSize == 0) 2522 { 2523 if (distance >= processedPos) 2524 { 2525 p.dicPos = dicPos; 2526 return SRes.ERROR_DATA; 2527 } 2528 } 2529 else if (distance >= checkDicSize) 2530 { 2531 p.dicPos = dicPos; 2532 return SRes.ERROR_DATA; 2533 } 2534 state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; 2535 } 2536 2537 len += kMatchMinLen; 2538 2539 { 2540 SizeT rem; 2541 uint curLen; 2542 SizeT pos; 2543 2544 if ((rem = limit - dicPos) == 0) 2545 { 2546 p.dicPos = dicPos; 2547 return SRes.ERROR_DATA; 2548 } 2549 2550 curLen = ((rem < len) ? cast(uint)rem : len); 2551 pos = dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0); 2552 2553 processedPos += curLen; 2554 2555 len -= curLen; 2556 if (curLen <= dicBufSize - pos) 2557 { 2558 Byte *dest = dic + dicPos; 2559 ptrdiff_t src = cast(ptrdiff_t)pos - cast(ptrdiff_t)dicPos; 2560 const(Byte)* lim = dest + curLen; 2561 dicPos += curLen; 2562 do 2563 *(dest) = cast(Byte)*(dest + src); 2564 while (++dest != lim); 2565 } 2566 else 2567 { 2568 do 2569 { 2570 dic[dicPos++] = dic[pos]; 2571 if (++pos == dicBufSize) 2572 pos = 0; 2573 } 2574 while (--curLen != 0); 2575 } 2576 } 2577 } 2578 } 2579 while (dicPos < limit && buf < bufLimit); 2580 2581 if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } 2582 2583 p.buf = buf; 2584 p.range = range; 2585 p.code = code; 2586 p.remainLen = len; 2587 p.dicPos = dicPos; 2588 p.processedPos = processedPos; 2589 p.reps.ptr[0] = rep0; 2590 p.reps.ptr[1] = rep1; 2591 p.reps.ptr[2] = rep2; 2592 p.reps.ptr[3] = rep3; 2593 p.state = state; 2594 2595 return SRes.OK; 2596 } 2597 2598 private void LzmaDec_WriteRem (CLzmaDec* p, SizeT limit) { 2599 if (p.remainLen != 0 && p.remainLen < kMatchSpecLenStart) 2600 { 2601 Byte *dic = p.dic; 2602 SizeT dicPos = p.dicPos; 2603 SizeT dicBufSize = p.dicBufSize; 2604 uint len = p.remainLen; 2605 SizeT rep0 = p.reps.ptr[0]; /* we use SizeT to avoid the BUG of VC14 for AMD64 */ 2606 SizeT rem = limit - dicPos; 2607 if (rem < len) 2608 len = cast(uint)(rem); 2609 2610 if (p.checkDicSize == 0 && p.prop.dicSize - p.processedPos <= len) 2611 p.checkDicSize = p.prop.dicSize; 2612 2613 p.processedPos += len; 2614 p.remainLen -= len; 2615 while (len != 0) 2616 { 2617 len--; 2618 dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; 2619 dicPos++; 2620 } 2621 p.dicPos = dicPos; 2622 } 2623 } 2624 2625 private SRes LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const(Byte)* bufLimit) 2626 { 2627 do 2628 { 2629 SizeT limit2 = limit; 2630 if (p.checkDicSize == 0) 2631 { 2632 UInt32 rem = p.prop.dicSize - p.processedPos; 2633 if (limit - p.dicPos > rem) 2634 limit2 = p.dicPos + rem; 2635 } 2636 2637 if (auto sres = LzmaDec_DecodeReal(p, limit2, bufLimit)) return sres; 2638 2639 if (p.checkDicSize == 0 && p.processedPos >= p.prop.dicSize) 2640 p.checkDicSize = p.prop.dicSize; 2641 2642 LzmaDec_WriteRem(p, limit); 2643 } 2644 while (p.dicPos < limit && p.buf < bufLimit && p.remainLen < kMatchSpecLenStart); 2645 2646 if (p.remainLen > kMatchSpecLenStart) 2647 p.remainLen = kMatchSpecLenStart; 2648 2649 return SRes.OK; 2650 } 2651 2652 alias ELzmaDummy = int; 2653 enum /*ELzmaDummy*/ { 2654 DUMMY_ERROR, /* unexpected end of input stream */ 2655 DUMMY_LIT, 2656 DUMMY_MATCH, 2657 DUMMY_REP 2658 } 2659 2660 private ELzmaDummy LzmaDec_TryDummy(const(CLzmaDec)* p, const(Byte)* buf, SizeT inSize) 2661 { 2662 UInt32 range = p.range; 2663 UInt32 code = p.code; 2664 const(Byte)* bufLimit = buf + inSize; 2665 const(CLzmaProb)* probs = p.probs; 2666 uint state = p.state; 2667 ELzmaDummy res; 2668 2669 { 2670 const(CLzmaProb)* prob; 2671 UInt32 bound; 2672 uint ttt; 2673 uint posState = (p.processedPos) & ((1 << p.prop.pb) - 1); 2674 2675 prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; 2676 ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) 2677 { 2678 range = bound; 2679 2680 /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ 2681 2682 prob = probs + Literal; 2683 if (p.checkDicSize != 0 || p.processedPos != 0) 2684 prob += (cast(UInt32)LZMA_LIT_SIZE * 2685 ((((p.processedPos) & ((1 << (p.prop.lp)) - 1)) << p.prop.lc) + 2686 (p.dic[(p.dicPos == 0 ? p.dicBufSize : p.dicPos) - 1] >> (8 - p.prop.lc)))); 2687 2688 if (state < kNumLitStates) 2689 { 2690 uint symbol = 1; 2691 do { mixin(GET_BIT_CHECK!("prob + symbol", "symbol")); } while (symbol < 0x100); 2692 } 2693 else 2694 { 2695 uint matchByte = p.dic[p.dicPos - p.reps.ptr[0] + 2696 (p.dicPos < p.reps.ptr[0] ? p.dicBufSize : 0)]; 2697 uint offs = 0x100; 2698 uint symbol = 1; 2699 do 2700 { 2701 uint bit; 2702 const(CLzmaProb)* probLit; 2703 matchByte <<= 1; 2704 bit = (matchByte & offs); 2705 probLit = prob + offs + bit + symbol; 2706 mixin(GET_BIT2_CHECK!("probLit", "symbol", "offs &= ~bit;", "offs &= bit;")); 2707 } 2708 while (symbol < 0x100); 2709 } 2710 res = DUMMY_LIT; 2711 } 2712 else 2713 { 2714 uint len; 2715 range -= bound; code -= bound; 2716 2717 prob = probs + IsRep + state; 2718 ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) 2719 { 2720 range = bound; 2721 state = 0; 2722 prob = probs + LenCoder; 2723 res = DUMMY_MATCH; 2724 } 2725 else 2726 { 2727 range -= bound; code -= bound; 2728 res = DUMMY_REP; 2729 prob = probs + IsRepG0 + state; 2730 ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) 2731 { 2732 range = bound; 2733 prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; 2734 ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) 2735 { 2736 range = bound; 2737 if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } 2738 return DUMMY_REP; 2739 } 2740 else 2741 { 2742 range -= bound; code -= bound; 2743 } 2744 } 2745 else 2746 { 2747 range -= bound; code -= bound; 2748 prob = probs + IsRepG1 + state; 2749 ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) 2750 { 2751 range = bound; 2752 } 2753 else 2754 { 2755 range -= bound; code -= bound; 2756 prob = probs + IsRepG2 + state; 2757 ttt = *(prob); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) 2758 { 2759 range = bound; 2760 } 2761 else 2762 { 2763 range -= bound; code -= bound; 2764 } 2765 } 2766 } 2767 state = kNumStates; 2768 prob = probs + RepLenCoder; 2769 } 2770 { 2771 uint limit, offset; 2772 const(CLzmaProb)* probLen = prob + LenChoice; 2773 ttt = *(probLen); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) 2774 { 2775 range = bound; 2776 probLen = prob + LenLow + (posState << kLenNumLowBits); 2777 offset = 0; 2778 limit = 1 << kLenNumLowBits; 2779 } 2780 else 2781 { 2782 range -= bound; code -= bound; 2783 probLen = prob + LenChoice2; 2784 ttt = *(probLen); if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) 2785 { 2786 range = bound; 2787 probLen = prob + LenMid + (posState << kLenNumMidBits); 2788 offset = kLenNumLowSymbols; 2789 limit = 1 << kLenNumMidBits; 2790 } 2791 else 2792 { 2793 range -= bound; code -= bound; 2794 probLen = prob + LenHigh; 2795 offset = kLenNumLowSymbols + kLenNumMidSymbols; 2796 limit = 1 << kLenNumHighBits; 2797 } 2798 } 2799 mixin(TREE_DECODE_CHECK!("probLen", "limit", "len")); 2800 len += offset; 2801 } 2802 2803 if (state < 4) 2804 { 2805 uint posSlot; 2806 prob = probs + PosSlot + 2807 ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << 2808 kNumPosSlotBits); 2809 mixin(TREE_DECODE_CHECK!("prob", "1 << kNumPosSlotBits", "posSlot")); 2810 if (posSlot >= kStartPosModelIndex) 2811 { 2812 uint numDirectBits = ((posSlot >> 1) - 1); 2813 2814 /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ 2815 2816 if (posSlot < kEndPosModelIndex) 2817 { 2818 prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; 2819 } 2820 else 2821 { 2822 numDirectBits -= kNumAlignBits; 2823 do 2824 { 2825 if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } 2826 range >>= 1; 2827 code -= range & (((code - range) >> 31) - 1); 2828 /* if (code >= range) code -= range; */ 2829 } 2830 while (--numDirectBits != 0); 2831 prob = probs + Align; 2832 numDirectBits = kNumAlignBits; 2833 } 2834 { 2835 uint i = 1; 2836 do 2837 { 2838 mixin(GET_BIT_CHECK!("prob + i", "i")); 2839 } 2840 while (--numDirectBits != 0); 2841 } 2842 } 2843 } 2844 } 2845 } 2846 if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } 2847 return res; 2848 } 2849 2850 2851 void LzmaDec_InitDicAndState(CLzmaDec *p, bool initDic, bool initState) 2852 { 2853 p.needFlush = 1; 2854 p.remainLen = 0; 2855 p.tempBufSize = 0; 2856 2857 if (initDic) 2858 { 2859 p.processedPos = 0; 2860 p.checkDicSize = 0; 2861 p.needInitState = 1; 2862 } 2863 if (initState) 2864 p.needInitState = 1; 2865 } 2866 2867 public void LzmaDec_Init(CLzmaDec *p) 2868 { 2869 p.dicPos = 0; 2870 LzmaDec_InitDicAndState(p, true, true); 2871 } 2872 2873 private void LzmaDec_InitStateReal(CLzmaDec *p) 2874 { 2875 SizeT numProbs = (Literal+(cast(UInt32)LZMA_LIT_SIZE<<((&p.prop).lc+(&p.prop).lp))); 2876 SizeT i; 2877 CLzmaProb *probs = p.probs; 2878 for (i = 0; i < numProbs; i++) 2879 probs[i] = kBitModelTotal >> 1; 2880 p.reps.ptr[0] = p.reps.ptr[1] = p.reps.ptr[2] = p.reps.ptr[3] = 1; 2881 p.state = 0; 2882 p.needInitState = 0; 2883 } 2884 2885 public SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const(Byte)* src, SizeT *srcLen, 2886 ELzmaFinishMode finishMode, ELzmaStatus *status) 2887 { 2888 SizeT inSize = *srcLen; 2889 (*srcLen) = 0; 2890 LzmaDec_WriteRem(p, dicLimit); 2891 2892 *status = LZMA_STATUS_NOT_SPECIFIED; 2893 2894 while (p.remainLen != kMatchSpecLenStart) 2895 { 2896 int checkEndMarkNow; 2897 2898 if (p.needFlush) 2899 { 2900 for (; inSize > 0 && p.tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) 2901 p.tempBuf.ptr[p.tempBufSize++] = *src++; 2902 if (p.tempBufSize < RC_INIT_SIZE) 2903 { 2904 *status = LZMA_STATUS_NEEDS_MORE_INPUT; 2905 return SRes.OK; 2906 } 2907 if (p.tempBuf.ptr[0] != 0) 2908 return SRes.ERROR_DATA; 2909 p.code = 2910 (cast(UInt32)p.tempBuf.ptr[1] << 24) 2911 | (cast(UInt32)p.tempBuf.ptr[2] << 16) 2912 | (cast(UInt32)p.tempBuf.ptr[3] << 8) 2913 | (cast(UInt32)p.tempBuf.ptr[4]); 2914 p.range = 0xFFFFFFFF; 2915 p.needFlush = 0; 2916 p.tempBufSize = 0; 2917 } 2918 2919 checkEndMarkNow = 0; 2920 if (p.dicPos >= dicLimit) 2921 { 2922 if (p.remainLen == 0 && p.code == 0) 2923 { 2924 *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; 2925 return SRes.OK; 2926 } 2927 if (finishMode == LZMA_FINISH_ANY) 2928 { 2929 *status = LZMA_STATUS_NOT_FINISHED; 2930 return SRes.OK; 2931 } 2932 if (p.remainLen != 0) 2933 { 2934 *status = LZMA_STATUS_NOT_FINISHED; 2935 return SRes.ERROR_DATA; 2936 } 2937 checkEndMarkNow = 1; 2938 } 2939 2940 if (p.needInitState) 2941 LzmaDec_InitStateReal(p); 2942 2943 if (p.tempBufSize == 0) 2944 { 2945 SizeT processed; 2946 const(Byte)* bufLimit; 2947 if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) 2948 { 2949 int dummyRes = LzmaDec_TryDummy(p, src, inSize); 2950 if (dummyRes == DUMMY_ERROR) 2951 { 2952 import core.stdc.string : memcpy; 2953 memcpy(p.tempBuf.ptr, src, inSize); 2954 p.tempBufSize = cast(uint)inSize; 2955 (*srcLen) += inSize; 2956 *status = LZMA_STATUS_NEEDS_MORE_INPUT; 2957 return SRes.OK; 2958 } 2959 if (checkEndMarkNow && dummyRes != DUMMY_MATCH) 2960 { 2961 *status = LZMA_STATUS_NOT_FINISHED; 2962 return SRes.ERROR_DATA; 2963 } 2964 bufLimit = src; 2965 } 2966 else 2967 bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; 2968 p.buf = src; 2969 if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) 2970 return SRes.ERROR_DATA; 2971 processed = cast(SizeT)(p.buf - src); 2972 (*srcLen) += processed; 2973 src += processed; 2974 inSize -= processed; 2975 } 2976 else 2977 { 2978 uint rem = p.tempBufSize, lookAhead = 0; 2979 while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) 2980 p.tempBuf.ptr[rem++] = src[lookAhead++]; 2981 p.tempBufSize = rem; 2982 if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) 2983 { 2984 int dummyRes = LzmaDec_TryDummy(p, p.tempBuf.ptr, rem); 2985 if (dummyRes == DUMMY_ERROR) 2986 { 2987 (*srcLen) += lookAhead; 2988 *status = LZMA_STATUS_NEEDS_MORE_INPUT; 2989 return SRes.OK; 2990 } 2991 if (checkEndMarkNow && dummyRes != DUMMY_MATCH) 2992 { 2993 *status = LZMA_STATUS_NOT_FINISHED; 2994 return SRes.ERROR_DATA; 2995 } 2996 } 2997 p.buf = p.tempBuf.ptr; 2998 if (LzmaDec_DecodeReal2(p, dicLimit, p.buf) != 0) 2999 return SRes.ERROR_DATA; 3000 3001 { 3002 uint kkk = cast(uint)(p.buf - p.tempBuf.ptr); 3003 if (rem < kkk) 3004 return SRes.ERROR_FAIL; /* some internal error */ 3005 rem -= kkk; 3006 if (lookAhead < rem) 3007 return SRes.ERROR_FAIL; /* some internal error */ 3008 lookAhead -= rem; 3009 } 3010 (*srcLen) += lookAhead; 3011 src += lookAhead; 3012 inSize -= lookAhead; 3013 p.tempBufSize = 0; 3014 } 3015 } 3016 if (p.code == 0) 3017 *status = LZMA_STATUS_FINISHED_WITH_MARK; 3018 return (p.code == 0) ? SRes.OK : SRes.ERROR_DATA; 3019 } 3020 3021 public SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const(Byte)* src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) 3022 { 3023 import core.stdc.string : memcpy; 3024 SizeT outSize = *destLen; 3025 SizeT inSize = *srcLen; 3026 *srcLen = *destLen = 0; 3027 for (;;) 3028 { 3029 SizeT inSizeCur = inSize, outSizeCur, dicPos; 3030 ELzmaFinishMode curFinishMode; 3031 SRes res; 3032 if (p.dicPos == p.dicBufSize) 3033 p.dicPos = 0; 3034 dicPos = p.dicPos; 3035 if (outSize > p.dicBufSize - dicPos) 3036 { 3037 outSizeCur = p.dicBufSize; 3038 curFinishMode = LZMA_FINISH_ANY; 3039 } 3040 else 3041 { 3042 outSizeCur = dicPos + outSize; 3043 curFinishMode = finishMode; 3044 } 3045 3046 res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); 3047 src += inSizeCur; 3048 inSize -= inSizeCur; 3049 *srcLen += inSizeCur; 3050 outSizeCur = p.dicPos - dicPos; 3051 memcpy(dest, p.dic + dicPos, outSizeCur); 3052 dest += outSizeCur; 3053 outSize -= outSizeCur; 3054 *destLen += outSizeCur; 3055 if (res != 0) 3056 return res; 3057 if (outSizeCur == 0 || outSize == 0) 3058 return SRes.OK; 3059 } 3060 } 3061 3062 public void LzmaDec_FreeProbs(CLzmaDec *p) { 3063 import core.stdc.stdlib : free; 3064 if (p.probs !is null) free(p.probs); 3065 p.probs = null; 3066 } 3067 3068 private void LzmaDec_FreeDict(CLzmaDec *p) { 3069 import core.stdc.stdlib : free; 3070 if (p.dic !is null) free(p.dic); 3071 p.dic = null; 3072 } 3073 3074 public void LzmaDec_Free(CLzmaDec *p) { 3075 LzmaDec_FreeProbs(p); 3076 LzmaDec_FreeDict(p); 3077 } 3078 3079 public SRes LzmaProps_Decode(CLzmaProps *p, const(Byte)*data, uint size) 3080 { 3081 UInt32 dicSize; 3082 Byte d; 3083 3084 if (size < LZMA_PROPS_SIZE) 3085 return SRes.ERROR_UNSUPPORTED; 3086 else 3087 dicSize = data[1] | (data[2] << 8) | (data[3] << 16) | (data[4] << 24); 3088 3089 if (dicSize < LZMA_DIC_MIN) 3090 dicSize = LZMA_DIC_MIN; 3091 p.dicSize = dicSize; 3092 3093 d = data[0]; 3094 if (d >= (9 * 5 * 5)) 3095 return SRes.ERROR_UNSUPPORTED; 3096 3097 p.lc = d % 9; 3098 d /= 9; 3099 p.pb = d / 5; 3100 p.lp = d % 5; 3101 3102 return SRes.OK; 3103 } 3104 3105 private SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const(CLzmaProps)* propNew) { 3106 import core.stdc.stdlib : malloc; 3107 UInt32 numProbs = (Literal+(cast(UInt32)LZMA_LIT_SIZE<<((propNew).lc+(propNew).lp))); 3108 if (!p.probs || numProbs != p.numProbs) 3109 { 3110 LzmaDec_FreeProbs(p); 3111 p.probs = cast(CLzmaProb *)malloc(numProbs * CLzmaProb.sizeof); 3112 p.numProbs = numProbs; 3113 if (!p.probs) 3114 return SRes.ERROR_MEM; 3115 } 3116 return SRes.OK; 3117 } 3118 3119 public SRes LzmaDec_AllocateProbs(CLzmaDec *p, const(Byte)* props, uint propsSize) 3120 { 3121 CLzmaProps propNew; 3122 if (auto sres = LzmaProps_Decode(&propNew, props, propsSize)) return sres; 3123 if (auto sres = LzmaDec_AllocateProbs2(p, &propNew)) return sres; 3124 p.prop = propNew; 3125 return SRes.OK; 3126 } 3127 3128 public SRes LzmaDec_Allocate(CLzmaDec *p, const(Byte)*props, uint propsSize) 3129 { 3130 import core.stdc.stdlib : malloc; 3131 CLzmaProps propNew; 3132 SizeT dicBufSize; 3133 if (auto sres = LzmaProps_Decode(&propNew, props, propsSize)) return sres; 3134 if (auto sres = LzmaDec_AllocateProbs2(p, &propNew)) return sres; 3135 3136 { 3137 UInt32 dictSize = propNew.dicSize; 3138 SizeT mask = (1U << 12) - 1; 3139 if (dictSize >= (1U << 30)) mask = (1U << 22) - 1; 3140 else if (dictSize >= (1U << 22)) mask = (1U << 20) - 1; 3141 dicBufSize = (cast(SizeT)dictSize + mask) & ~mask; 3142 if (dicBufSize < dictSize) 3143 dicBufSize = dictSize; 3144 } 3145 3146 if (!p.dic || dicBufSize != p.dicBufSize) 3147 { 3148 LzmaDec_FreeDict(p); 3149 p.dic = cast(Byte *)malloc(dicBufSize); 3150 if (!p.dic) 3151 { 3152 LzmaDec_FreeProbs(p); 3153 return SRes.ERROR_MEM; 3154 } 3155 } 3156 p.dicBufSize = dicBufSize; 3157 p.prop = propNew; 3158 return SRes.OK; 3159 } 3160 3161 public SRes LzmaDecode(Byte *dest, SizeT *destLen, const(Byte)* src, SizeT *srcLen, 3162 const(Byte)* propData, uint propSize, ELzmaFinishMode finishMode, 3163 ELzmaStatus *status) 3164 { 3165 CLzmaDec p; 3166 SRes res; 3167 SizeT outSize = *destLen, inSize = *srcLen; 3168 *destLen = *srcLen = 0; 3169 *status = LZMA_STATUS_NOT_SPECIFIED; 3170 if (inSize < RC_INIT_SIZE) 3171 return SRes.ERROR_INPUT_EOF; 3172 //LzmaDec_Construct(&p); 3173 p.dic = null; p.probs = null; 3174 if (auto sres = LzmaDec_AllocateProbs(&p, propData, propSize)) return sres; 3175 p.dic = dest; 3176 p.dicBufSize = outSize; 3177 LzmaDec_Init(&p); 3178 *srcLen = inSize; 3179 res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); 3180 *destLen = p.dicPos; 3181 if (res == SRes.OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) 3182 res = SRes.ERROR_INPUT_EOF; 3183 LzmaDec_FreeProbs(&p); 3184 return res; 3185 } 3186 3187 3188 3189 /* Lzma2Dec.c -- LZMA2 Decoder 3190 2009-05-03 : Igor Pavlov : Public domain */ 3191 // also by Lasse Collin 3192 // ported to D by adr. 3193 3194 /* 3195 00000000 - EOS 3196 00000001 U U - Uncompressed Reset Dic 3197 00000010 U U - Uncompressed No Reset 3198 100uuuuu U U P P - LZMA no reset 3199 101uuuuu U U P P - LZMA reset state 3200 110uuuuu U U P P S - LZMA reset state + new prop 3201 111uuuuu U U P P S - LZMA reset state + new prop + reset dic 3202 3203 u, U - Unpack Size 3204 P - Pack Size 3205 S - Props 3206 */ 3207 3208 struct CLzma2Dec 3209 { 3210 CLzmaDec decoder; 3211 UInt32 packSize; 3212 UInt32 unpackSize; 3213 int state; 3214 Byte control; 3215 bool needInitDic; 3216 bool needInitState; 3217 bool needInitProp; 3218 } 3219 3220 enum LZMA2_CONTROL_LZMA = (1 << 7); 3221 enum LZMA2_CONTROL_COPY_NO_RESET = 2; 3222 enum LZMA2_CONTROL_COPY_RESET_DIC = 1; 3223 enum LZMA2_CONTROL_EOF = 0; 3224 3225 auto LZMA2_IS_UNCOMPRESSED_STATE(P)(P p) { return (((p).control & LZMA2_CONTROL_LZMA) == 0); } 3226 3227 auto LZMA2_GET_LZMA_MODE(P)(P p) { return (((p).control >> 5) & 3); } 3228 auto LZMA2_IS_THERE_PROP(P)(P mode) { return ((mode) >= 2); } 3229 3230 enum LZMA2_LCLP_MAX = 4; 3231 auto LZMA2_DIC_SIZE_FROM_PROP(P)(P p) { return ((cast(UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)); } 3232 3233 enum ELzma2State 3234 { 3235 LZMA2_STATE_CONTROL, 3236 LZMA2_STATE_UNPACK0, 3237 LZMA2_STATE_UNPACK1, 3238 LZMA2_STATE_PACK0, 3239 LZMA2_STATE_PACK1, 3240 LZMA2_STATE_PROP, 3241 LZMA2_STATE_DATA, 3242 LZMA2_STATE_DATA_CONT, 3243 LZMA2_STATE_FINISHED, 3244 LZMA2_STATE_ERROR 3245 } 3246 3247 static SRes Lzma2Dec_GetOldProps(Byte prop, Byte *props) 3248 { 3249 UInt32 dicSize; 3250 if (prop > 40) 3251 return SRes.ERROR_UNSUPPORTED; 3252 dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop); 3253 props[0] = cast(Byte)LZMA2_LCLP_MAX; 3254 props[1] = cast(Byte)(dicSize); 3255 props[2] = cast(Byte)(dicSize >> 8); 3256 props[3] = cast(Byte)(dicSize >> 16); 3257 props[4] = cast(Byte)(dicSize >> 24); 3258 return SRes.OK; 3259 } 3260 3261 SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop) 3262 { 3263 Byte[LZMA_PROPS_SIZE] props; 3264 auto wtf = Lzma2Dec_GetOldProps(prop, props.ptr); 3265 if(wtf != 0) return wtf; 3266 return LzmaDec_AllocateProbs(&p.decoder, props.ptr, LZMA_PROPS_SIZE); 3267 } 3268 3269 SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop) 3270 { 3271 Byte[LZMA_PROPS_SIZE] props; 3272 auto wtf = Lzma2Dec_GetOldProps(prop, props.ptr); 3273 if(wtf != 0) return wtf; 3274 return LzmaDec_Allocate(&p.decoder, props.ptr, LZMA_PROPS_SIZE); 3275 } 3276 3277 void Lzma2Dec_Init(CLzma2Dec *p) 3278 { 3279 p.state = ELzma2State.LZMA2_STATE_CONTROL; 3280 p.needInitDic = true; 3281 p.needInitState = true; 3282 p.needInitProp = true; 3283 LzmaDec_Init(&p.decoder); 3284 } 3285 3286 static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) 3287 { 3288 switch(p.state) 3289 { 3290 default: return ELzma2State.LZMA2_STATE_ERROR; 3291 case ELzma2State.LZMA2_STATE_CONTROL: 3292 p.control = b; 3293 if (p.control == 0) 3294 return ELzma2State.LZMA2_STATE_FINISHED; 3295 if (LZMA2_IS_UNCOMPRESSED_STATE(p)) 3296 { 3297 if ((p.control & 0x7F) > 2) 3298 return ELzma2State.LZMA2_STATE_ERROR; 3299 p.unpackSize = 0; 3300 } 3301 else 3302 p.unpackSize = cast(UInt32)(p.control & 0x1F) << 16; 3303 return ELzma2State.LZMA2_STATE_UNPACK0; 3304 3305 case ELzma2State.LZMA2_STATE_UNPACK0: 3306 p.unpackSize |= cast(UInt32)b << 8; 3307 return ELzma2State.LZMA2_STATE_UNPACK1; 3308 3309 case ELzma2State.LZMA2_STATE_UNPACK1: 3310 p.unpackSize |= cast(UInt32)b; 3311 p.unpackSize++; 3312 return (LZMA2_IS_UNCOMPRESSED_STATE(p)) ? ELzma2State.LZMA2_STATE_DATA : ELzma2State.LZMA2_STATE_PACK0; 3313 3314 case ELzma2State.LZMA2_STATE_PACK0: 3315 p.packSize = cast(UInt32)b << 8; 3316 return ELzma2State.LZMA2_STATE_PACK1; 3317 3318 case ELzma2State.LZMA2_STATE_PACK1: 3319 p.packSize |= cast(UInt32)b; 3320 p.packSize++; 3321 return LZMA2_IS_THERE_PROP(LZMA2_GET_LZMA_MODE(p)) ? ELzma2State.LZMA2_STATE_PROP: 3322 (p.needInitProp ? ELzma2State.LZMA2_STATE_ERROR : ELzma2State.LZMA2_STATE_DATA); 3323 3324 case ELzma2State.LZMA2_STATE_PROP: 3325 { 3326 int lc, lp; 3327 if (b >= (9 * 5 * 5)) 3328 return ELzma2State.LZMA2_STATE_ERROR; 3329 lc = b % 9; 3330 b /= 9; 3331 p.decoder.prop.pb = b / 5; 3332 lp = b % 5; 3333 if (lc + lp > LZMA2_LCLP_MAX) 3334 return ELzma2State.LZMA2_STATE_ERROR; 3335 p.decoder.prop.lc = lc; 3336 p.decoder.prop.lp = lp; 3337 p.needInitProp = false; 3338 return ELzma2State.LZMA2_STATE_DATA; 3339 } 3340 } 3341 } 3342 3343 static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const(Byte) *src, SizeT size) 3344 { 3345 import core.stdc.string; 3346 memcpy(p.dic + p.dicPos, src, size); 3347 p.dicPos += size; 3348 if (p.checkDicSize == 0 && p.prop.dicSize - p.processedPos <= size) 3349 p.checkDicSize = p.prop.dicSize; 3350 p.processedPos += cast(UInt32)size; 3351 } 3352 3353 SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, 3354 const(Byte) *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) 3355 { 3356 SizeT inSize = *srcLen; 3357 *srcLen = 0; 3358 *status = LZMA_STATUS_NOT_SPECIFIED; 3359 3360 while (p.state != ELzma2State.LZMA2_STATE_FINISHED) 3361 { 3362 SizeT dicPos = p.decoder.dicPos; 3363 if (p.state == ELzma2State.LZMA2_STATE_ERROR) 3364 return SRes.ERROR_DATA; 3365 if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY) 3366 { 3367 *status = LZMA_STATUS_NOT_FINISHED; 3368 return SRes.OK; 3369 } 3370 if (p.state != ELzma2State.LZMA2_STATE_DATA && p.state != ELzma2State.LZMA2_STATE_DATA_CONT) 3371 { 3372 if (*srcLen == inSize) 3373 { 3374 *status = LZMA_STATUS_NEEDS_MORE_INPUT; 3375 return SRes.OK; 3376 } 3377 (*srcLen)++; 3378 p.state = Lzma2Dec_UpdateState(p, *src++); 3379 continue; 3380 } 3381 { 3382 SizeT destSizeCur = dicLimit - dicPos; 3383 SizeT srcSizeCur = inSize - *srcLen; 3384 ELzmaFinishMode curFinishMode = LZMA_FINISH_ANY; 3385 3386 if (p.unpackSize <= destSizeCur) 3387 { 3388 destSizeCur = cast(SizeT)p.unpackSize; 3389 curFinishMode = LZMA_FINISH_END; 3390 } 3391 3392 if (LZMA2_IS_UNCOMPRESSED_STATE(p)) 3393 { 3394 if (*srcLen == inSize) 3395 { 3396 *status = LZMA_STATUS_NEEDS_MORE_INPUT; 3397 return SRes.OK; 3398 } 3399 3400 if (p.state == ELzma2State.LZMA2_STATE_DATA) 3401 { 3402 bool initDic = (p.control == LZMA2_CONTROL_COPY_RESET_DIC); 3403 if (initDic) 3404 p.needInitProp = p.needInitState = true; 3405 else if (p.needInitDic) 3406 return SRes.ERROR_DATA; 3407 p.needInitDic = false; 3408 LzmaDec_InitDicAndState(&p.decoder, initDic, false); 3409 } 3410 3411 if (srcSizeCur > destSizeCur) 3412 srcSizeCur = destSizeCur; 3413 3414 if (srcSizeCur == 0) 3415 return SRes.ERROR_DATA; 3416 3417 LzmaDec_UpdateWithUncompressed(&p.decoder, src, srcSizeCur); 3418 3419 src += srcSizeCur; 3420 *srcLen += srcSizeCur; 3421 p.unpackSize -= cast(UInt32)srcSizeCur; 3422 p.state = (p.unpackSize == 0) ? ELzma2State.LZMA2_STATE_CONTROL : ELzma2State.LZMA2_STATE_DATA_CONT; 3423 } 3424 else 3425 { 3426 SizeT outSizeProcessed; 3427 SRes res; 3428 3429 if (p.state == ELzma2State.LZMA2_STATE_DATA) 3430 { 3431 int mode = LZMA2_GET_LZMA_MODE(p); 3432 bool initDic = (mode == 3); 3433 bool initState = (mode > 0); 3434 if ((!initDic && p.needInitDic) || (!initState && p.needInitState)) 3435 return SRes.ERROR_DATA; 3436 3437 LzmaDec_InitDicAndState(&p.decoder, initDic, initState); 3438 p.needInitDic = false; 3439 p.needInitState = false; 3440 p.state = ELzma2State.LZMA2_STATE_DATA_CONT; 3441 } 3442 if (srcSizeCur > p.packSize) 3443 srcSizeCur = cast(SizeT)p.packSize; 3444 3445 res = LzmaDec_DecodeToDic(&p.decoder, dicPos + destSizeCur, src, &srcSizeCur, curFinishMode, status); 3446 3447 src += srcSizeCur; 3448 *srcLen += srcSizeCur; 3449 p.packSize -= cast(UInt32)srcSizeCur; 3450 3451 outSizeProcessed = p.decoder.dicPos - dicPos; 3452 p.unpackSize -= cast(UInt32)outSizeProcessed; 3453 3454 if(res != 0) return res; 3455 if (*status == LZMA_STATUS_NEEDS_MORE_INPUT) 3456 return res; 3457 3458 if (srcSizeCur == 0 && outSizeProcessed == 0) 3459 { 3460 if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK || 3461 p.unpackSize != 0 || p.packSize != 0) 3462 return SRes.ERROR_DATA; 3463 p.state = ELzma2State.LZMA2_STATE_CONTROL; 3464 } 3465 if (*status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) 3466 *status = LZMA_STATUS_NOT_FINISHED; 3467 } 3468 } 3469 } 3470 *status = LZMA_STATUS_FINISHED_WITH_MARK; 3471 return SRes.OK; 3472 } 3473 3474 SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const(Byte) *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) 3475 { 3476 import core.stdc.string; 3477 SizeT outSize = *destLen, inSize = *srcLen; 3478 *srcLen = *destLen = 0; 3479 for (;;) 3480 { 3481 SizeT srcSizeCur = inSize, outSizeCur, dicPos; 3482 ELzmaFinishMode curFinishMode; 3483 SRes res; 3484 if (p.decoder.dicPos == p.decoder.dicBufSize) 3485 p.decoder.dicPos = 0; 3486 dicPos = p.decoder.dicPos; 3487 if (outSize > p.decoder.dicBufSize - dicPos) 3488 { 3489 outSizeCur = p.decoder.dicBufSize; 3490 curFinishMode = LZMA_FINISH_ANY; 3491 } 3492 else 3493 { 3494 outSizeCur = dicPos + outSize; 3495 curFinishMode = finishMode; 3496 } 3497 3498 res = Lzma2Dec_DecodeToDic(p, outSizeCur, src, &srcSizeCur, curFinishMode, status); 3499 src += srcSizeCur; 3500 inSize -= srcSizeCur; 3501 *srcLen += srcSizeCur; 3502 outSizeCur = p.decoder.dicPos - dicPos; 3503 memcpy(dest, p.decoder.dic + dicPos, outSizeCur); 3504 dest += outSizeCur; 3505 outSize -= outSizeCur; 3506 *destLen += outSizeCur; 3507 if (res != 0) 3508 return res; 3509 if (outSizeCur == 0 || outSize == 0) 3510 return SRes.OK; 3511 } 3512 } 3513 3514 SRes Lzma2Decode(Byte *dest, SizeT *destLen, const(Byte) *src, SizeT *srcLen, 3515 Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status) 3516 { 3517 CLzma2Dec decoder; 3518 SRes res; 3519 SizeT outSize = *destLen, inSize = *srcLen; 3520 Byte[LZMA_PROPS_SIZE] props; 3521 3522 //Lzma2Dec_Construct(&decoder); 3523 3524 *destLen = *srcLen = 0; 3525 *status = LZMA_STATUS_NOT_SPECIFIED; 3526 decoder.decoder.dic = dest; 3527 decoder.decoder.dicBufSize = outSize; 3528 3529 auto wtf = Lzma2Dec_GetOldProps(prop, props.ptr); 3530 if(wtf != 0) return wtf; 3531 wtf = LzmaDec_AllocateProbs(&decoder.decoder, props.ptr, LZMA_PROPS_SIZE); 3532 if(wtf != 0) return wtf; 3533 3534 *srcLen = inSize; 3535 res = Lzma2Dec_DecodeToDic(&decoder, outSize, src, srcLen, finishMode, status); 3536 *destLen = decoder.decoder.dicPos; 3537 if (res == SRes.OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) 3538 res = SRes.ERROR_INPUT_EOF; 3539 3540 LzmaDec_FreeProbs(&decoder.decoder); 3541 return res; 3542 } 3543 3544 }