python-rsrcfork/rsrcfork/compress/__init__.py

45 lines
1.5 KiB
Python
Raw Normal View History

from . import dcmp0
from . import dcmp1
from . import dcmp2
from .common import DecompressError, CompressedApplicationHeaderInfo, CompressedHeaderInfo, CompressedSystemHeaderInfo
__all__ = [
"DecompressError",
"decompress",
]
# Maps 'dcmp' IDs to their corresponding Python implementations.
# Each decompressor has the signature (header_info: CompressedHeaderInfo, data: bytes, *, debug: bool=False) -> bytes.
DECOMPRESSORS = {
0: dcmp0.decompress,
1: dcmp1.decompress,
2: dcmp2.decompress,
}
def decompress_parsed(header_info: CompressedHeaderInfo, data: bytes, *, debug: bool=False) -> bytes:
"""Decompress the given compressed resource data, whose header has already been removed and parsed into a CompressedHeaderInfo object."""
try:
decompress_func = DECOMPRESSORS[header_info.dcmp_id]
except KeyError:
raise DecompressError(f"Unsupported 'dcmp' ID: {header_info.dcmp_id}")
decompressed = decompress_func(header_info, data, debug=debug)
if len(decompressed) != header_info.decompressed_length:
raise DecompressError(f"Actual length of decompressed data ({len(decompressed)}) does not match length stored in resource ({header_info.decompressed_length})")
return decompressed
def decompress(data: bytes, *, debug: bool=False) -> bytes:
"""Decompress the given compressed resource data."""
header_info = CompressedHeaderInfo.parse(data)
if debug:
print(f"Compressed resource data header: {header_info}")
return decompress_parsed(header_info, data[header_info.header_length:], debug=debug)