Magic Pockets RNC Compression
The binary assets in Magic Pockets use RNC ProPack method 1. This is a compression wrapper, not an archive: each file decompresses to one payload interpreted by its own graphics, map, event, sound or driver format. All 60 binary files in the analysed data directory have this wrapper; install.txt does not.
File format
Header
| Offset | Data type | Name | Description |
|---|---|---|---|
| 0 | BYTE[4] | signature | 52 4E 43 01: ASCII RNC followed by method 1 |
| 4 | UINT32BE | unpacked length | Size of the decoded payload |
| 8 | UINT32BE | packed length | Size of the compressed data after the header |
| 12 | UINT16BE | unpacked CRC | CRC-16/ARC of the decoded payload |
| 14 | UINT16BE | packed CRC | CRC of the packed payload |
| 16 | UINT8 | leeway | Workspace/leeway field; not used by the repo decoder |
| 17 | UINT8 | chunks | Number of compressed chunks |
The compressed stream starts at byte 18. Numeric header fields are big-endian, whereas the bit reader consumes little-endian 16-bit words, least significant bit first. The decoded payload's own byte order is independent of the wrapper.
Huffman tables
Discard the first two stream bits during initialisation. Each chunk begins with three fresh Huffman tables, in order: literal-run length, match distance and match length.
For each table, read a 5-bit symbol count and then one 4-bit code length per symbol. A zero length means an unused symbol. Assign canonical codes in increasing code-length and symbol order, reversing the code bits for the LSB-first reader.
The decoded symbol number is a value class. Classes 0 and 1 return those values directly. For class s of 2 or more, read s - 1 extra bits and return:
value = (1 << (s - 1)) | extra_bits
Chunk body
After the tables, read a 16-bit group count. Copy a literal run decoded from the literal-length table. For each remaining group, decode a match distance and match length, copy the match, then copy the next literal run.
match distance = decoded distance + 1 match length = decoded length + 2
Match copies are byte-by-byte and may overlap their own output. A chunk with n groups contains n literal runs and n - 1 matches.
Literal bytes are read directly from the source pointer rather than as Huffman bits. After a literal copy, rebuild the bit window from the new source position while retaining the already-valid unconsumed bits. Simply byte-aligning or clearing the bit reader loses data. The source code implements the original two-word window and this resynchronisation explicitly.
Checksums
The decoded checksum uses reflected CRC-16/ARC, polynomial 0xA001, initial value zero. All 60 checked assets decode to the declared length and match their embedded unpacked CRCs. The implementation comes from the original game's Rnc_Depack routine and helpers at 1000:FCC4, FEC2, FE86 and FF22.
Notes
- This page records the method-1 variant used by the game, not support for every RNC method.
- The EJWLIB logo resources use LZHUF instead. Executable packing and asset packing are separate concerns.
- Matching an unpacked CRC validates the decompressed bytes; it does not by itself identify the payload format.
Source code
From re-magicpockets, the open-source browser port and reverse-engineering project:
- web/core/rnc.js – browser-safe decompressor and CRC-16/ARC
- tools/rnc.js – Node.js decoder
- tools/unpack.js – corpus unpacking and checksum validation
- docs/formats/rnc.md – bit-reader and original-consumer details
Credits
This file format was reverse engineered by VorticonCmdr. Please credit this work in projects that use it; a link back to this wiki is appreciated.