mirror of
https://github.com/ARMSX2/ARMSX1.git
synced 2026-08-24 16:53:35 -07:00
A CHD never produced a serial. Ps1DiscId reads SYSTEM.CNF itself, and it cannot see inside a compressed container, so every .chd fell through to Ps1TitleSerials -- a curated filename table of two dozen USA titles behind a USA-only region filter. A CHD library therefore showed placeholder tiles for everything the table did not list, and the new Download cover art action could not help because it fetches by serial. The serial is not only art: RetroAchievements identifies against it, per-game settings key off it and play time accrues under it. A CHD had none of that either. Writing a CHD parser in Kotlin would have been wrong twice over. CHD v5 Huffman- compresses its own hunk map, so it is a few hundred lines of bit-level decoding before the filesystem is even reachable; and a decoder that is slightly wrong returns a PLAUSIBLE WRONG serial rather than failing, which silently attaches one game's art, settings and achievements to another. Relaxing the region filter has the same defect -- a USA serial fetches the wrong regional box art with total confidence. So the disc goes to the reader that already boots it. psx/discid.c walks the ISO9660 root directory to SYSTEM.CNF and parses its BOOT line through the same psx_disc_t vtable the emulated drive reads through, which means the build still contains exactly ONE CHD decoder and there is nothing for a second one to disagree with. Everything psx_disc_open handles comes along for free: .cue, .bin, .iso, .pbp and .chd. Geometry is detected, not assumed. The same filesystem arrives in four shapes and nothing in the container says which: a CHD puts ISO sector 0 at LBA 150 because its LBA space includes the lead-in, a raw rip puts it at 0 -- or a few sectors in, if the dump captured the pregap -- and the user data sits 24, 16 or 0 bytes into the sector depending on MODE2/MODE1/2048. Each candidate is carried through to a serial-shaped BOOT line before being accepted, so a geometry that finds a plausible volume descriptor but no SYSTEM.CNF falls through to the next instead of becoming a wrong answer. The normalisation accepts the same shape Ps1DiscId.kt does, on purpose: the two are routes to one identity and a disagreement would split a game's settings in half. Kotlin keeps the fast path. It still reads .bin/.cue/.iso/.pbp itself and only calls native for containers it cannot open, plus as a last resort when its own walk and byte scan both came up empty -- which can only add serials where there were none. That does mean identification can touch native now; it costs nothing, because Pasx2Application already loads SDL2 + libarmsx on its warm-up thread at process start and MainActivity calls initializeOnce regardless. Every call is guarded, so a process without the library degrades to exactly the previous behaviour and says so in serial_probe.log. Gated by tests/disc_serial.c (make test-disc-serial): the CHD addressing driven through a synthetic vtable that mimics it, MODE1/MODE2/2048 sectors, pregap rips, a track table that disagrees with the filesystem, SYSTEM.CNF 9000 sectors in, fourteen BOOT spellings, and the cases that must yield NOTHING -- PSX.EXE, MAIN.EXE, a PS2 BOOT2 line, a short serial, a missing SYSTEM.CNF, no volume descriptor, a corrupt root extent. libchdr's decompression is deliberately not re-tested; it is what already boots these discs. Pass --image <path> to identify a real image end to end. One consequence worth knowing: a .chd with no serial in its filename keyed its per-game settings off the filename stem and now keys off the real serial, so per-game settings, cheats, custom names, per-game BIOS and the per-game memory card ASSIGNMENT revert to global once for those entries. Save states are untouched (they key on disc path plus fingerprint, not serial) and no memory card file is affected.