1 module zpack
/*is aliced*/;
10 // ////////////////////////////////////////////////////////////////////////// //
11 string
n2c (ulong n
) {
13 string t
= to
!string(n
);
14 if (t
.length
< 4) return t
;
16 while (t
.length
> 3) {
17 res
= ","~t
[$-3..$]~res
;
20 if (t
.length
) res
= t
~res
; else res
= res
[1..$];
25 // ////////////////////////////////////////////////////////////////////////// //
26 void doMain (string
[] args
) {
27 import std
.file
, std
.path
, std
.stdio
: File
;
30 long parseSize (const(char)[] s
) {
31 if (s
.length
== 0) return -1;
32 if (s
[0] < '0' || s
[0] > '9') return -1;
34 while (s
.length
&& s
[0] >= '0' && s
[0] <= '9') {
35 res
= res
*10+s
[0]-'0';
36 if (res
> 0x3fff_ffff
) return -1;
39 if (s
.length
== 0) return res
;
42 case 'K': case 'k': return res
*1024;
43 case 'M': case 'm': return res
*1024*1024;
44 case 'G': case 'g': return res
*1024*1024*1024;
47 } else if (s
.length
== 2) {
49 foreach (immutable idx
, char ch
; s
) {
50 if (ch
>= 'a' && ch
<= 'z') ch
-= 32;
54 case "KB": return res
*1024;
55 case "MB": return res
*1024*1024;
56 case "GB": return res
*1024*1024*1024;
63 uint blockSize
= 256*1024;
66 import std
.stdio
: write
;
67 static if (arcz_has_balz
) enum sbalz
= " --balz use Balz compressor\n"; else sbalz
= "";
68 static if (arcz_has_balz
) enum sbalz1
= " --balzx use Balz compressor in \"maximum\" mode\n"; else sbalz1
= "";
69 static if (arcz_has_zopfli
) enum szop
= " --zopfli use Zopfli compressor\n"; else enum szop
= "";
70 static if (arcz_has_dlzma
) enum lzmaop
= " --lzma use LZMA compressor\n"; else enum lzmaop
= "";
72 "zpack [options] -o outfile sourcedir\n"~
74 " -b block size [1..32MB] (default is 256KB)\n"~
75 " --zlib use ZLib compressor (default)\n"~
83 string outfname
= null;
85 ArzCreator
.Compressor cpr
= ArzCreator
.Compressor
.ZLib
;
90 if (args
.length
< 2) { usage(); return; }
94 while (f
< args
.length
) {
95 string arg
= args
[f
++];
96 if (arg
.length
== 0) continue;
98 if (arg
== "--") { nomore
= true; continue; }
99 if (arg
== "-") throw new Exception("stdin is not supported");
101 if (arg
== "--balz") { cpr
= ArzCreator
.Compressor
.Balz
; continue; }
102 if (arg
== "--balzx") { cpr
= ArzCreator
.Compressor
.BalzMax
; continue; }
103 if (arg
== "--zopfli") { cpr
= ArzCreator
.Compressor
.Zopfli
; continue; }
104 if (arg
== "--zlib") { cpr
= ArzCreator
.Compressor
.ZLib
; continue; }
105 if (arg
== "--lzma") { cpr
= ArzCreator
.Compressor
.Lzma
; continue; }
106 if (arg
[1] == '-') throw new Exception("long options aren't supported");
112 if (arg
.length
== 0) {
113 if (f
>= args
.length
) throw new Exception("block size?");
116 auto sz
= parseSize(arg
);
117 if (sz
< 1 || sz
>= 32*1024*1024) throw new Exception("invalid block size: '"~arg
~"'");
118 blockSize
= cast(uint)sz
;
121 if (ch
== 'h') { usage(); return; }
123 if (outfname
!is null) throw new Exception("duplicate output name");
124 if (arg
.length
== 0) {
125 if (f
>= args
.length
) throw new Exception("block size?");
128 if (arg
.length
== 0) throw new Exception("empty output name");
129 outfname
= arg
.defaultExtension(".arz");
132 throw new Exception("invalid option: '"~ch
~"' ("~arg
~")");
137 if (srcdir
!is null) throw new Exception("duplicate source directory");
138 if (arg
== "-") throw new Exception("stdin is not supported");
142 if (srcdir
is null) throw new Exception("source directory?");
143 if (outfname
is null) throw new Exception("output file name?");
145 writeln("creating archive '", outfname
, "' with block size ", blockSize
.n2c
);
147 static struct FInfo
{
154 writeln("building file list...");
156 foreach (DirEntry e
; dirEntries(srcdir
, SpanMode
.breadth
)) {
158 if (e
.size
> 0x3fff_ffff
) throw new Exception("file '"~e
.name
~"' is too big");
160 string fname
= e
.name
[srcdir
.length
..$];
162 import std
.string
: replace
;
163 while (fname
.length
> 0 && fname
[0] == '\\') fname
= fname
[1..$];
164 fname
= fname
.replace("\\", "/");
166 while (fname
.length
> 0 && fname
[0] == '/') fname
= fname
[1..$];
168 if (fname
.length
== 0) throw new Exception("invalid file name (wtf?!)");
169 filelist
~= FInfo(e
.name
, fname
, cast(uint)e
.size
);
173 if (filelist
.length
== 0) throw new Exception("no files found");
174 writeln(filelist
.length
, " files found, total ", total
.n2c
, " bytes");
176 // sort files by size and extension
177 import std
.algorithm
: sort
;
179 filelist.sort!((ref a, ref b) {
180 if (a.size < b.size) return true;
181 if (a.size > b.size) return false;
182 // same size, try extension
183 return (a.name.extension < b.name.extension);
186 filelist
.sort
!((ref a
, ref b
) {
187 // same size, try extension
188 return (a
.name
< b
.name
);
191 auto arcz
= new ArzCreator(outfname
, blockSize
, cpr
);
192 auto stt
= MonoTime
.currTime
;
193 foreach (immutable filenum
, ref nfo
; filelist
) {
194 arcz
.newFile(nfo
.name
, nfo
.size
);
195 auto fi
= File(nfo
.diskname
);
197 auto rd
= fi
.rawRead(rdbuf
[]);
198 if (rd
.length
== 0) break;
202 auto ctt
= MonoTime
.currTime
;
203 if ((ctt
-stt
).total
!"seconds" > 0) {
205 stdout
.write(" [", (filenum
+1).n2c
, "/", filelist
.length
.n2c
, "] files processed\r");
211 writeln("\r", total
.n2c
, " bytes packed to ", getSize(outfname
).n2c
, " (", arcz
.chunksWritten
, " chunks, ", arcz
.filesWritten
, " files)\x1b[K");
215 // ////////////////////////////////////////////////////////////////////////// //
216 int main (string
[] args
) {
220 } catch (Exception e
) {
221 writeln("FATAL: ", e
.msg
);