diff --git a/scripts/cross_reference.py b/scripts/cross_reference.py index f1b2c941..c3c1f067 100644 --- a/scripts/cross_reference.py +++ b/scripts/cross_reference.py @@ -338,6 +338,8 @@ class FileConsumers: # Under which of its systems each platform declares a file, so a # link can land on that section rather than the page top. self.platform_systems: dict[str, dict[str, set[str]]] = {} + # Where each platform puts the file in its BIOS directory. + self.platform_destinations: dict[str, dict[str, set[str]]] = {} self.emulators: dict[str, set[str]] = {} def sha1_of(self, entry: dict, dest: str = "", owner: str = "") -> str | None: @@ -368,6 +370,13 @@ class FileConsumers: self.platform_systems.setdefault(sha1, {}).setdefault( name, set() ).add(system_id) + where = str(entry.get("destination") or entry["name"]) + # EmuDeck matches by md5 alone: its entries carry a + # system:md5 key, not a place in the folder. + if ":" not in where: + self.platform_destinations.setdefault(sha1, {}).setdefault( + name, set() + ).add(where) def path_of(self, sha1: str) -> str: """The collection path the database records for a file.""" diff --git a/scripts/generate_site.py b/scripts/generate_site.py index 0e588f59..2a850dbb 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -2083,6 +2083,23 @@ def _fetch_url(path: str) -> str: ) +def _platform_links(sha1: str, consumers: FileConsumers) -> list[str]: + """Each platform declaring the file: a link to its system section, and + the destination it gives the file.""" + systems_of = getattr(consumers, "platform_systems", {}).get(sha1, {}) + destinations_of = getattr(consumers, "platform_destinations", {}).get(sha1, {}) + links: list[str] = [] + for platform in sorted(consumers.platforms.get(sha1, ())): + section = sorted(systems_of.get(platform, ())) + fragment = f"#{section[0].replace(' ', '-')}" if section else "" + link = f"[{platform}](../platforms/{platform}.md{fragment})" + destinations = sorted(destinations_of.get(platform, ())) + if destinations: + link += " " + ", ".join(_code(d) for d in destinations) + links.append(link) + return links + + def _system_file_card(f: dict, consumers: FileConsumers) -> list[str]: """One file of a system page: hashes, who reads it, its provenance.""" name = f["name"] @@ -2112,11 +2129,10 @@ def _system_file_card(f: dict, consumers: FileConsumers) -> list[str]: if value and value != "unknown": lines.append(f'- {label}: {_code(value)}') if plats: - plat_badges = " ".join( - f'[{p}](../platforms/{p}.md)' - for p in plats - ) - lines.append(f"- Platforms: {plat_badges}") + # Each link lands on the platform's system section, and says where + # that platform puts the file: from the card, retrodeck sent the + # reader to the top of a 2,000-file page. + lines.append(f"- Platforms: {', '.join(_platform_links(sha1_full, consumers))}") if emus: lines.append(f"- Emulators: {', '.join(_emulator_link(e, '../') for e in emus)}") provenance = f.get("provenance", {}) @@ -2513,15 +2529,8 @@ def _render_emulator_file( f"Collection copy: [{_code(held)}]" f"(../systems/{urllib.parse.quote(page)}.md#sha1-{sha1[:12]})" ) - plats = sorted(consumers.platforms.get(sha1, ())) if sha1 else [] - if plats: - systems_of = consumers.platform_systems.get(sha1, {}) - plat_links = [] - for p in plats: - section = sorted(systems_of.get(p, ())) - # Land on the system section that gives the destination. - fragment = f"#{section[0].replace(' ', '-')}" if section else "" - plat_links.append(f"[{p}](../platforms/{p}.md{fragment})") + plat_links = _platform_links(sha1, consumers) if sha1 else [] + if plat_links: details.append(f"Platforms: {', '.join(plat_links)}") if dest and dest != fname and dest != fpath: diff --git a/tests/test_site_exports.py b/tests/test_site_exports.py index e19d376a..7acf7bf1 100644 --- a/tests/test_site_exports.py +++ b/tests/test_site_exports.py @@ -437,5 +437,26 @@ class AGoneSourceIsSaid(unittest.TestCase): self.assertIn("| Mirror | [https://example.org/mirror/Citron]", page) + +class ASystemCardLandsOnThePlatformsSection(unittest.TestCase): + """From scph5000.bin's card, [retrodeck] opened the top of a 2,000-file + page; the emulator card already landed on #sony-playstation.""" + + def test_section_and_destination(self): + import types + + from generate_site import _platform_links + + consumers = types.SimpleNamespace( + platforms={"s": {"retrodeck"}}, + platform_systems={"s": {"retrodeck": {"sony-playstation"}}}, + platform_destinations={"s": {"retrodeck": {"bios/scph5000.bin"}}}, + ) + self.assertEqual( + _platform_links("s", consumers), + ["[retrodeck](../platforms/retrodeck.md#sony-playstation) `bios/scph5000.bin`"], + ) + + if __name__ == "__main__": unittest.main()