From b5feaccc2a62766dc0d4a8ba31bc701d00345ba8 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Tue, 6 Oct 2026 04:29:16 +0200 Subject: [PATCH] fix: carry pcsx2 companions under the names it opens --- emulators/pcsx2-legacy.yml | 24 ++++++++++++-- emulators/pcsx2.yml | 34 +++++++++++++------- scripts/packextras.py | 59 +++++++++++++++++++++++++++++++++++ tests/test_bios_companions.py | 56 +++++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 14 deletions(-) create mode 100644 tests/test_bios_companions.py diff --git a/emulators/pcsx2-legacy.yml b/emulators/pcsx2-legacy.yml index 168f08ef..5c2b3ec9 100644 --- a/emulators/pcsx2-legacy.yml +++ b/emulators/pcsx2-legacy.yml @@ -78,7 +78,7 @@ files: note: "Any file in the BIOS folder whose romdir holds RESET and ROMVER is accepted, whatever its name or size. Naming convention ps2-VVVVr-YYYYMMDD.bin carries version, region and date." source_ref: "pcsx2/ps2/BiosTools.cpp:261-323, pcsx2/ps2/BiosTools.cpp:74-167" - - name: rom1.bin + - name: ps2-0230a-20080220.rom1 system: sony-playstation-2 required: false max_size: 262144 @@ -86,7 +86,7 @@ files: note: "Tried as {bios}.rom1 then {biosbase}.rom1. Read into the 256 KB ROM1 region and truncated to it. Logged and skipped when absent." source_ref: "pcsx2/ps2/BiosTools.cpp:190-229, pcsx2/ps2/BiosTools.cpp:298, pcsx2/MemoryTypes.h:22" - - name: ROM2.BIN + - name: ps2-0230a-20080220.rom2 system: sony-playstation-2 required: false max_size: 524288 @@ -94,7 +94,7 @@ files: note: "Tried as {bios}.rom2 then {biosbase}.rom2. Read into the 512 KB ROM2 region and truncated to it. Present on Chinese region consoles." source_ref: "pcsx2/ps2/BiosTools.cpp:190-229, pcsx2/ps2/BiosTools.cpp:299, pcsx2/MemoryTypes.h:23" - - name: EROM.BIN + - name: ps2-0230a-20080220.erom system: sony-playstation-2 required: false max_size: 1835008 @@ -102,6 +102,24 @@ files: note: "Tried as {bios}.erom then {biosbase}.erom. Read into the 1.75 MB EROM region and truncated to it." source_ref: "pcsx2/ps2/BiosTools.cpp:190-229, pcsx2/ps2/BiosTools.cpp:300, pcsx2/MemoryTypes.h:24" + - name: ps2-0230a-20080220.nvm + system: sony-playstation-2 + required: false + hle_fallback: true + min_size: 1024 + description: "Console NVRAM" + note: "Read at the BIOS path with the extension set to nvm. A substitute is written when the file is shorter than 1024 bytes." + source_ref: "pcsx2/CDVD/CDVD.cpp:138-149" + + - name: ps2-0230a-20080220.mec + system: sony-playstation-2 + required: false + hle_fallback: true + size: 4 + description: "Mechacon version" + note: "Read at the BIOS path with the extension set to mec. A 4-byte substitute (03 06 02 00) is written when the file is shorter." + source_ref: "pcsx2/CDVD/CDVD.cpp:93-122" + - name: eeprom.dat system: sony-playstation-2 required: false diff --git a/emulators/pcsx2.yml b/emulators/pcsx2.yml index 5d9eae3d..6fa72946 100644 --- a/emulators/pcsx2.yml +++ b/emulators/pcsx2.yml @@ -15,7 +15,7 @@ systems: [sony-playstation-2] notes: | Filename-agnostic BIOS detection. Scans bios/ for any file between 4-8 MB with valid romdir structure (RESET + ROMVER entries). No hash validation. - Companion files (.rom1, .rom2, .nvm, .mec) derive paths from selected BIOS. + Companion files (.rom1, .rom2, .nvm, .mec) derive their names from the selected BIOS. ROM1 (DVD player) and ROM2 (Chinese extension) silently skipped if missing. NVM and MEC auto-created with defaults if missing. @@ -28,23 +28,35 @@ files: source_ref: "pcsx2/ps2/BiosTools.cpp:258-362" note: "Accepts any file 4-8 MB with valid romdir (RESET + ROMVER). Naming convention ps2-VVVVr-YYYYMMDD.bin (version, region, date)." - - name: rom1.bin + - name: ps2-0230a-20080220.rom1 required: false max_size: 4194304 - source_ref: "pcsx2/ps2/BiosTools.cpp:214-241,366" - note: "DVD player ROM. Tries {biospath}.rom1 then {biosbase}.rom1. Silently skipped if missing." + description: "DVD player ROM" + note: "Tried as {bios}.rom1 then {biosbase}.rom1, opened case-insensitively. Copied to the ROM1 region. Logged and skipped when absent." + source_ref: "pcsx2/ps2/BiosTools.cpp:214-241, pcsx2/ps2/BiosTools.cpp:366" - - name: ROM2.BIN + - name: ps2-0230a-20080220.rom2 required: false max_size: 4194304 - source_ref: "pcsx2/ps2/BiosTools.cpp:214-241,367" - note: "Chinese ROM extension. Same naming convention as rom1. Only present on Chinese region consoles." + description: "Chinese ROM extension" + note: "Same two-step naming as rom1. Only Chinese region consoles carry one." + source_ref: "pcsx2/ps2/BiosTools.cpp:214-241, pcsx2/ps2/BiosTools.cpp:367" - - name: EROM.BIN + - name: ps2-0230a-20080220.nvm required: false - source_ref: "pcsx2/ps2/BiosTools.cpp" - note: "Extended ROM. Present in some BIOS dumps but not loaded by PCSX2 code via LoadExtraRom." - path: null + hle_fallback: true + size: 1024 + description: "Console NVRAM" + note: "Read at the BIOS path with the extension replaced by nvm, opened case-insensitively. Built in memory from region defaults when absent or blank." + source_ref: "pcsx2/CDVD/CDVD.cpp:160-188" + + - name: ps2-0230a-20080220.mec + required: false + hle_fallback: true + size: 4 + description: "Mechacon version" + note: "Read at the BIOS path with the extension replaced by mec. Defaults to 0x00020603 and is written back when it cannot be read." + source_ref: "pcsx2/CDVD/CDVD.cpp:190-204" - name: eeprom.dat required: false diff --git a/scripts/packextras.py b/scripts/packextras.py index 7b284434..9e129bc6 100644 --- a/scripts/packextras.py +++ b/scripts/packextras.py @@ -266,9 +266,68 @@ def _agnostic_scan_extras( "agnostic_scan": True, } ) + for companion_sha1, companion in _companions( + path, scan_name, _companion_extensions(profile, fname), files_db + ): + companion_dest = f"{folder}/{companion['name']}" if folder else companion["name"] + companion_full = ( + f"{extras_prefix}/{companion_dest}" if extras_prefix else companion_dest + ) + if companion_full in seen_dests: + continue + seen_dests.add(companion_full) + extras.append({ + "name": companion["name"], + "destination": companion_dest, + "sha1": companion_sha1, + "required": False, + "hle_fallback": False, + "source_emulator": profile.get("emulator", emu_name), + "source_profile": emu_name, + "source_system": f.get("system"), + "source_systems": list(profile.get("systems", [])), + "region": f.get("region"), + "variant_group": f.get("variant_group"), + "agnostic_scan": True, + }) return extras +def _companion_extensions(profile: dict, seed_name: str) -> set[str]: + """Extensions the profile declares beside a free-named BIOS entry. + + PCSX2 reads `.rom1` or the BIOS path with its extension replaced + (`.rom1`), and likewise nvm and mec (BiosTools.cpp:214-229, + CDVD.cpp:160-195). A profile states that by naming the companions after + its BIOS entry: ps2-0230a-20080220.rom1 beside ps2-0230a-20080220.bin. + """ + stem = seed_name.rsplit(".", 1)[0].lower() + exts: set[str] = set() + for entry in profile.get("files", []): + other = str(entry.get("name", "")).lower() + if other != seed_name.lower() and other.startswith(stem + "."): + exts.add(other[len(stem) + 1:]) + return exts + + +def _companions( + image_path: str, image_name: str, extensions: set[str], files_db: dict +) -> list[tuple[str, dict]]: + """Files beside a scanned image that the core opens as its companions.""" + if not extensions: + return [] + directory = image_path.rsplit("/", 1)[0] + "/" + stem = image_name.rsplit(".", 1)[0].lower() + wanted = {f"{image_name.lower()}.{ext}" for ext in extensions} + wanted |= {f"{stem}.{ext}" for ext in extensions} + return sorted( + (sha1, entry) + for sha1, entry in files_db.items() + if entry.get("path", "").rsplit("/", 1)[0] + "/" == directory + and str(entry.get("name", "")).lower() in wanted + ) + + def _archive_prefix_extras( profiles: dict, relevant: set, diff --git a/tests/test_bios_companions.py b/tests/test_bios_companions.py new file mode 100644 index 00000000..43e1bf62 --- /dev/null +++ b/tests/test_bios_companions.py @@ -0,0 +1,56 @@ +"""Companions travel with the BIOS image they belong to. + +PCSX2 opens .rom1 or .rom1, and nvm and mec the same way. Its +profile named them rom1.bin, ROM2.BIN and EROM.BIN, names the code never +opens; ROM2.BIN found a misfiled Galaksija ROM inside the PS2 folder, and a +4 MB size bound then swept GameIndex.yaml and cheat archives to the root of +the Batocera BIOS folder. +""" + +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(REPO_ROOT / "scripts")) + +from packextras import _companion_extensions, _companions # noqa: E402 + + +class Companions(unittest.TestCase): + PROFILE = {"files": [ + {"name": "ps2-0230a-20080220.bin"}, + {"name": "ps2-0230a-20080220.rom1"}, + {"name": "ps2-0230a-20080220.nvm"}, + {"name": "GameIndex.yaml"}, + ]} + + def test_extensions_come_from_entries_named_after_the_bios(self): + self.assertEqual( + _companion_extensions(self.PROFILE, "ps2-0230a-20080220.bin"), {"rom1", "nvm"} + ) + + def test_only_files_named_after_the_image_follow_it(self): + files_db = { + "a": {"path": "bios/PS2/SCPH-70004.ROM1", "name": "SCPH-70004.ROM1"}, + "b": {"path": "bios/PS2/scph70004.bin.nvm", "name": "scph70004.bin.nvm"}, + "c": {"path": "bios/PS2/GameIndex.yaml", "name": "GameIndex.yaml"}, + "d": {"path": "bios/Other/SCPH-70004.ROM1", "name": "SCPH-70004.ROM1"}, + } + found = _companions("bios/PS2/SCPH-70004.BIN", "SCPH-70004.BIN", {"rom1", "nvm"}, files_db) + self.assertEqual([sha for sha, _ in found], ["a"]) + + def test_pcsx2_profiles_name_no_free_standing_companion(self): + import yaml + + for name in ("pcsx2", "pcsx2-legacy"): + profile = yaml.safe_load((REPO_ROOT / "emulators" / f"{name}.yml").read_text()) + names = {str(f.get("name", "")).lower() for f in profile.get("files", [])} + with self.subTest(profile=name): + self.assertFalse({"rom1.bin", "rom2.bin", "erom.bin"} & names) + + +if __name__ == "__main__": + unittest.main()