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