-
Notifications
You must be signed in to change notification settings - Fork 0
compression packing
compression field of the disc struct:
| ID | Name | Read support | Write support | Notes |
|---|---|---|---|---|
| 0 | None | ✔ | ✔ | identity |
| 1 | Purge | ✔ (WIA) | ✖ (RVZ) | WIA-only; rejected by RVZ readers |
| 2 | Bzip2 | ✔ | ✔ | level 1–9 |
| 3 | LZMA | ✔ | ✔ | LZMA1, 7-Zip properties |
| 4 | LZMA2 | ✔ | ✔ | raw LZMA2 chunk framing |
| 5 | Zstd | ✔ | ✔ | level 1–22 (default 3) |
| Method | Properties |
|---|---|
| None / Purge / Bzip2 / Zstd | empty |
| LZMA | 5 bytes: lc + lp·9 + pb·45 (7-Zip default 0x5D) + 4-byte little-endian dictionary size |
| LZMA2 | 1 byte: dictionary-size property (2^(prop/2+12) style, 7-Zip table) |
The writer's LZMA dictionary size depends on the level: 1<<18 (≤1), 1<<20 (≤3),
1<<23 (≤5), 1<<25 (≤7), 1<<27 otherwise; position bits 2, literal context 3,
literal position 0.
-
LZMA1: the 7-Zip encoder's output (range-coder data) after the 5-byte properties;
the properties travel in
compr_data, not in the stream. The encoder runs with a known size (no end marker) — both Dolphin and RVZSharp decode with a known output size. -
LZMA2: a sequence of chunks, each
[control][unpack_size-1:2][pack_size-1:2][props:1] [LZMA1 stream], ended by a0x00control byte. Chunk payloads are capped at 0xF800 bytes and each chunk resets the dictionary (control0xE0 | (size-1)>>16). The single props byte (0x5D) must match thecompr_dataproperty.
- Lists + data are compressed together for methods > Purge; otherwise the lists are stored raw (padded to 4) ahead of the compressed stream.
- The
compressedflag (bit 31 of the group size) is set only when compression shrank the data. - All-zero chunks with no exceptions become zero groups (size 0) — nothing is stored.
Wii discs are full of padding — pseudo-random junk produced by Nintendo's generator.
Instead of storing it, RVZ stores a seed that regenerates the junk, plus segment
headers. Packing is optional (Packing option / --no-packing); a file without packing is
perfectly valid.
- Lagged Fibonacci generator:
k = 521,j = 32, XOR combination. - Seed: 17 big-endian
u32words (68 bytes). Words 17..520 are derived byword[i] = (word[i-17] << 23) ^ (word[i-16] >> 9) ^ word[i-1], then 4 warm-up advances. - Output byte order per word:
>>24,>>18(not>>16— this is the famous oddity),>>8,>>0. - The stream position is defined as
offset % 0x8000: junk at disc offset P equals the generator output starting at positionP % 0x8000. This is why discs' junk looks periodic per 32 KiB.
A packed chunk is a sequence of segments; rvz_packed_size is the total bytes of the
segment stream (headers included):
| Segment | Layout |
|---|---|
| literal |
u32 BE size (no MSB) + size literal bytes |
| junk | `u32 BE size |
Special case: if a chunk contains no junk at all, the writer stores the whole chunk
literally without any headers and rvz_packed_size stays 0 — readers treat 0 as "no
packing".
The reader processes segments sequentially and skips the PRNG by the running data offset
((chunkStart + bytesEmitted) % 0x8000) before each junk segment.
LaggedFibonacciGenerator.GetSeed(data, size, dataOffsetMod) implements Dolphin's reverse
algorithm:
- Quick filter: the first 521 words must satisfy the generator's bit-structure property (bits 22–23 == bits 20–21 of the swapped word); random data fails almost always.
- Fill the 521-word state from the junk and rewind it to position 0 (partial
Backwardfor the word offset, fullBackwardper 521-word cycle). - Reconstruct the 17-word seed (undoing the transform and recovering bits 16–17 from the neighbouring words), re-derive words 17..520 and verify them.
- Re-advance to the junk position and count how many bytes the seed actually reproduces.
GetSeed is best-effort: junk regions that fail the checks (or diverge) are stored
literally. The writer scans forward in 32 KiB-aligned steps with a running data offset
(offset % 0x8000), records zero runs as zero-junk when uncompressed, and emits segments
per chunk.
Recovered seeds are canonical: the writer's generator and the reader's PRNG agree on them even though their internal word formats differ (the reconstruction makes the seed invariant under the output transform).
Dolphin's default for RVZ is Zstandard — best ratio/speed trade-off for game data, and the only method that consistently beats LZMA2 on random-ish data. LZMA2 is the strongest compressor for highly compressible data; Bzip2 is a middle ground; None is useful for speed-critical round trips.