Disc hashing (HashDisc.cs + CdReader.cs — port of hash_disc.c + cdreader.c)¶
Disc hashing reads files inside an optical disc image: track selection,
sector math, ISO9660 lookups, and console-specific boot-file rules. The
default CD reader (CdReader.cs) provides the .cue/.gdi/.bin parsing
and the track abstraction.
Track selection¶
The engine opens tracks by logical selection, mirroring RC_HASH_CDTRACK_*:
| Selection | Meaning |
|---|---|
FIRST_DATA |
first track whose mode is MODE* |
LAST |
last track in the table |
LARGEST |
largest data track (by file size) |
FIRST_OF_SECOND_SESSION |
first track of session 2 (Jaguar CD) |
Sector math (the part most likely to surprise)¶
- MSF ↔ LBA: the C computes
msf_to_lba = (m*60+s)*75 + f − 150(LBA 0 = MSF00:02:00). Raw 2352-byte images must carry real MSF in the sector headers — a synthetic image whose sector 0 says00:00:00yieldstrack_first_sector = −150, which breaks absolute-LBA reads. - Cue
INDEX 01 00:00:00parses to sector offset 0 (the cue parser does not subtract 150); real-world cues use00:02:00. - Sector sizes:
MODE1/2048(2048, header 0),MODE2/2336(2336, header 8),MODE1/2352(2352, header 16),MODE2/2352(2352, header 24 — includes the 8-byte XA subheader),AUDIO(2352, header 0, raw stream). - Sector-size probe (
cdreader_determine_sector_size): reads 32 bytes at(16 + pregap) · 2352looking for the 12-byte sync pattern, thenCD001at +25 (XA) / +17 / +1, falling back to file-size heuristics.
ISO9660 mini-parser¶
HashDisc.CdFindFileSector is the port of rc_cd_find_file_sector:
- reads the PVD at LBA 16 (256 bytes);
- root directory extent + size from the PVD record;
- walks directory records (name, version
;1handling, case-insensitive compare), recursing through subdirectories; - returns the file's sector and size for
rc_hash_cd_file.
The walker is deliberately tolerant (it does not require spec-perfect
directory records) — this is the default backend. The alternative backend
is FileSystemResolver (VideoGameFileSystemParser), which is spec-strict
and CHD-bound (see the porting guide).
Per-console rules¶
| Console | Algorithm |
|---|---|
| PSX | first data track; read SYSTEM.CNF, parse BOOT=cdrom:\..., MD5 of the boot file with the exe name prefixed; fallback to PSX.EXE |
| PS2 | BOOT2 = cdrom0:\... via ISO9660; MD5 of the ELF (with ELF marker check) |
| PSP | PSP_GAME\PARAM.SFO + PSP_GAME\SYSDIR\EBOOT.BIN (MD5 of both); .pbp = whole-file |
| Saturn / Sega CD | first 512 bytes of sector 0, magic-checked (SEGA SEGASATURN / SEGADISCSYSTEM) |
| PCE-CD | first data track, header check PC Engine CD-ROM SYSTEM |
| PC-FX | largest data track, sector-0 marker check |
| 3DO | OperaFS: LAUNCHME lookup (case-insensitive), volume header included in the hash |
| Jaguar CD | second-session first track; ATARI APPROVED DATA HEADER scan, byteswap variants, homebrew KART-track logic |
| Neo Geo CD | IPL.TXT contents lookup (lowercase variant), hash the listed executables |
| Dreamcast | OperaFS IP.BIN + largest track rules |
| GameCube | 0xC2339F3D magic at 0x1C, then disc-partition reading |
| Wii | disc partition path vs WiiWare TMD/content |
rc_hash_cd_file¶
The shared helper streams a file region (sector + size) into the MD5 across sector boundaries, handling 2352/2048 conversions and the 64 MiB cap — ported 1:1.
12.4.0 changes ported in Part II¶
- 3DO short-read guard: the OperaFS magic is only checked when the
sector read returned ≥ 132 bytes (
rc_hash_3do). - GDI filename bounds: an unterminated quoted filename errors with
Quoted string without closing quote; filenames ≥ 256 bytes error withCannot copy %u byte filename into %u byte buffer(previously an unbounded copy). - Wii/WiiWare malloc-failure checks are N/A in managed code (noted in comments).