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