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