aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Tatham <anakin@pobox.com>2017-05-16 19:06:05 +0100
committerSimon Tatham <anakin@pobox.com>2017-05-16 19:06:05 +0100
commitf0cdcb370ec7023428bc421e3a8b5f5cbf1de502 (patch)
tree8e488b5a958d9c37f23986d8fbda19781b142db5
parentdecda92874250a1e25d185fa8fd1f2d2f4b94d20 (diff)
downloadwix-on-linux-f0cdcb370ec7023428bc421e3a8b5f5cbf1de502.tar.gz
wix-on-linux-f0cdcb370ec7023428bc421e3a8b5f5cbf1de502.tar.bz2
wix-on-linux-f0cdcb370ec7023428bc421e3a8b5f5cbf1de502.zip
My own CAB-maker, which compresses.
lcab produces uncompressed CAB files, which is a bit low-quality for my taste - especially when it turns out CAB files have Deflate as one of their compression options, so I can write a compressed CAB-builder in only about 100 lines of Python using the zlib module! I'd expected to have to faff about finding an implementation of LZX, but there's really no need to bother.
-rw-r--r--fake-winterop.c17
-rwxr-xr-xmakecab.py107
2 files changed, 112 insertions, 12 deletions
diff --git a/fake-winterop.c b/fake-winterop.c
index eb891c4..cc69179 100644
--- a/fake-winterop.c
+++ b/fake-winterop.c
@@ -26,7 +26,6 @@ uint32_t ResetAcls(const char16_t **pwzFiles, uint32_t cFiles)
26} 26}
27 27
28typedef struct CabCreateContext { 28typedef struct CabCreateContext {
29 char *tempdir;
30 char *outdir; 29 char *outdir;
31 char *outfile; 30 char *outfile;
32 31
@@ -43,14 +42,11 @@ uint32_t CreateCabBegin(const char16_t *wzCab, const char16_t *wzCabDir,
43 ctx->outdir = ascii(wzCabDir, true); 42 ctx->outdir = ascii(wzCabDir, true);
44 ctx->outfile = dupcat(ctx->outdir, "/", 43 ctx->outfile = dupcat(ctx->outdir, "/",
45 ascii(wzCab, true), (const char *)NULL); 44 ascii(wzCab, true), (const char *)NULL);
46 ctx->tempdir = dupcat(ctx->outdir, "/cabXXXXXX", (const char *)NULL);
47 if (!mkdtemp(ctx->tempdir))
48 err(1, "mkdtemp");
49 ctx->nargs = 0; 45 ctx->nargs = 0;
50 ctx->argsize = 16; 46 ctx->argsize = 16;
51 ctx->args = snewn(ctx->argsize, char *); 47 ctx->args = snewn(ctx->argsize, char *);
52 ctx->args[ctx->nargs++] = dupcat("lcab", (const char *)NULL); 48 ctx->args[ctx->nargs++] = dupcat("makecab.py", (const char *)NULL);
53 ctx->args[ctx->nargs++] = dupcat("-n", (const char *)NULL); 49 ctx->args[ctx->nargs++] = ctx->outfile;
54 *out_ctx = ctx; 50 *out_ctx = ctx;
55 return 0; 51 return 0;
56} 52}
@@ -61,16 +57,14 @@ uint32_t CreateCabAddFile(const char16_t *wzFile, const char16_t *wzToken,
61 char *file = ascii(wzFile, true); 57 char *file = ascii(wzFile, true);
62 char *file_abs = realpath(file, NULL); 58 char *file_abs = realpath(file, NULL);
63 char *cabname = ascii(wzToken, true); 59 char *cabname = ascii(wzToken, true);
64 char *cab_abs = dupcat(ctx->tempdir, "/", cabname, (const char *)NULL);
65 printf("CreateCabAddFile: %s :: %s <- %s\n", ctx->outfile, 60 printf("CreateCabAddFile: %s :: %s <- %s\n", ctx->outfile,
66 cabname, file_abs); 61 cabname, file_abs);
67 if (symlink(file_abs, cab_abs) < 0)
68 err(1, "symlink");
69 if (ctx->nargs + 1 >= ctx->argsize) { 62 if (ctx->nargs + 1 >= ctx->argsize) {
70 ctx->argsize = ctx->nargs * 5 / 4 + 16; 63 ctx->argsize = ctx->nargs * 5 / 4 + 16;
71 ctx->args = sresize(ctx->args, ctx->argsize, char *); 64 ctx->args = sresize(ctx->args, ctx->argsize, char *);
72 } 65 }
73 ctx->args[ctx->nargs++] = cab_abs; 66 ctx->args[ctx->nargs++] = cabname;
67 ctx->args[ctx->nargs++] = file_abs;
74 return 0; 68 return 0;
75} 69}
76 70
@@ -86,11 +80,10 @@ uint32_t CreateCabAddFiles(const char16_t *const *pwzFiles,
86 80
87uint32_t CreateCabFinish(CabCreateContext *ctx, void (*split_callback)(void)) 81uint32_t CreateCabFinish(CabCreateContext *ctx, void (*split_callback)(void))
88{ 82{
89 if (ctx->nargs + 2 >= ctx->argsize) { 83 if (ctx->nargs + 1 >= ctx->argsize) {
90 ctx->argsize = ctx->nargs * 5 / 4 + 16; 84 ctx->argsize = ctx->nargs * 5 / 4 + 16;
91 ctx->args = sresize(ctx->args, ctx->argsize, char *); 85 ctx->args = sresize(ctx->args, ctx->argsize, char *);
92 } 86 }
93 ctx->args[ctx->nargs++] = ctx->outfile;
94 ctx->args[ctx->nargs++] = NULL; 87 ctx->args[ctx->nargs++] = NULL;
95 system_argv_array(ctx->args); 88 system_argv_array(ctx->args);
96 return 0; 89 return 0;
diff --git a/makecab.py b/makecab.py
new file mode 100755
index 0000000..befa94a
--- /dev/null
+++ b/makecab.py
@@ -0,0 +1,107 @@
1#!/usr/bin/env python
2
3import sys
4import os
5import time
6import zlib
7import struct
8from collections import namedtuple
9
10CFHEADER_s = struct.Struct("<4sLLLLLBBHHHHH")
11CFHEADER = namedtuple("CFHEADER", "sig res0 size res1 firstfile res2 "
12 "verminor vermajor folders files flags setid icabinet")
13CFHEADER_sig = "MSCF"
14
15CFFOLDER_s = struct.Struct("<LHH")
16CFFOLDER = namedtuple("CFFOLDER", "firstdata ndata compresstype")
17
18CFFILE_s = struct.Struct("<LLHHHH")
19CFFILE = namedtuple("CFFILE", "size offset ifolder date time attrs")
20
21CFDATA_s = struct.Struct("<LHH")
22CFDATA = namedtuple("CFDATA", "checksum compressedlen uncompressedlen")
23
24def mszip(data):
25 compressor = zlib.compressobj(9, zlib.DEFLATED, -zlib.MAX_WBITS,
26 zlib.DEF_MEM_LEVEL, zlib.Z_DEFAULT_STRATEGY)
27 compressed = compressor.compress(data)
28 compressed += compressor.flush()
29 return "CK" + compressed # add MSZIP header
30
31def packdate(y,m,d):
32 return ((y - 1980) << 9) | (m << 5) | d
33def packtime(h,m,s):
34 return ((h << 11) | (m << 5) | (s >> 1))
35
36def checksum(data):
37 data += "\0" * (3 & -len(data)) # pad to multiple of 4 bytes
38 toret = 0
39 for offset in xrange(0, len(data), 4):
40 toret ^= struct.unpack_from("<L", data, offset)[0]
41 return toret
42
43def build_cab(files):
44 uncompressed_data = ""
45 fileheaders = ""
46 for name, data, mtime in files:
47 mtime_u = time.gmtime(mtime)
48 fileheader = CFFILE(
49 size=len(data), offset=len(uncompressed_data), ifolder=0, attrs=0,
50 date=packdate(mtime_u.tm_year, mtime_u.tm_mon, mtime_u.tm_mday),
51 time=packtime(mtime_u.tm_hour, mtime_u.tm_min, mtime_u.tm_sec))
52 uncompressed_data += data
53 fileheaders += CFFILE_s.pack(*fileheader) + name + "\0"
54
55 compressed_data = ""
56 offset = 0
57 n_data_blocks = 0
58 while offset < len(uncompressed_data):
59 uncompressed_block = uncompressed_data[offset:offset+0x8000]
60 compressed_block = mszip(uncompressed_block)
61 blockheader = CFDATA(
62 checksum=0,
63 compressedlen=len(compressed_block),
64 uncompressedlen=len(uncompressed_block))
65 header_after_checksum = CFDATA_s.pack(*blockheader)[4:]
66 blockheader = blockheader._replace(
67 checksum=checksum(header_after_checksum + compressed_block))
68 compressed_data += CFDATA_s.pack(*blockheader) + compressed_block
69 offset += len(uncompressed_block)
70 n_data_blocks += 1
71
72 totalsize = (CFHEADER_s.size +
73 CFFOLDER_s.size +
74 len(fileheaders) +
75 len(compressed_data))
76
77 header = CFHEADER(
78 sig=CFHEADER_sig, res0=0, res1=0, res2=0,
79 vermajor=1, verminor=3, folders=1, files=len(files),
80 flags=0, setid=0, icabinet=0, size=totalsize,
81 firstfile=CFHEADER_s.size + CFFOLDER_s.size)
82
83 folder = CFFOLDER(
84 ndata=n_data_blocks, compresstype=1,
85 firstdata = (CFHEADER_s.size + CFFOLDER_s.size + len(fileheaders)))
86
87 return (CFHEADER_s.pack(*header) +
88 CFFOLDER_s.pack(*folder) +
89 fileheaders +
90 compressed_data)
91
92def main():
93 args = sys.argv[1:]
94 outfile = args.pop(0)
95 files = []
96 while len(args) > 0:
97 cabname = args.pop(0)
98 filename = args.pop(0)
99 with open(filename, "rb") as f:
100 filedata = f.read()
101 files.append((cabname, filedata, os.stat(filename).st_mtime))
102 cabdata = build_cab(files)
103 with open(outfile, "wb") as f:
104 f.write(cabdata)
105
106if __name__ == '__main__':
107 main()