refactor: require a hash before relocating a ref

This commit is contained in:
Abdessamad Derraz committed 2026-08-11 00:37:30 +02:00
1 parent 5330ed1950
commit 902bb4e01e
6 files changed
+40 -21

No files matched your search

+2 -2
View File
@@ -101,7 +101,7 @@ files:
size: 273 size: 273
validation: [size] validation: [size]
note: "console region and serial number; loaded on a size check alone" note: "console region and serial number; loaded on a size check alone"
source_ref: "src/core/hle/service/cfg/cfg.cpp:689-709, src/core/hle/service/cfg/cfg.h:180-186" source_ref: "src/core/hle/service/cfg/cfg.cpp:608-610, 689-709, src/core/hle/service/cfg/cfg.h:180-186"
- name: LocalFriendCodeSeed_B - name: LocalFriendCodeSeed_B
path: nand/rw/sys/LocalFriendCodeSeed_B path: nand/rw/sys/LocalFriendCodeSeed_B
@@ -110,4 +110,4 @@ files:
size: 272 size: 272
validation: [size] validation: [size]
note: "friend code generation seed; loaded on a size check alone" note: "friend code generation seed; loaded on a size check alone"
source_ref: "src/core/hle/service/cfg/cfg.cpp:711-732, src/core/hle/service/cfg/cfg.h:188-193" source_ref: "src/core/hle/service/cfg/cfg.cpp:604-606, 711-732, src/core/hle/service/cfg/cfg.h:188-193"
+2 -2
View File
@@ -111,7 +111,7 @@ files:
size: 273 size: 273
validation: [size] validation: [size]
note: "console region and serial number; loaded on a size check alone, and paired with LocalFriendCodeSeed_B" note: "console region and serial number; loaded on a size check alone, and paired with LocalFriendCodeSeed_B"
source_ref: "src/core/hle/service/cfg/cfg.cpp:689-709, src/core/hle/service/cfg/cfg.h:180-186" source_ref: "src/core/hle/service/cfg/cfg.cpp:608-610, 689-709, src/core/hle/service/cfg/cfg.h:180-186"
- name: LocalFriendCodeSeed_B - name: LocalFriendCodeSeed_B
path: nand/rw/sys/LocalFriendCodeSeed_B path: nand/rw/sys/LocalFriendCodeSeed_B
@@ -120,4 +120,4 @@ files:
size: 272 size: 272
validation: [size] validation: [size]
note: "friend code generation seed; loaded on a size check alone, and paired with SecureInfo_A" note: "friend code generation seed; loaded on a size check alone, and paired with SecureInfo_A"
source_ref: "src/core/hle/service/cfg/cfg.cpp:711-732, src/core/hle/service/cfg/cfg.h:188-193" source_ref: "src/core/hle/service/cfg/cfg.cpp:604-606, 711-732, src/core/hle/service/cfg/cfg.h:188-193"
+7 -7
View File
@@ -240,40 +240,40 @@ files:
# Level tileset shapes (loaded per-level from .lvl data) # Level tileset shapes (loaded per-level from .lvl data)
# The level file specifies a character via fgetc() at tyrian2.c:3039, # The level file specifies a character via fgetc() at tyrian2.c:3039,
# which maps to shapes{char}.dat at tyrian2.c:3079. # which maps to shapes{char}.dat at tyrian2.c:3079.
# Only 5 characters are used by actual levels: ), W, X, Y, Z. # Episode 4 uses Z, W, X and } (lvlmast.c:128-131).
# Note: shapeFile[34] in lvlmast.c is a DIFFERENT mechanism — # Note: shapeFile[34] in lvlmast.c is a DIFFERENT mechanism —
# it maps indices to characters for newsh?.shp (enemy sprites), # it maps indices to characters for newsh?.shp (enemy sprites),
# NOT for shapes?.dat (level tilesets). # NOT for shapes?.dat (level tilesets).
# ----------------------------------------------------------- # -----------------------------------------------------------
- name: "shapes).dat" - name: "shapes}.dat"
category: game_data category: game_data
system: tyrian system: tyrian
required: true required: true
source_ref: "src/tyrian2.c:3039-3079" source_ref: "src/tyrian2.c:3039, 3079, src/lvlmast.c:128-131"
- name: "shapesw.dat" - name: "shapesw.dat"
category: game_data category: game_data
system: tyrian system: tyrian
required: true required: true
source_ref: "src/tyrian2.c:3039-3079" source_ref: "src/tyrian2.c:3039, 3079, src/lvlmast.c:128-131"
- name: "shapesx.dat" - name: "shapesx.dat"
category: game_data category: game_data
system: tyrian system: tyrian
required: true required: true
source_ref: "src/tyrian2.c:3039-3079" source_ref: "src/tyrian2.c:3039, 3079, src/lvlmast.c:128-131"
- name: "shapesy.dat" - name: "shapesy.dat"
category: game_data category: game_data
system: tyrian system: tyrian
required: true required: true
source_ref: "src/tyrian2.c:3039-3079" source_ref: "src/tyrian2.c:3039, 3079, src/lvlmast.c:128-131"
- name: "shapesz.dat" - name: "shapesz.dat"
category: game_data category: game_data
system: tyrian system: tyrian
required: true required: true
source_ref: "src/tyrian2.c:3039-3079" source_ref: "src/tyrian2.c:3039, 3079, src/lvlmast.c:128-131"
# ----------------------------------------------------------- # -----------------------------------------------------------
# Compressed shape files (newshX.shp, loaded per-level) # Compressed shape files (newshX.shp, loaded per-level)
+3 -3
View File
@@ -42,7 +42,7 @@ files:
system: cdi system: cdi
required: true required: true
note: "Forced system. Main CPU BIOS (3 region variants via MAME BIOS switch) + servo/slave MCU dumps." note: "Forced system. Main CPU BIOS (3 region variants via MAME BIOS switch) + servo/slave MCU dumps."
source_ref: "src/mame/drivers/cdi.cpp:428-443" source_ref: "src/mame/drivers/cdi.cpp:829-443"
contents: contents:
- name: cdi200.rom - name: cdi200.rom
description: Magnavox CD-i 200 BIOS (default, US) description: Magnavox CD-i 200 BIOS (default, US)
@@ -100,7 +100,7 @@ files:
system: cdi system: cdi
required: false required: false
note: "CD-i BIOS for Quizard arcade. MACHINE_IS_BIOS_ROOT. Subset of cdimono1 + slave MCU." note: "CD-i BIOS for Quizard arcade. MACHINE_IS_BIOS_ROOT. Subset of cdimono1 + slave MCU."
source_ref: "src/mame/drivers/cdi.cpp:630-640" source_ref: "src/mame/drivers/cdi.cpp:836-640"
contents: contents:
- name: cdi200.rom - name: cdi200.rom
description: Magnavox CD-i 200 BIOS (same as cdimono1) description: Magnavox CD-i 200 BIOS (same as cdimono1)
@@ -124,7 +124,7 @@ files:
system: cdi system: cdi
required: false required: false
note: "CD-i 910-17P Mini-MMC (PAL). MACHINE_NOT_WORKING. Not in .info firmware list." note: "CD-i 910-17P Mini-MMC (PAL). MACHINE_NOT_WORKING. Not in .info firmware list."
source_ref: "src/mame/drivers/cdi.cpp:319-335" source_ref: "src/mame/drivers/cdi.cpp:831-335"
contents: contents:
- name: "philips__cd-i_2.1__mb834200b-15__26b_aa__9224_z01.tc574200.7211" - name: "philips__cd-i_2.1__mb834200b-15__26b_aa__9224_z01.tc574200.7211"
description: CD-i 910-17P Mini-MMC BIOS (default) description: CD-i 910-17P Mini-MMC BIOS (default)
+13 -5
View File
@@ -746,7 +746,7 @@ def branch_in_url(url: str) -> str | None:
SELF_CHECK_CONTEXT = 2 SELF_CHECK_CONTEXT = 2
def verify_at_pin(part: RefPart, pin_lines, tokens) -> PartResult: def verify_at_pin(part: RefPart, pin_lines, tokens, hash_tokens=()) -> PartResult:
"""Check a ref against its own revision instead of against HEAD. """Check a ref against its own revision instead of against HEAD.
A profile pinned to a superseded tag documents a program HEAD no longer A profile pinned to a superseded tag documents a program HEAD no longer
@@ -780,7 +780,12 @@ def verify_at_pin(part: RefPart, pin_lines, tokens) -> PartResult:
return PartResult(part, "ANCHORED", None, None, None, []) return PartResult(part, "ANCHORED", None, None, None, [])
# A ref that cites loading logic never spells the value out, so its absence # A ref that cites loading logic never spells the value out, so its absence
# here proves nothing. Only finding the value somewhere else in the file # here proves nothing. Only finding the value somewhere else in the file
# shows the ref points at the wrong place. # shows the ref points at the wrong place -- and only a hash carries that
# weight. A filename fragment matches by coincidence: `tyrian2` hits every
# line naming `tyrian2.c`, and a name built at runtime from a format
# string appears nowhere at all.
if not hash_tokens:
return PartResult(part, "ANCHORED", None, None, None, [])
elsewhere = sorted( elsewhere = sorted(
{ {
index index
@@ -877,6 +882,7 @@ def build_report(
entry.get("name", "") + (f" [{label}]" if label else ""), entry.get("name", "") + (f" [{label}]" if label else ""),
value, value,
_anchor_tokens(entry), _anchor_tokens(entry),
entry_hashes(entry),
) )
for entry in (profile.get("files") or []) for entry in (profile.get("files") or [])
if isinstance(entry, dict) and entry.get("source_ref") if isinstance(entry, dict) and entry.get("source_ref")
@@ -1028,10 +1034,12 @@ def build_report(
if self_check: if self_check:
staged = [ staged = [
(entry_name, ref, reconcile_self_check([ (entry_name, ref, reconcile_self_check([
verify_at_pin(part, fetch(PIN, part.path, part.start), tokens) verify_at_pin(
part, fetch(PIN, part.path, part.start), tokens, hashes
)
for part in split_source_ref(ref) for part in split_source_ref(ref)
])) ]))
for entry_name, ref, tokens in refs for entry_name, ref, tokens, hashes in refs
] ]
else: else:
staged = [ staged = [
@@ -1039,7 +1047,7 @@ def build_report(
anchor_part(part, fetch, rename_getter, describe, tokens) anchor_part(part, fetch, rename_getter, describe, tokens)
for part in split_source_ref(ref) for part in split_source_ref(ref)
]) ])
for entry_name, ref, tokens in refs for entry_name, ref, tokens, hashes in refs
] ]
shifts = dominant_shifts([p for _, _, parts in staged for p in parts]) shifts = dominant_shifts([p for _, _, parts in staged for p in parts])
+13 -2
View File
@@ -337,14 +337,18 @@ class TestVerifyAtPin(unittest.TestCase):
def test_declared_value_carried_by_another_line(self): def test_declared_value_carried_by_another_line(self):
lines = ["load()", "", "", "", "", "", 'rom("cafebabe")'] lines = ["load()", "", "", "", "", "", 'rom("cafebabe")']
part = RefPart("a.c", 1, 1, "a.c:1") part = RefPart("a.c", 1, 1, "a.c:1")
result = profile_sync.verify_at_pin(part, lines, ["cafebabe"]) result = profile_sync.verify_at_pin(
part, lines, ["cafebabe"], ["cafebabe"]
)
self.assertEqual(result.status, "MOVED") self.assertEqual(result.status, "MOVED")
self.assertEqual(result.start, 7) self.assertEqual(result.start, 7)
def test_declared_value_on_several_lines_is_ambiguous(self): def test_declared_value_on_several_lines_is_ambiguous(self):
lines = ["load()", "", "", "", 'a("cafebabe")', "", 'b("cafebabe")'] lines = ["load()", "", "", "", 'a("cafebabe")', "", 'b("cafebabe")']
part = RefPart("a.c", 1, 1, "a.c:1") part = RefPart("a.c", 1, 1, "a.c:1")
result = profile_sync.verify_at_pin(part, lines, ["cafebabe"]) result = profile_sync.verify_at_pin(
part, lines, ["cafebabe"], ["cafebabe"]
)
self.assertEqual(result.status, "AMBIGUOUS") self.assertEqual(result.status, "AMBIGUOUS")
def test_one_anchored_part_settles_the_others(self): def test_one_anchored_part_settles_the_others(self):
@@ -386,6 +390,13 @@ class TestVerifyAtPin(unittest.TestCase):
result = profile_sync.verify_at_pin(part, None, ["cafebabe"]) result = profile_sync.verify_at_pin(part, None, ["cafebabe"])
self.assertEqual(result.status, "EXTERNAL") self.assertEqual(result.status, "EXTERNAL")
def test_a_name_fragment_elsewhere_is_not_evidence(self):
# Only a hash is unique enough to prove a ref points elsewhere.
lines = ["load()", "", "", "", "", "", "tyrian2.c line"]
part = RefPart("a.c", 1, 1, "a.c:1")
result = profile_sync.verify_at_pin(part, lines, ["tyrian2"], [])
self.assertEqual(result.status, "ANCHORED")
def test_missing_file(self): def test_missing_file(self):
part = RefPart("a.c", 1, 1, "a.c:1") part = RefPart("a.c", 1, 1, "a.c:1")
self.assertEqual( self.assertEqual(