diff options
Diffstat (limited to 'makecab.py')
-rwxr-xr-x | makecab.py | 107 |
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 | |||
3 | import sys | ||
4 | import os | ||
5 | import time | ||
6 | import zlib | ||
7 | import struct | ||
8 | from collections import namedtuple | ||
9 | |||
10 | CFHEADER_s = struct.Struct("<4sLLLLLBBHHHHH") | ||
11 | CFHEADER = namedtuple("CFHEADER", "sig res0 size res1 firstfile res2 " | ||
12 | "verminor vermajor folders files flags setid icabinet") | ||
13 | CFHEADER_sig = "MSCF" | ||
14 | |||
15 | CFFOLDER_s = struct.Struct("<LHH") | ||
16 | CFFOLDER = namedtuple("CFFOLDER", "firstdata ndata compresstype") | ||
17 | |||
18 | CFFILE_s = struct.Struct("<LLHHHH") | ||
19 | CFFILE = namedtuple("CFFILE", "size offset ifolder date time attrs") | ||
20 | |||
21 | CFDATA_s = struct.Struct("<LHH") | ||
22 | CFDATA = namedtuple("CFDATA", "checksum compressedlen uncompressedlen") | ||
23 | |||
24 | def 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 | |||
31 | def packdate(y,m,d): | ||
32 | return ((y - 1980) << 9) | (m << 5) | d | ||
33 | def packtime(h,m,s): | ||
34 | return ((h << 11) | (m << 5) | (s >> 1)) | ||
35 | |||
36 | def 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 | |||
43 | def 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 | |||
92 | def 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 | |||
106 | if __name__ == '__main__': | ||
107 | main() | ||