Skip to content
drpetersonfernandes edited this page Aug 15, 2026 · 1 revision

Legacy formats

All legacy formats are decoded to the canonical ISO view through IBlobReader. Detection is by magic bytes (see CLI reference). 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.

Clone this wiki locally