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