Replaces the bpj2ini.py prototype with a packaged tool. The A2F codec is now reimplemented in Python, so neither Boxsim.exe nor a CPU emulator is needed to decode a container; the emulator survives as a development-only oracle that the codec is differentially tested against. Verified: - 134 containers decode byte-identically to Boxsim's own decoder, 270 pre-2.0 files rejected by both implementations (tools/difftest.py --self-test) - Default.BPJ: 22 components, 14 nets, no dangling terminal - 22 of 22 parts resolve to library symbols; one series inferred from a sibling, two DC resistance deviations reported - ERC clean and the netlist KiCad extracts from the generated sheet is an identical partition to the one derived from the Boxsim grid - container build byte-identical to the host build - 32 tests passing, ruff clean Decode time went 69 s -> 14.5 s (binding emulator hooks to trap addresses) -> 1.6 s (pure Python). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
36 lines
979 B
Python
36 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")
|