[ROMM-2552] Rom hashes should only include top-level nested files

This commit is contained in:
Georges-Antoine Assi
2025-10-18 18:05:57 -04:00
parent f83caeefe1
commit 24a5acce5d
4 changed files with 88 additions and 7 deletions

View File

@@ -330,16 +330,29 @@ class FSRomsHandler(FSHandler):
):
continue
# Check if this is a top-level file (not in a subdirectory)
is_top_level = f_path.samefile(Path(abs_fs_path, rom.fs_name))
if hashable_platform:
try:
crc_c, rom_crc_c, md5_h, rom_md5_h, sha1_h, rom_sha1_h = (
self._calculate_rom_hashes(
Path(f_path, file_name),
rom_crc_c,
rom_md5_h,
rom_sha1_h,
if is_top_level:
# Include this file in the main ROM hash calculation
crc_c, rom_crc_c, md5_h, rom_md5_h, sha1_h, rom_sha1_h = (
self._calculate_rom_hashes(
Path(f_path, file_name),
rom_crc_c,
rom_md5_h,
rom_sha1_h,
)
)
else:
# Calculate individual file hash only
crc_c, _, md5_h, _, sha1_h, _ = self._calculate_rom_hashes(
Path(f_path, file_name),
0,
hashlib.md5(usedforsecurity=False),
hashlib.sha1(usedforsecurity=False),
)
)
except zlib.error:
crc_c = 0
md5_h = hashlib.md5(usedforsecurity=False)

View File

@@ -0,0 +1 @@
00000000

View File

@@ -46,6 +46,28 @@ class TestFSRomsHandler:
full_path="n64/roms/Paper Mario (USA).z64",
)
@pytest.fixture
def rom_single_nested(self, platform: Platform):
return Rom(
id=3,
fs_name="Sonic (EU) [T]",
fs_path="n64/roms",
platform=platform,
full_path="n64/roms/Sonic (EU) [T]",
files=[
RomFile(
id=1,
file_name="Sonic (EU) [T].n64",
file_path="n64/roms",
),
RomFile(
id=2,
file_name="Sonic (EU) [T-En].z64",
file_path="n64/roms/translation",
),
],
)
@pytest.fixture
def rom_multi(self, platform: Platform):
return Rom(
@@ -555,3 +577,47 @@ class TestFSRomsHandler:
async with await handler.stream_file("psx/roms/PaRappa the Rapper.zip") as f:
content = await f.read()
assert len(content) > 0
async def test_top_level_files_only_in_main_hash(
self, handler: FSRomsHandler, rom_single_nested
):
"""Test that only top-level files contribute to main ROM hash calculation"""
rom_files, rom_crc, rom_md5, rom_sha1, rom_ra = await handler.get_rom_files(
rom_single_nested
)
# Verify we have multiple files (base game + translation)
assert len(rom_files) == 2
base_game_rom_file = None
translation_rom_file = None
for rom_file in rom_files:
if rom_file.file_name == "Sonic (EU) [T].n64":
base_game_rom_file = rom_file
elif rom_file.file_name == "Sonic (EU) [T-En].z64":
translation_rom_file = rom_file
assert base_game_rom_file is not None, "Base game file not found"
assert translation_rom_file is not None, "Translation file not found"
# Verify file categories
assert base_game_rom_file.category is None
assert translation_rom_file.category == RomFileCategory.TRANSLATION
# The main ROM hash should be different from the translation file hash
# (this verifies that the translation is not included in the main hash)
assert (
rom_md5 == base_game_rom_file.md5_hash
), "Main ROM hash should include base game file"
assert (
rom_md5 != translation_rom_file.md5_hash
), "Main ROM hash should not include translation file"
assert (
rom_sha1 == base_game_rom_file.sha1_hash
), "Main ROM hash should include base game file"
assert (
rom_sha1 != translation_rom_file.sha1_hash
), "Main ROM hash should not include translation file"