magdevandClaude Fable 5 adb318f57c Reverse engineer Boxsim MyIni2CK container, add bpj2ini decoder
Format: 8-byte magic, XOR layer (table-shuffled 3-LCG PRNG, seeds 123/456),
custom LZ+Huffman codec (A2F). Decompression runs the original ExpandA2F
code from Boxsim.exe under Unicorn. Decodes .bpj and .bch files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-28 19:55:19 +02:00

Boxsim .bpj / .bch file format (reverse engineered)

Boxsim (Visaton, Delphi, codec and container "MyIniFile" by UG 2004) stores projects (.bpj) and driver files (.bch) in a three-layer container. Reverse engineered 2026-09-28 from Boxsim 2.1 DE (Boxsim.exe, no prior public documentation existed).

Container layout

Layer Content
1 8-byte plaintext magic MyIni2CK
2 Everything after the magic is XOR-encrypted with a position-based PRNG keystream, seeds (123, 456), identical for every file
3 The decrypted body is a custom LZ + canonical-Huffman stream ("A2F" codec, CompressA2F/ExpandA2F at VA 0xa1013c/0xa11880)
4 The decompressed payload is plain INI-style text: key=<TAB>value lines, CRLF, #Section headers, German decimal commas, arrays as ;-separated values

Layer 2: the Krypt PRNG

Table-shuffled combination of three LCGs (found at VA 0xa12be4 init, 0xa12c9c next byte). All arithmetic mod 2^32:

init(s1=123, s2=456):
    s3 = (s1 mod 0xFFFE) * (s2 mod 0x7F23)
    repeat 256 times: step(); table[i] = s1 + s2

step():
    s1 = s1 * 0x10B95 + 1
    s2 = s2 * 0x10BA9 + 1        (reset to 0 if >= 0xFFFFFFFF)
    s3 = s3 * 0x1155D + 1

next_byte():
    step()
    idx = s3 and 0xFF
    out = table[idx]; table[idx] = s1 + s2
    return out mod 256

plaintext[i] = filebyte[8+i] XOR next_byte().

Layer 3: the A2F codec

Sliding-window LZ (1 MB window, flush keeps the last 0x3FFF8 bytes) with a 356-symbol canonical Huffman alphabet: 0..255 literals, 0x100 end-of-stream, 0x101 rebuild code-length tables (adaptive), 0x102 special, rest match lengths/distances. Decoding uses a 65536-entry direct lookup table; initial state gives every symbol a 9-bit code. Not re-implemented: bpj2ini.py runs the original ExpandA2F machine code under Unicorn instead, which is exact by construction.

Crossover encoding (#Weiche<n> section)

The crossover editor grid serializes as _Part<N>_* keys. Only non-empty slots are written:

Key Meaning
_Part<N>_typ 0 empty, 1 wire, 2 resistor, 3 capacitor, 4 inductor, 5 driver, 6 amplifier input
_Part<N>_value Component value in SI base units (Ohm, Farad, Henry)
_Part<N>_Rvalue Series resistance of an inductor in Ohm
_Part<N>_bem Part label, e.g. MKT-A, SP 0,6mm, Keramik 10W
_Part<N>_chassisnr Driver index (0-based) for typ 5
_Part<N>_verpolt Polarity flag (-1 / 1) for typ 5 and 6
_Part368..375_nod1/nod2 Extra node-to-node connections

Other sections: #General (nAmps, nChassis), #AktFilter<n> (active filters), #Chassis<n> (TSP set plus measured frequency/impedance/phase arrays), #Einbau<n> (enclosure: Typ 1 closed with Vb in m³, etc.), #Schallwand<n> (baffle geometry in m), #GemGeh (shared enclosure).

Tool

.venv/bin/python bpj2ini.py <input.bpj|.bch> [output.ini]

Needs unicorn and pefile (see .venv/) and Boxsim.exe (path via BOXSIM_EXE env, default: the bundled download under boxsim_dl/). Works for .bpj and .bch alike.

S
Description
Boxsim project file format reverse enigneering
Readme MIT
391 KiB
1 Watchers 0 Forks
Languages
Python 97.3%
Shell 1.4%
Dockerfile 1.3%