1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
#!/usr/bin/env python3
import sys
import os
import time
import zlib
import struct
from collections import namedtuple
CFHEADER_s = struct.Struct("<4sLLLLLBBHHHHH")
CFHEADER = namedtuple("CFHEADER", "sig res0 size res1 firstfile res2 "
"verminor vermajor folders files flags setid icabinet")
CFHEADER_sig = b"MSCF"
CFFOLDER_s = struct.Struct("<LHH")
CFFOLDER = namedtuple("CFFOLDER", "firstdata ndata compresstype")
CFFILE_s = struct.Struct("<LLHHHH")
CFFILE = namedtuple("CFFILE", "size offset ifolder date time attrs")
CFDATA_s = struct.Struct("<LHH")
CFDATA = namedtuple("CFDATA", "checksum compressedlen uncompressedlen")
def mszip(data):
compressor = zlib.compressobj(9, zlib.DEFLATED, -zlib.MAX_WBITS,
zlib.DEF_MEM_LEVEL, zlib.Z_DEFAULT_STRATEGY)
compressed = compressor.compress(data)
compressed += compressor.flush()
return b"CK" + compressed # add MSZIP header
def packdate(y,m,d):
return ((y - 1980) << 9) | (m << 5) | d
def packtime(h,m,s):
return ((h << 11) | (m << 5) | (s >> 1))
def checksum(data):
data_fullwords_len = len(data) & ~3
data_last_word = data[data_fullwords_len:]
data_last_word = bytes(reversed(data_last_word))
data_last_word += b"\0" * (3 & -len(data)) # pad to multiple of 4 bytes
toret = 0
for part, partlen in ((data, data_fullwords_len),
(data_last_word, len(data_last_word))):
for offset in range(0, partlen, 4):
toret ^= struct.unpack_from("<L", part, offset)[0]
return toret
def build_cab(files):
uncompressed_data = b""
fileheaders = b""
for name, data, mtime in files:
mtime_u = time.gmtime(mtime)
fileheader = CFFILE(
size=len(data), offset=len(uncompressed_data), ifolder=0, attrs=0,
date=packdate(mtime_u.tm_year, mtime_u.tm_mon, mtime_u.tm_mday),
time=packtime(mtime_u.tm_hour, mtime_u.tm_min, mtime_u.tm_sec))
uncompressed_data += data
fileheaders += CFFILE_s.pack(*fileheader)
fileheaders += name.encode("ASCII") + b"\0"
compressed_data = b""
offset = 0
n_data_blocks = 0
while offset < len(uncompressed_data):
uncompressed_block = uncompressed_data[offset:offset+0x8000]
compressed_block = mszip(uncompressed_block)
blockheader = CFDATA(
checksum=0,
compressedlen=len(compressed_block),
uncompressedlen=len(uncompressed_block))
header_after_checksum = CFDATA_s.pack(*blockheader)[4:]
blockheader = blockheader._replace(
checksum=checksum(header_after_checksum + compressed_block))
compressed_data += CFDATA_s.pack(*blockheader) + compressed_block
offset += len(uncompressed_block)
n_data_blocks += 1
totalsize = (CFHEADER_s.size +
CFFOLDER_s.size +
len(fileheaders) +
len(compressed_data))
header = CFHEADER(
sig=CFHEADER_sig, res0=0, res1=0, res2=0,
vermajor=1, verminor=3, folders=1, files=len(files),
flags=0, setid=0, icabinet=0, size=totalsize,
firstfile=CFHEADER_s.size + CFFOLDER_s.size)
folder = CFFOLDER(
ndata=n_data_blocks, compresstype=1,
firstdata = (CFHEADER_s.size + CFFOLDER_s.size + len(fileheaders)))
return (CFHEADER_s.pack(*header) +
CFFOLDER_s.pack(*folder) +
fileheaders +
compressed_data)
def main():
args = sys.argv[1:]
outfile = args.pop(0)
files = []
while len(args) > 0:
cabname = args.pop(0)
filename = args.pop(0)
with open(filename, "rb") as f:
filedata = f.read()
files.append((cabname, filedata, os.stat(filename).st_mtime))
cabdata = build_cab(files)
with open(outfile, "wb") as f:
f.write(cabdata)
if __name__ == '__main__':
main()
|