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