Skip to main content
The WIA (Wii Archive) and RVZ formats are compressed disc image formats for GameCube and Wii games. These formats offer superior compression compared to older formats like GCZ by supporting modern compression algorithms and storing Wii partition data decrypted without hashes.

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_t struct 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.
The type sha1_hash_t refers to an array of 20 bytes.

File Structures

wia_file_head_t

Stored at offset 0x0, size 0x48 bytes. This format will never be changed according to wit source code.
Format: AABBCCDD = A.BB | A.BB.CC
If D != 0x00 && D != 0xff => append: 'beta' D
Type and NameDescription
char magic[4]Always contains "WIA\x1"
u32 versionThe WIA format version
u32 version_compatibleMinimum version required to read this file. Can be lower than version (wit v2.40a: version=0x01000000, compatible=0x00090000)
u32 disc_sizeSize of the wia_disc_t struct. wit v2.40a always includes the full 7 bytes of compr_data
sha1_hash_t disc_hashSHA-1 hash of the wia_disc_t struct. Number of bytes to hash is determined by disc_size
u64 iso_file_sizeOriginal size of the disc (equivalent ISO file size)
u64 wia_file_sizeSize of this WIA/RVZ file
sha1_hash_t file_head_hashSHA-1 hash of this struct, up to but not including file_head_hash itself

wia_disc_t

Stored at offset 0x48, immediately after wia_file_head_t.
Type and NameDescription
u32 disc_type0 = unknown, 1 = GameCube, 2 = Wii
u32 compression0 = NONE, 1 = PURGE, 2 = BZIP2, 3 = LZMA, 4 = LZMA2, 5 = Zstandard (RVZ only)
u32 compr_levelCompression level used (compressor-specific, informational only). Treat as signed for RVZ due to negative Zstandard levels
u32 chunk_sizeSize 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_partNumber of wia_part_t structs
u32 part_t_sizeSize of one wia_part_t struct. If smaller than expected, fill missing bytes with 0x00
u64 part_offFile offset where wia_part_t structs are stored (uncompressed)
sha1_hash_t part_hashSHA-1 hash of the wia_part_t structs (n_part * part_t_size bytes)
u32 n_raw_dataNumber of wia_raw_data_t structs
u64 raw_data_offFile offset where wia_raw_data_t structs are stored (compressed)
u32 raw_data_sizeTotal compressed size of wia_raw_data_t structs
u32 n_groupsNumber of wia_group_t structs
u64 group_offFile offset where wia_group_t structs are stored (compressed)
u32 group_sizeTotal compressed size of wia_group_t structs
u8 compr_data_lenNumber of used bytes in compr_data array
u8 compr_data[7]Compressor-specific data
NONE, PURGE, BZIP2: compr_data_len is 0LZMA (5 bytes, 7-Zip SDK format):
  • Byte 0: Encodes lc, pb, lp parameters
  • Bytes 1-4: Dictionary size (little endian)
Decoding byte 0:
d = data[0];
if (d >= (9 * 5 * 5))
  return SZ_ERROR_UNSUPPORTED;

p->lc = d % 9;
d /= 9;
p->pb = d / 5;
p->lp = d % 5;
LZMA2 (1 byte, 7-Zip SDK format):
  • Single byte encoding dictionary size
#define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11))

if (prop > 40)
  return SZ_ERROR_UNSUPPORTED;
dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop);
Zstandard (RVZ): No compressor-specific dataPreset dictionaries are not used for any compression method.

wia_part_data_t

Type and NameDescription
u32 first_sectorStarting sector on disc for this data (1 sector = 32 KiB or 31 KiB excluding hashes)
u32 n_sectorsNumber of sectors covered by this struct
u32 group_indexIndex of first wia_group_t struct. Other indices follow sequentially
u32 n_groupsNumber 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.
Wii partition data is stored decrypted with hashes removed. For each 0x8000 bytes on disc, 0x7C00 bytes are stored in the WIA file (before compression).
Type and NameDescription
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 as wia_part_t. Data is stored as-is (with compression applied).
The first wia_raw_data_t has raw_data_off = 0x80 and raw_data_size = 0x4FF80, but actually contains 0x50000 bytes. Handle this by rounding offset down to previous multiple of 0x8000 and adjusting size accordingly.
Type and NameDescription
u64 raw_data_offOffset on disc where this data starts
u64 raw_data_sizeNumber of bytes on disc covered by this struct
u32 group_indexIndex of first wia_group_t struct. Other indices follow sequentially
u32 n_groupsNumber 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 by wia_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 NameDescription
u32 data_off4File offset where compressed data is stored, divided by 4
u32 data_sizeSize 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 of wia_group_t with per-group compression control.
Type and NameDescription
u32 data_off4File offset where compressed data is stored, divided by 4
u32 data_sizeBit 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_sizeSize 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.
  • 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 NameDescription
u16 offsetOffset 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 hashHash to replace the automatically generated hash. Apply after calculating all hashes for current 2 MiB but before encrypting

wia_except_list_t

Each wia_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.
Implementation limits:
  • Dolphin reading: 52×64 = 3328 exceptions max (unlimited for NONE/PURGE)
  • wit reading: Appears to support ~3008+ exceptions
  • Safe assumption: Reading code can handle at least 3328 exceptions per list
Type and NameDescription
u16 n_exceptionsNumber of wia_exception_t structs
wia_exception_t exception[n_exceptions]Each entry describes one hash difference
Storage:
  • PURGE: Exception lists stored uncompressed (before first wia_segment_t)
  • BZIP2, LZMA, LZMA2, Zstandard: Compressed with the rest of the data
Padding:
  • 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 NameDescription
u32 offsetOffset of data within decompressed data (exception lists not counted)
u32 sizeNumber of bytes in data
u8 data[size]Data bytes
Each PURGE chunk contains:
  1. Zero or more wia_segment_t structs in ascending offset order
  2. SHA-1 hash (0x14 bytes) of exception lists (if any) and segment structs
  3. 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

  1. Read 4 bytes as 32-bit unsigned big endian integer (size)
  2. If bit 31 is clear: Read size bytes and output unchanged
  3. If bit 31 is set: Clear bit 31 of size, read 68 bytes of PRNG seed data, output size bytes using PRNG
  4. Repeat until all input consumed

PRNG Algorithm

Lagged Fibonacci generator with parameters: f = xor, j = 32, k = 521
u32 buffer[521];

// Initialize buffer with seed data (big endian)
// Copy 68 bytes (17 words) to buffer start, byteswap if needed

// Fill remaining buffer
for (size_t i = 17; i < 521; i++)
    buffer[i] = (buffer[i - 17] << 23) ^ (buffer[i - 16] >> 9) ^ buffer[i - 1];

// Advance state (run 4 times before first output, then once per 521 words)
for (size_t i = 0; i < 32; i++)
    buffer[i] ^= buffer[i + 521 - 32];

for (size_t i = 32; i < 521; i++)
    buffer[i] ^= buffer[i - 32];
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

u8* out;
u32* buffer_ptr;

/* ... */

*(out++) = *buffer_ptr >> 24;
*(out++) = *buffer_ptr >> 18;  // NB: 18, not 16
*(out++) = *buffer_ptr >> 8;
*(out++) = *buffer_ptr;

buffer_ptr++;

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_t offset rounding
  • Group size calculations for non-aligned partition ends
Both implementations are compatible for reading, but may produce slightly different files when writing.