diff --git a/emulators/beetle_saturn.yml b/emulators/beetle_saturn.yml index 23edbf9f..ee2194fd 100644 --- a/emulators/beetle_saturn.yml +++ b/emulators/beetle_saturn.yml @@ -5,7 +5,7 @@ core: mednafen_saturn_libretro source: "https://github.com/libretro/beetle-saturn-libretro" upstream: "https://mednafen.github.io/" profiled_date: "2026-03-24" -source_commit: "b4df47a9f0f30d09eb95b07a4435d0f435a2e95d" +source_commit: "84461434f249c1b5cd10c83ae922feae84e1acf8" core_version: "v0.9.45.1" display_name: "Sega - Saturn (Beetle Saturn)" cores: @@ -36,7 +36,7 @@ files: size: 524288 validation: [size] note: "Saturn BIOS v1.01 (Japan). Used when region is JP or Asia NTSC." - source_ref: "mednafen/ss/ss.cpp:928-929,937-949" + source_ref: "mednafen/ss/ss.c:1761 (region filename), mednafen/ss/ss.c:1784-1899 (open, size check, read, sha256)" - name: "mpr-17933.bin" system: sega-saturn @@ -45,25 +45,25 @@ files: size: 524288 validation: [size] note: "Saturn BIOS (NA/EU). Used for all non-JP/Asia-NTSC regions." - source_ref: "mednafen/ss/ss.cpp:930-931,937-949" + source_ref: "mednafen/ss/ss.c:1763 (region filename), mednafen/ss/ss.c:1784-1899 (open, size check, read, sha256)" - name: "mpr-18811-mx.ic1" system: sega-saturn required: false size: 2097152 note: "KOF95 expansion ROM cart. Loaded when cart type set to KOF95." - source_ref: "mednafen/ss/cart.cpp:157-172, mednafen/ss/cart/rom.cpp:32-43" + source_ref: "mednafen/ss/cart.c:200-215 (firmware path + open), mednafen/ss/cart/rom.c:51 (CART_ROM_Init)" - name: "mpr-19367-mx.ic1" system: sega-saturn required: false size: 2097152 note: "Ultraman expansion ROM cart. Loaded when cart type set to Ultraman." - source_ref: "mednafen/ss/cart.cpp:157-172, mednafen/ss/cart/rom.cpp:32-43" + source_ref: "mednafen/ss/cart.c:200-215 (firmware path + open), mednafen/ss/cart/rom.c:51 (CART_ROM_Init)" - name: "satar4mp.bin" system: sega-saturn required: false size: 262144 note: "Action Replay 4M Plus firmware. Unfinished in source (ar4mp.cpp:22-24), needs CPU UBC emulation. Cart type not exposed in core options." - source_ref: "mednafen/ss/cart.cpp:174-188, mednafen/ss/cart/ar4mp.cpp:120-159" + source_ref: "mednafen/ss/cart.c:218 (CART_AR4MP case), mednafen/ss/cart/ar4mp.c:150 (CART_AR4MP_Init)" diff --git a/scripts/profile_sync.py b/scripts/profile_sync.py index 321228cd..073929ce 100644 --- a/scripts/profile_sync.py +++ b/scripts/profile_sync.py @@ -46,7 +46,8 @@ PIN = "pin" HEAD = "head" _REF_RE = re.compile(r"^(?P[^:]+?)(?::(?P\d+)(?:-(?P\d+))?)?$") -_BARE_RANGE_RE = re.compile(r"^\d+(?:-\d+)?$") +_BARE_RANGE_RE = re.compile(r"^:?(\d+(?:-\d+)?)$") +HEADER_SUFFIXES = (".h", ".hpp", ".hh", ".hxx", ".inc", ".inl") _SPLIT_RE = re.compile(r"[,;]") _ANNOTATION_RE = re.compile(r"\s*\([^)]*\)?") _LOCATED_RE = re.compile(r"^[^\s:]+:\d+(?:-\d+)?$") @@ -128,8 +129,9 @@ def split_source_ref(ref: str) -> list[RefPart]: chunk = _trim_prose(_ANNOTATION_RE.sub("", raw).strip()) if not chunk: continue - if _BARE_RANGE_RE.match(chunk) and parts: - start, _, end = chunk.partition("-") + bare = _BARE_RANGE_RE.match(chunk) + if bare and parts: + start, _, end = bare.group(1).partition("-") parts.append( RefPart(parts[-1].path, int(start), int(end or start), raw) ) @@ -370,13 +372,25 @@ def resolve_rename( return None, matches +def _is_header(path: str) -> bool: + return path.lower().endswith(HEADER_SUFFIXES) + + def _narrow(matches: list[str], path: str) -> list[str]: - """Prefer candidates sitting in the directory the file came from.""" + """Prefer candidates of the same kind, then of the same directory. + + A stem search on `ss.cpp` matches both `ss.c` and `ss.h`, and only one of + them is the implementation the ref meant. + """ if len(matches) < 2: return matches + kind = [p for p in matches if _is_header(p) == _is_header(path)] + if len(kind) == 1: + return kind + pool = kind or matches directory = posixpath.dirname(path) - same = [p for p in matches if posixpath.dirname(p) == directory] - return same if len(same) == 1 else matches + same = [p for p in pool if posixpath.dirname(p) == directory] + return same if len(same) == 1 else pool def locate_by_declared_value( diff --git a/tests/test_profile_sync.py b/tests/test_profile_sync.py index e13302a7..fab0b484 100644 --- a/tests/test_profile_sync.py +++ b/tests/test_profile_sync.py @@ -167,6 +167,23 @@ class TestSplitSourceRef(unittest.TestCase): parts = split_source_ref("munt ROMInfo.cpp") self.assertEqual(parts[0].path, "munt ROMInfo.cpp") + def test_leading_colon_continues_the_previous_file(self): + parts = split_source_ref("sms_mapper.c:12, :61, :162-170") + self.assertEqual( + [(p.path, p.start, p.end) for p in parts], + [("sms_mapper.c", 12, 12), ("sms_mapper.c", 61, 61), + ("sms_mapper.c", 162, 170)], + ) + + def test_leading_colon_with_annotations(self): + parts = split_source_ref( + "genesis.rs:41-51 (read), :256-262 (bios_path_for_region)" + ) + self.assertEqual( + [(p.path, p.start, p.end) for p in parts], + [("genesis.rs", 41, 51), ("genesis.rs", 256, 262)], + ) + def test_leading_bare_range_stays_a_path(self): parts = split_source_ref("273-285") self.assertEqual(parts[0].path, "273-285") @@ -531,10 +548,22 @@ class TestResolveRename(unittest.TestCase): ) self.assertEqual(found, "libretro.c") - def test_same_directory_tie_break_needs_a_single_winner(self): - found, candidates = resolve_rename( + def test_an_implementation_wins_over_a_header(self): + found, _ = resolve_rename( CompareResult([], True), "src/a.cpp", ["src/a.c", "src/a.h"] ) + self.assertEqual(found, "src/a.c") + + def test_a_header_ref_picks_the_header(self): + found, _ = resolve_rename( + CompareResult([], True), "src/a.hpp", ["src/a.c", "src/a.h"] + ) + self.assertEqual(found, "src/a.h") + + def test_two_implementations_stay_ambiguous(self): + found, candidates = resolve_rename( + CompareResult([], True), "src/a.cpp", ["one/a.c", "two/a.cc"] + ) self.assertIsNone(found) self.assertEqual(len(candidates), 2)