diff --git a/scripts/exporter/bizhawk_exporter.py b/scripts/exporter/bizhawk_exporter.py index 5b369dcc..bff18a75 100644 --- a/scripts/exporter/bizhawk_exporter.py +++ b/scripts/exporter/bizhawk_exporter.py @@ -61,10 +61,14 @@ class Exporter(BaseExporter): return True def states(self, fe: NativeFile, field_name: str) -> bool: - """A name that resolves to several SHA1s is left alone, so its - correction is not a change the database receives.""" - touchable = getattr(self, "_touchable", None) - if field_name == "sha1" and touchable is not None and fe.name.casefold() not in touchable: + """Only a call the render rewrote receives the change. + + A name that resolves to several SHA1s is left alone, and the entries + BizHawk declares without a hash are FirmwareAndOption(SHA1Checksum + .Dummy, ...) calls, which hold no hash to replace. + """ + written = getattr(self, "_written", None) + if field_name in ("sha1", "size") and written is not None and fe.name.casefold() not in written: return False return super().states(fe, field_name) @@ -114,7 +118,7 @@ class Exporter(BaseExporter): ) index = self._unambiguous(systems) - self._touchable = set(index) + self._written: set[str] = set() def commented_out(text: str, position: int) -> bool: """Whether the call sits on a line the compiler never sees. @@ -137,6 +141,7 @@ class Exporter(BaseExporter): groups = list(match.groups()) groups[1] = sha1 groups[3] = str(size) + self._written.add(name.casefold()) return "".join(groups) patched = _FILE_CALL.sub(lambda m: rewrite(m, 6), source) diff --git a/tests/test_exporters.py b/tests/test_exporters.py index 5279cf65..cacd306d 100644 --- a/tests/test_exporters.py +++ b/tests/test_exporters.py @@ -678,6 +678,45 @@ class RommExport(unittest.TestCase): self.assertTrue(all(k.startswith("psx:") for k in data)) +class BizhawkExport(unittest.TestCase): + SHA1 = "a" * 40 + + def _export(self, name, call): + scraped = {"systems": {"nintendo-ds": {"native_id": "NDS", "files": [{ + "name": name, "destination": name, "native_system": "NDS", + }]}}} + truth = {"systems": {"nintendo-ds": {"files": [{ + "name": name, "path": name, "sha1": self.SHA1, + "required": True, "_cores": ["melonds"], + }]}}} + systems, report = build_native_model(truth, scraped) + (entry,) = systems["NDS"].files + self.assertIn("sha1", entry.filled) + exporter = discover_exporters()["bizhawk"]() + source = f"class FirmwareDatabase {{\n {call}\n}}\n" + produced = exporter.render( + systems, report, {"FirmwareDatabase.cs": source}, None + )["FirmwareDatabase.cs"] + return exporter, entry, source, produced + + def test_a_fill_a_dummy_call_cannot_hold_is_not_counted(self): + """BizHawk states DSi_Firmware.bin with SHA1Checksum.Dummy.""" + exporter, entry, source, produced = self._export( + "DSi_Firmware.bin", + 'FirmwareAndOption(SHA1Checksum.Dummy, 131072, "NDS", "firmwarei", ' + '"DSi_Firmware.bin", "DSi Firmware");', + ) + self.assertEqual(produced, source) + self.assertFalse(exporter.states(entry, "sha1")) + + def test_a_fill_the_call_holds_is_counted(self): + exporter, entry, _, produced = self._export( + "bios.bin", f'File("{"0" * 40}", 4096, "bios.bin", "BIOS");' + ) + self.assertIn(self.SHA1.upper(), produced) + self.assertTrue(exporter.states(entry, "sha1")) + + class RetrodeckExport(unittest.TestCase): A, B, C = "a" * 32, "b" * 32, "c" * 32