35 lines
979 B
Python
35 lines
979 B
Python
"""The MyIni2CK container: magic, XOR scrambling, A2F compression.
|
|||
|
|
|
||
|
|
Boxsim uses one container for project files (`.bpj`) and driver files (`.bch`).
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from . import a2f
|
||
|
|
from .krypt import unscramble
|
||
|
|
|
||
|
|
MAGIC = b"MyIni2CK"
|
||
|
|
|
||
|
|
|
||
|
|
class ContainerError(ValueError):
|
||
|
|
pass
|
||
|
|
|
||
|
|
|
||
|
|
def is_container(raw: bytes) -> bool:
|
||
|
|
return raw[: len(MAGIC)] == MAGIC
|
||
|
|
|
||
|
|
|
||
|
|
def decode(raw: bytes) -> bytes:
|
||
|
|
"""Decode a Boxsim container to its raw INI payload."""
|
||
|
|
if not is_container(raw):
|
||
|
|
raise ContainerError(
|
||
|
|
f"not a MyIni2CK file (magic {raw[:8]!r}, expected {MAGIC!r})"
|
||
|
|
)
|
||
|
|
# The compressed layer reads its own 8 byte header back, so hand it the
|
||
|
|
# magic followed by the descrambled body exactly as Boxsim does.
|
||
|
|
return a2f.expand(MAGIC + unscramble(raw[len(MAGIC) :]))
|
||
|
|
|
||
|
|
|
||
|
|
def decode_text(raw: bytes) -> str:
|
||
|
|
"""Decode to text. Boxsim writes Windows-1252 with CRLF line ends."""
|
||
|
|
return decode(raw).decode("cp1252")
|