joeylib2/tools/szddExpand.py

81 lines
2.5 KiB
Python
Executable file

#!/usr/bin/env python3
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
# See LICENSE.md for license information.
# SPDX-License-Identifier: MIT
"""Expand a file compressed by Microsoft's COMPRESS.EXE (SZDD, the WINGDE.DL_
kind on Windows 3.x setup disks), what EXPAND.EXE or msexpand do, so the
installer needs neither.
The format: the 8-byte magic, a mode byte ('A'), the name's last character
(the one the _ replaced), the expanded length (32-bit little-endian), then
LZSS: a control byte, its bits low first, a 1 meaning a literal byte and a 0
a two-byte match -- offset b0 | (b1 & 0xF0) << 4 into a 4096-byte window
that starts filled with spaces and written from 4096 - 16, length
(b1 & 0x0F) + 3.
Usage:
tools/szddExpand.py IN OUT
"""
import sys
MAGIC = b"SZDD\x88\xf0\x27\x33"
HEADER_BYTES = 14
WINDOW_BYTES = 4096
WINDOW_MASK = WINDOW_BYTES - 1
WINDOW_START = WINDOW_BYTES - 16
WINDOW_FILL = 0x20
MATCH_MIN = 3
def expand(data):
if data[:len(MAGIC)] != MAGIC:
raise ValueError("not an SZDD file")
length = int.from_bytes(data[10:HEADER_BYTES], "little")
window = bytearray([WINDOW_FILL]) * WINDOW_BYTES
pos = WINDOW_START
out = bytearray()
i = HEADER_BYTES
while i < len(data) and len(out) < length:
control = data[i]
i += 1
for bit in range(8):
if i >= len(data) or len(out) >= length:
break
if control & (1 << bit):
byte = data[i]
i += 1
out.append(byte)
window[pos] = byte
pos = (pos + 1) & WINDOW_MASK
else:
if i + 1 >= len(data):
break
b0 = data[i]
b1 = data[i + 1]
i += 2
offset = b0 | ((b1 & 0xF0) << 4)
for k in range((b1 & 0x0F) + MATCH_MIN):
byte = window[(offset + k) & WINDOW_MASK]
out.append(byte)
window[pos] = byte
pos = (pos + 1) & WINDOW_MASK
if len(out) != length:
raise ValueError("expanded %d bytes, the header says %d" % (len(out), length))
return bytes(out)
def main():
if len(sys.argv) != 3:
sys.stderr.write(__doc__)
return 2
with open(sys.argv[1], "rb") as f:
data = f.read()
with open(sys.argv[2], "wb") as f:
f.write(expand(data))
return 0
if __name__ == "__main__":
sys.exit(main())