Format Overview
WIA Features
- Support for compression algorithms: bzip2, LZMA, and LZMA2
- Wii partition data stored decrypted and without hashes for better compression
- Chunk-based compression enabling random access
- Version 1.00 as implemented in wit v2.40a
RVZ Enhancements
RVZ is based on WIA with the following improvements:- Zstandard compression support
- Removed PURGE compression method
- Support for chunk sizes smaller than 2 MiB (minimum 32 KiB, must be power of two)
- Enhanced
wia_group_tstruct with per-group compression flags - Lossless storage of pseudorandom padding data
Data Format Conventions
All integers are big endian unless otherwise noted. Data in WIA/RVZ files can be stored in any order unless explicitly specified.
sha1_hash_t refers to an array of 20 bytes.
File Structures
wia_file_head_t
Stored at offset0x0, size 0x48 bytes. This format will never be changed according to wit source code.
Version Number Format
Version Number Format
| Type and Name | Description |
|---|---|
char magic[4] | Always contains "WIA\x1" |
u32 version | The WIA format version |
u32 version_compatible | Minimum version required to read this file. Can be lower than version (wit v2.40a: version=0x01000000, compatible=0x00090000) |
u32 disc_size | Size of the wia_disc_t struct. wit v2.40a always includes the full 7 bytes of compr_data |
sha1_hash_t disc_hash | SHA-1 hash of the wia_disc_t struct. Number of bytes to hash is determined by disc_size |
u64 iso_file_size | Original size of the disc (equivalent ISO file size) |
u64 wia_file_size | Size of this WIA/RVZ file |
sha1_hash_t file_head_hash | SHA-1 hash of this struct, up to but not including file_head_hash itself |
wia_disc_t
Stored at offset0x48, immediately after wia_file_head_t.
| Type and Name | Description |
|---|---|
u32 disc_type | 0 = unknown, 1 = GameCube, 2 = Wii |
u32 compression | 0 = NONE, 1 = PURGE, 2 = BZIP2, 3 = LZMA, 4 = LZMA2, 5 = Zstandard (RVZ only) |
u32 compr_level | Compression level used (compressor-specific, informational only). Treat as signed for RVZ due to negative Zstandard levels |
u32 chunk_size | Size of data chunks. WIA: must be multiple of 2 MiB. RVZ: minimum 32 KiB, must be power of two if <2 MiB |
u8 dhead[0x80] | First 0x80 bytes of the disc image |
u32 n_part | Number of wia_part_t structs |
u32 part_t_size | Size of one wia_part_t struct. If smaller than expected, fill missing bytes with 0x00 |
u64 part_off | File offset where wia_part_t structs are stored (uncompressed) |
sha1_hash_t part_hash | SHA-1 hash of the wia_part_t structs (n_part * part_t_size bytes) |
u32 n_raw_data | Number of wia_raw_data_t structs |
u64 raw_data_off | File offset where wia_raw_data_t structs are stored (compressed) |
u32 raw_data_size | Total compressed size of wia_raw_data_t structs |
u32 n_groups | Number of wia_group_t structs |
u64 group_off | File offset where wia_group_t structs are stored (compressed) |
u32 group_size | Total compressed size of wia_group_t structs |
u8 compr_data_len | Number of used bytes in compr_data array |
u8 compr_data[7] | Compressor-specific data |
Compression-Specific Data
Compression-Specific Data
NONE, PURGE, BZIP2: LZMA2 (1 byte, 7-Zip SDK format):Zstandard (RVZ): No compressor-specific dataPreset dictionaries are not used for any compression method.
compr_data_len is 0LZMA (5 bytes, 7-Zip SDK format):- Byte 0: Encodes
lc,pb,lpparameters - Bytes 1-4: Dictionary size (little endian)
- Single byte encoding dictionary size
wia_part_data_t
| Type and Name | Description |
|---|---|
u32 first_sector | Starting sector on disc for this data (1 sector = 32 KiB or 31 KiB excluding hashes) |
u32 n_sectors | Number of sectors covered by this struct |
u32 group_index | Index of first wia_group_t struct. Other indices follow sequentially |
u32 n_groups | Number of wia_group_t structs used for this data |
wia_part_t
Tracks Wii partition data that is encrypted and hashed on actual discs. Does not include the unencrypted partition header (ticket, TMD, certificate chain, H3 table).For a typical game partition,
pd[0].first_sector * 0x8000 would be 0x0F820000, not 0x0F800000.0x8000 bytes on disc, 0x7C00 bytes are stored in the WIA file (before compression).
| Type and Name | Description |
|---|---|
u8 part_key[16] | Title key for this partition (128-bit AES). Already decrypted, can be used directly without Wii common key |
wia_part_data_t pd[2] | Segment 0: Small segment for management data (boot to FST). Segment 1: Remaining data. Split point is FST end offset rounded up to next 2 MiB |
wia_raw_data_t
Tracks disc data not stored aswia_part_t. Data is stored as-is (with compression applied).
| Type and Name | Description |
|---|---|
u64 raw_data_off | Offset on disc where this data starts |
u64 raw_data_size | Number of bytes on disc covered by this struct |
u32 group_index | Index of first wia_group_t struct. Other indices follow sequentially |
u32 n_groups | Number of wia_group_t structs used for this data |
wia_group_t (WIA)
Points directly to compressed disc data. Interpretation differs based on whether referenced bywia_part_data_t or wia_raw_data_t.
A group normally contains chunk_size bytes of decompressed data (or chunk_size / 0x8000 * 0x7C00 for Wii partition data excluding hashes). The last group may contain less data.
| Type and Name | Description |
|---|---|
u32 data_off4 | File offset where compressed data is stored, divided by 4 |
u32 data_size | Size of compressed data, including wia_except_list_t structs and required padding. 0 = special case: all decompressed data is 0x00 and exception lists (if any) contain 0 exceptions |
rvz_group_t (RVZ)
Expanded version ofwia_group_t with per-group compression control.
| Type and Name | Description |
|---|---|
u32 data_off4 | File offset where compressed data is stored, divided by 4 |
u32 data_size | Bit 31: 1 if using compression method from wia_disc_t, 0 if using NONE. Bits 0-30: Size of compressed data including exception lists and padding. 0 = special case: all data is 0x00 with 0 exceptions |
u32 rvz_packed_size | Size after decompression but before RVZ unpacking. 0 = RVZ packing not used for this group |
wia_exception_t
Represents a 20-byte difference between recalculated hash data and original hash data.Implementation Notes
Implementation Notes
- When recalculating hashes for a group not evenly divisible by 2 MiB, treat missing bytes as zeroes
- wit: Only outputs exceptions for actual hash data, not padding
- Dolphin: Outputs exceptions for both hash data and padding. For 32-byte padding areas, writes two overlapping exceptions (first 20 bytes, last 20 bytes = 12 bytes overlap)
| Type and Name | Description |
|---|---|
u16 offset | Offset among the hashes. Offsets 0x0000-0x0400 map to 0x0000-0x0400 in full 2 MiB; offsets 0x0400-0x0800 map to 0x8000-0x8400, etc. Resets to 0 for each new wia_except_list_t |
sha1_hash_t hash | Hash to replace the automatically generated hash. Apply after calculating all hashes for current 2 MiB but before encrypting |
wia_except_list_t
Eachwia_group_t of Wii partition data contains one or more exception lists before the actual data.
Number of lists per group: Always chunk_size / 0x200000, even for undersized groups at partition end.
| Type and Name | Description |
|---|---|
u16 n_exceptions | Number of wia_exception_t structs |
wia_exception_t exception[n_exceptions] | Each entry describes one hash difference |
Compression Method Exceptions
Compression Method Exceptions
Storage:
- PURGE: Exception lists stored uncompressed (before first
wia_segment_t) - BZIP2, LZMA, LZMA2, Zstandard: Compressed with the rest of the data
- NONE, PURGE: Padding inserted after last exception list if end offset not divisible by 4
- Other methods: No padding inserted
wia_segment_t
Used by the PURGE compression method to efficiently store runs of zeroes.PURGE is only available in WIA, not in RVZ.
| Type and Name | Description |
|---|---|
u32 offset | Offset of data within decompressed data (exception lists not counted) |
u32 size | Number of bytes in data |
u8 data[size] | Data bytes |
- Zero or more
wia_segment_tstructs in ascendingoffsetorder - SHA-1 hash (0x14 bytes) of exception lists (if any) and segment structs
- Bytes not covered by any segment are set to
0x00
RVZ Packing
RVZ introduces a packing encoding scheme for pseudorandom padding data, applied before bzip2/LZMA/Zstandard compression.Decoding Algorithm
- Read 4 bytes as 32-bit unsigned big endian integer (
size) - If bit 31 is clear: Read
sizebytes and output unchanged - If bit 31 is set: Clear bit 31 of
size, read 68 bytes of PRNG seed data, outputsizebytes using PRNG - Repeat until all input consumed
PRNG Algorithm
Lagged Fibonacci generator with parameters: f = xor, j = 32, k = 521Offset Alignment
Offset Alignment
Before outputting data, check if offset (relative to disc start for
wia_raw_data_t, partition data start for wia_part_t) is evenly divisible by 32 KiB.If not, advance PRNG state by offset % 0x8000 bytes first.For
wia_part_t, hashes are not counted in offset calculation, but the number is still 32 KiB (not 31 KiB).Output Generation
Implementation Notes
Dolphin vs wit Differences
Where Dolphin’s implementation differs from wit:- Hash exception handling for padding areas
- Special handling of first
wia_raw_data_toffset rounding - Group size calculations for non-aligned partition ends
Both implementations are compatible for reading, but may produce slightly different files when writing.