# Legacy formats All legacy formats are decoded to the canonical ISO view through `IBlobReader`. Detection is by magic bytes (see [CLI reference](../usage-cli.md#input-auto-detection)). Layout details below follow Dolphin's `DiscIO` sources. ## GCZ A compressed GameCube image, invented by GCZTool and supported by Dolphin. The header is 32 bytes; all integers little-endian. ``` 0x00 magic u32 — 0xB10BC001 (bytes 01 C0 0B B1) 0x08 compressed_data_size u64 — size of the data area after the tables 0x10 disc_size u64 — uncompressed ISO size 0x18 block_size u32 — 0x4000 for standard images 0x1C num_blocks u32 0x20 block pointer table: 8 bytes per block { u64 LE offset | bit 63 = uncompressed flag } (offset relative to the start of the compressed data; bit 63 set = block stored uncompressed, clear = deflate-compressed) then the hash table: u32 LE Adler-32 per block ``` Header area total = `0x20 + 12 · num_blocks`; the size fields are validated in `ulong` so a header with the top bit set cannot wrap past the bounds check. Each 0x4000-byte block of ISO data is deflate-compressed independently; blocks with size 0 decode as zeroes. RVZSharp decompresses with the BCL `ZLibStream`. ## CISO / WBI A simple block-compressed format used by old homebrew tools. ``` 0x0000 magic "CISO" 0x0004 block_size u32 LE — 0x8000 for standard images 0x0008 block map 0x7FF8 bytes — 1 byte per block: 1 = block present, 0 = zero-fill 0x8000 present blocks, concatenated in order ``` The decoded length is always `0x7FF8 × block_size` (the map covers 0x7FF8 blocks, regardless of the `file_size` other tools write). Present blocks are served sequentially; absent blocks decode as zeroes. ## WBFS The Wii Backup File System container (a FAT-like structure in the file). Header is 512 bytes; multi-byte integers big-endian. ``` 0x000 magic "WBFS" 0x004 hd_sector_count u32 BE — total sectors of the raw device image 0x008 hd_sector_shift u8 — raw sector size = 1 << shift 0x009 cluster_shift u8 — cluster size = 1 << shift (≥ 0x8000; 2 MiB for standard files) 0x00A disc_table[0] u8 — slot 0 present? 0x00C disc_table… disc offsets in raw sectors 0x100 disc info table (DiscHeaderSize = 256 bytes) … disc tables: the wlba table at `raw_sector_size + 0x100`: u16 BE cluster index per 2 MiB cluster of the disc ``` - `WiiDataSize` is fixed at **9,399,549,952 bytes** (~9.4 GiB), so a WBFS blob always reports that logical size; clusters beyond the file are served as zeroes. - The file length must equal `hd_sector_count × hd_sector_size`. - Cluster indices are **u16 big-endian**; the wlba table holds one entry per 2 MiB cluster of the disc, starting at `hd_sector_size + 0x100`. - RVZSharp opens the disc in slot 0. ## TGC "Tiny GameCube" images: the disc without its empty tail, plus a header. RVZSharp follows Dolphin: the virtual image is the **file minus the TGC header** (`Length = file.Length − tgc_header_size`), and reads are served with three on-the-fly patches. Header fields are u32 big-endian, except the magic — the one field Dolphin reads without byte-swapping (the on-disk bytes are the little-endian form of `0xA2380FAE`): ``` 0x000 magic AE 0F 38 A2 0x008 tgc_header_size (removed from the front when decoding) 0x010 fst_real_offset offset of the FST within the file 0x014 fst_size 0x01C dol_real_offset 0x024 file_area_real_offset 0x034 file_area_virtual_offset ``` Read patches: - **Disc header rewrite** — the DOL and FST offset fields at `0x420` / `0x424` of the virtual image are rewritten to `real_offset − tgc_header_size`, so the virtual disc header points into the decoded image. - **FST relocation** — the FST is replaced by a relocated copy: every file entry (12 bytes, first byte 0 = file rather than directory) has its offset field shifted by `file_area_real_offset − file_area_virtual_offset − tgc_header_size` with **u32-wrapping** arithmetic (Dolphin relies on the wrap cancelling out). Entry count is clamped to the FST's actual size; a missing/unreadable FST is tolerated as empty, like Dolphin. No decompression is involved — reads are served straight from the file with the patches applied, and hostile header values are clamped before any allocation. ## NFS Wii U–era encrypted images (the "EGGS" container). The whole image is AES-128-CBC encrypted with a console key; data is organised in LBA ranges. Header is 0x200 bytes; multi-byte integers big-endian. ``` 0x000 magic "EGGS" 0x010 range count u32 BE (capped at 61) 0x014 LBA ranges: { start u32 BE, num u32 BE } × count, each unit = one 0x8000-byte block ``` Decoding: - The AES key comes from the sibling file `code/htk.bin` (16 bytes) next to the `content` directory — NFS only opens when the file's directory is named `content` (Dolphin's convention). `Blob.Open(stream, nfsKey, …)` bypasses the lookup. - The logical size is `0x200 + total_blocks × 0x8000`; each 0x8000-byte block is decrypted with **IV = 8 zero bytes + 8-byte big-endian block index** (index counted over the whole image), except block 0 which uses the "0x61 hack" (the IV is derived from the header byte at 0x61, matching the real disc data). - Images larger than one file continue in `hif_000000.nfs`, `hif_000001.nfs`, …; each file holds 0xFA00000 bytes (0x1F40 blocks), and the last 0x200 bytes of every full file belong to the next file's header region (the original disc layout has no gap there). - Reads beyond the last range (or beyond the files) decode as zeroes.