Repository navigation
Conversation
added 3 commits
October 13, 2026 20:47
The CD track metadata formats are used by downstream readers with sscanf, and their %s fields write as much as the CHD's metadata string holds - a crafted image overflows the caller's buffer (rtissera#165). Width-limiting the existing *_FORMAT macros is not an option: the same macros are used to write metadata, and in printf a width pads instead of limiting, so writers would emit space-padded TYPE fields. Add CDROM_TRACK_METADATA_SCANF, CDROM_TRACK_METADATA2_SCANF and GDROM_TRACK_METADATA_SCANF alongside them, bounded to CD_METADATA_STRING_SIZE (16; the longest real value, MODE2_FORM_MIX, is 14 characters). tests/metadata_scanf.c checks the bounds with canaries and needs no corpus.
Every CD consumer of libchdr re-derives the same thing from the track metadata: where a track's frames start in the CHD once each track is padded to four frames, whether its pregap is stored, and which hunk and frame an LBA lands in (rtissera#155). chd_cd_open() parses CHT2/CHTR/CHGD metadata into a public TOC and chd_cd_read_sector() reads one sector by LBA - 2048 bytes of user data, the raw sector as a .bin holds it (audio little-endian), or the subcode. Addressing follows MAME's cdrom_file: unstored pregaps and postgaps take LBAs and read as zeros. The handle keeps the last decoded hunk, so sequential reads decode each hunk once. MSF callers use the existing msf_to_lba(). Track metadata is parsed with the bounded *_SCANF formats, and offsets are accumulated in 64 bits and rejected past 32, so crafted metadata yields CHDERR_INVALID_METADATA rather than wrapped offsets. tests/cd_toc.c reads every LBA of a generated three-track CD (stored pregap, unstored pregap, odd track lengths) and compares it with the BIN it was built from.
CHDv5 track metadata keeps one pregap per track and nothing else, so INDEX 02+, CATALOG and ISRC are lost on conversion - and some Saturn and Dreamcast titles depend on them (rtissera#164). The thread there settled on keeping that data inside the CHD rather than in a sidecar file. A CD CHD may now carry its cue sheet as CUES metadata, written with chdman addmeta before compressing. chd_cd_open() reads it: CATALOG into the TOC, ISRC per track, and every INDEX, stored relative to its track's INDEX 01 so both single-BIN and Redump one-BIN-per-track cues land on the CHD's own track starts. chd_cd_get_index() returns any INDEX by track, falling back to INDEX 00/01 from the track metadata. A cue whose tracks do not match the CHD's, or that splits one track's indices across FILEs, is ignored as a whole; other readers ignore the tag entirely. The tag name is a proposal - nothing in MAME uses CUES. tests/cd_toc.c checks a generated CHD with an embedded cue, and one with a mismatched cue that must be ignored.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses #164. Stacked on #200. Only the last commit is new.
CHDv5 track metadata keeps one pregap per track, so INDEX 02+, CATALOG and ISRC are lost on conversion. The #164 thread settled on keeping that data inside the CHD rather than in a sidecar file. This PR does that with the original cue sheet stored as plain-text metadata:
chdman createcd -c none -i game.cue -o tmp.chd chdman addmeta -i tmp.chd --tag CUES --valuefile game.cue # addmeta can't write compressed CHDs chdman copy -i tmp.chd -o game.chdThe result is still a normal CHDv5. Readers that don't know the tag ignore it, and it doesn't need a custom chdman.
What libchdr does with it
chd_cd_open()readsCDROM_CUESHEET_METADATA_TAGif present. It fillstoc->catalog,tracks[n].isrc, and an internal index list, allocated only when a cue is present.chd_cd_get_index(cd, track, index, &lba)returns any INDEX listed in the cue. Without a cue it falls back to INDEX 01 and (for tracks with a pregap) INDEX 00 from the track metadata.FILEis never resolved.Open question
CUESis my suggestion for the tag name. Nothing in MAME uses it, but it's your call, and happy to rename it.Tests
generate.shaddscuesheet.chd(CATALOG, ISRC, INDEX 02) andcuesheet_bad.chd(a cue listing 2 tracks for a 3-track disc).cd-tocchecks that the first comes through and the second is ignored. I also checked a Redump-style multi-FILE cue with CRLF line endings by hand, and its indices resolved to the right LBAs.Tested on Ubuntu 24.04 with chdman 0.264 (GCC, ASan/UBSan) and Windows (MinGW).