aboutsummaryrefslogtreecommitdiff
path: root/makecab.py
diff options
context:
space:
mode:
Diffstat (limited to 'makecab.py')
-rwxr-xr-xmakecab.py107
1 files changed, 107 insertions, 0 deletions
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()