1 /* converted by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://www.wtfpl.net/txt/copying/ for more details.
12 import std
.digest
.ripemd
;
22 // ////////////////////////////////////////////////////////////////////////// //
23 void compressFile (ref VFile fi
, ref VFile fo
) {
25 ulong insize
= fi
.size
;
26 //fi.rawReadExact(inbuf[0..insize]);
30 LzmaEncProps_Init(&props
);
33 //while (props.dictSize < insize) props.dictSize <<= 1;
34 props
.dictSize
= 1<<27; //128MB
35 //props.dictSize = 1<<22; //4MB
36 props
.reduceSize
= insize
;
38 ubyte[LZMA_PROPS_SIZE
+8] header
;
39 uint headerSize
= cast(uint)header
.sizeof
;
41 CLzmaEncHandle enc
= LzmaEnc_Create(&lzmaDefAllocator
);
42 scope(exit
) LzmaEnc_Destroy(enc
, &lzmaDefAllocator
, &lzmaDefAllocator
);
44 if (LzmaEnc_SetProps(enc
, &props
) != SZ_OK
) throw new Exception("cannot set encoder properties");
45 LzmaEnc_SetDataSize(enc
, insize
); // just in case
47 if (LzmaEnc_WriteProperties(enc
, header
.ptr
, &headerSize
) != SZ_OK
) throw new Exception("cannot encode encoder properties");
48 assert(headerSize
> 0 && headerSize
< 256);
50 writeln("compressing...");
51 fo
.writeNum
!ushort(cast(ushort)1); // version and endianness check
52 fo
.writeNum
!ulong(insize
); // unpacked file size
53 fo
.writeNum
!ubyte(cast(ubyte)headerSize
); // properties size
54 fo
.rawWriteExact(header
[0..headerSize
]);
56 ISeqInStream inStream
;
57 ISeqOutStream outStream
;
58 ICompressProgress progress
;
60 immutable origInSize
= insize
;
63 auto csum
= makeDigest
!RIPEMD160
;
65 /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
66 (output(*size) < input(*size)) is allowed */
67 inStream
.Read
= delegate SRes (ISeqInStream
* p
, void* buf
, usize
* size
) nothrow {
68 if (*size
> insize
) *size
= cast(usize
)insize
;
71 fi
.rawReadExact(buf
[0..*size
]);
72 } catch (Exception e
) {
75 csum
.put((cast(const(ubyte)*)buf
)[0..*size
]);
81 /* Returns: result - the number of actually written bytes.
82 (result < size) means error */
83 outStream
.Write
= delegate usize (ISeqOutStream
* p
, const(void)* buf
, usize size
) nothrow {
85 fo
.rawWriteExact(buf
[0..size
]);
87 } catch (Exception e
) {
93 uint prevPrc
= uint.max
;
94 ulong prevReport
= clockMilli();
96 progress
.Progress
= delegate SRes (ICompressProgress
* p
, ulong inSize
, ulong outSize
) nothrow {
97 if (origInSize
== 0) return SZ_OK
; // just in case
98 immutable uint prc
= cast(uint)(inSize
*100U/origInSize
);
100 ulong rtt
= clockMilli();
101 if (rtt
-prevReport
< 500) return SZ_OK
;
106 auto num0
= intWithCommas(i0
[], inSize
);
107 auto num1
= intWithCommas(i1
[], origInSize
);
109 write(" [", num0
[], "/", num1
[], "] ", prc
, "%\x1b[K\r");
111 } catch (Exception
) {}
112 prevReport
= clockMilli();
116 progress
.Progress(&progress
, 0, 0);
118 SRes res
= LzmaEnc_Encode(enc
, &outStream
, &inStream
, &progress
, &lzmaDefAllocator
, &lzmaDefAllocator
);
122 case SZ_ERROR_MEM
: throw new Exception("FUCK: memory");
123 case SZ_ERROR_PARAM
: throw new Exception("FUCK: param");
124 case SZ_ERROR_OUTPUT_EOF
: throw new Exception("FUCK: compressed is bigger");
125 default: throw new Exception("FUCK: something else");
128 ubyte[20] hash
= csum
.finish()[];
129 fo
.rawWriteExact(hash
[]);
131 writeln("\rcompressed ", intWithCommas(origInSize
), " to ", intWithCommas(destSize
), "; ratio: ", destSize
*100U/(origInSize ? origInSize
: 1), "%\x1b[K");
135 // ////////////////////////////////////////////////////////////////////////// //
136 void decompressFile (ref VFile fi
, ref VFile fo
) {
137 ubyte[LZMA_PROPS_SIZE
+8] header
;
140 ulong pksize
= fi
.size
;
141 if (fi
.readNum
!ushort != 1) throw new Exception("invalid archive version");
142 ulong unsize
= fi
.readNum
!ulong; // unpacked size
143 ubyte hdrSize
= fi
.readNum
!ubyte;
144 if (hdrSize
== 0 || hdrSize
> header
.sizeof
) throw new Exception("invalid properties size");
145 fi
.rawReadExact(header
[0..hdrSize
]);
148 if (pksize
< 20) throw new Exception("invalid archive size");
150 if (unsize
!= 0) throw new Exception("invalid archive size");
151 return; // nothing to do
153 pksize
-= 20; // digest size
155 auto csum
= makeDigest
!RIPEMD160
;
157 enum InBufSize
= 1024*1024;
158 enum OutBufSize
= 1024*1024;
160 ubyte *inbuf
= cast(ubyte*)ISzAlloc_Alloc(&lzmaDefAllocator
, InBufSize
);
161 ubyte *outbuf
= cast(ubyte*)ISzAlloc_Alloc(&lzmaDefAllocator
, OutBufSize
);
163 ISzAlloc_Free(&lzmaDefAllocator
, inbuf
);
164 ISzAlloc_Free(&lzmaDefAllocator
, outbuf
);
170 SRes res
= LzmaDec_Allocate(&dec, header
.ptr
, hdrSize
, &lzmaDefAllocator
);
171 if (res
!= SZ_OK
) throw new Exception("cannot initialize decoder");
172 scope(exit
) LzmaDec_Free(&dec, &lzmaDefAllocator
);
174 ulong unpackedTotal
= 0;
175 ulong readleft
= pksize
;
178 uint prevPrc
= uint.max
;
179 ulong prevReport
= clockMilli();
180 immutable ulong sttime
= prevReport
;
182 void showProgress () nothrow {
183 immutable rds
= unpackedTotal
; //pksize-readleft;
184 immutable uint prc
= cast(uint)(rds
*100U/unsize
/*pksize*/);
185 if (prc
== prevPrc
&& unpackedTotal
!= unsize
) {
186 ulong rtt
= clockMilli();
187 if (rtt
-prevReport
< 500) return;
192 auto num0
= intWithCommas(i0
[], rds
);
193 auto num1
= intWithCommas(i1
[], unsize
/*pksize*/);
195 write(" [", num0
[], "/", num1
[], "] ", prc
, "%\x1b[K\r");
197 } catch (Exception
) {}
198 prevReport
= clockMilli();
201 while (readleft || inused
) {
203 if (readleft
&& inused
< InBufSize
) {
204 uint rd
= InBufSize
-cast(uint)inused
;
205 if (rd
> readleft
) rd
= cast(usize
)readleft
;
206 fi
.rawReadExact(inbuf
[inused
..inused
+rd
]);
211 usize outSize
= OutBufSize
;
212 usize inSize
= inused
;
214 // as we don't have a proper EOF mark, make sure to not unpack extra data
215 if (unsize
-unpackedTotal
< outSize
) outSize
= cast(usize
)(unsize
-unpackedTotal
);
216 //writeln("\nunsize=", unsize, "; unpackedTotal=", unpackedTotal, "; outSize=", outSize);
217 res
= LzmaDec_DecodeToBuf(&dec, outbuf
, &outSize
, inbuf
, &inSize
, LZMA_FINISH_ANY
, &status
);
220 writeln("ERROR: readleft=", readleft
, "; inused=", inused
, "; written=", unpackedTotal
, " of ", unsize
);
222 case SZ_ERROR_DATA
: throw new Exception("corrupted data");
223 case SZ_ERROR_MEM
: throw new Exception("out of memory");
224 case SZ_ERROR_UNSUPPORTED
: throw new Exception("unsupported properties");
225 case SZ_ERROR_INPUT_EOF
: throw new Exception("need bigger input buffer, but we don't have any");
226 default: throw new Exception("some other error");
230 fo
.rawWriteExact(outbuf
[0..outSize
]);
231 unpackedTotal
+= outSize
;
232 csum
.put((cast(const(ubyte)*)outbuf
)[0..outSize
]);
234 if (unpackedTotal
== unsize
) break; // we're done (we don't have EOF mark, so...)
236 if (inSize
< inused
) {
237 import core
.stdc
.string
: memmove
;
238 memmove(inbuf
, inbuf
+inSize
, inused
-inSize
);
242 case LZMA_STATUS_FINISHED_WITH_MARK
: throw new Exception("found EOF mark, but there should not be one");
243 case LZMA_STATUS_NOT_FINISHED
: break; // it is ok
244 case LZMA_STATUS_NEEDS_MORE_INPUT
: break; // it is ok
245 case LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
: break; // ok ;-)
246 default: break; // ignore others
250 if (unpackedTotal
!= unsize
) {
251 //import std.conv : to;
252 throw new Exception("invalid unpacked file size; expected "~intWithCommas(unsize
).idup
~" but got "~intWithCommas(unpackedTotal
).idup
);
257 fi
.rawReadExact(origcsum
[]);
258 ubyte[20] hash
= csum
.finish()[];
259 if (origcsum
[] != hash
[]) throw new Exception("invalid unpacked file hash");
261 ulong etime
= clockMilli()-sttime
;
262 if (!etime
) etime
= 1;
263 writeln("\rsuccesfully unpacked ", intWithCommas(unpackedTotal
), " bytes (", intWithCommas(unpackedTotal
*1000U/etime
), " MB/sec).\x1b[K");
267 // ////////////////////////////////////////////////////////////////////////// //
268 void main (string
[] args
) {
269 if (args
.length
< 2) {
270 writeln("usage: fuckme <c|x> infile outfile");
274 if (args
[1] == "c") {
275 if (args
.length
< 4) throw new Exception("out of args");
276 if (args
[2] == args
[3]) throw new Exception("cannot compress in-place");
277 auto fi
= VFile(args
[2]);
278 auto fo
= VFile(args
[3], "w");
279 compressFile(fi
, fo
);
280 } else if (args
[1] == "x") {
281 if (args
.length
< 4) throw new Exception("out of args");
282 if (args
[2] == args
[3]) throw new Exception("cannot decompress in-place");
283 auto fi
= VFile(args
[2]);
284 auto fo
= VFile(args
[3], "w");
285 decompressFile(fi
, fo
);
286 } else if (args
[1] == "t") {
287 if (args
.length
< 3) throw new Exception("out of args");
288 auto fi
= VFile(args
[2]);
289 auto fo
= VFile("/dev/null", "w");
290 decompressFile(fi
, fo
);