From e96f6af5839f81669a08147e7f95ec7c34164d21 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 21 Oct 2026 10:50:12 +0200 Subject: [PATCH] fix: add flycast standalone data directory paths The standalone build reads BIOS files from its data directory root, not from dc/ (core/oslib/oslib.cpp:118-196, core/stdclass.cpp:95-125, core/linux-dist/main.cpp:74-99). Each entry now carries standalone_path. # Conflicts: # wiki/testing-guide.md --- emulators/flycast.yml | 22 ++++++++++++++++++++++ tests/test_flycast_layout.py | 31 +++++++++++++++++++++++++++++++ wiki/testing-guide.md | 2 +- 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/test_flycast_layout.py diff --git a/emulators/flycast.yml b/emulators/flycast.yml index c25b9135..fc46cf8e 100644 --- a/emulators/flycast.yml +++ b/emulators/flycast.yml @@ -36,6 +36,18 @@ notes: | (shell/libretro/libretro.cpp:2249-2250). All files below are relative to system_dir/dc/. + The standalone emulator looks for each file in the BIOS paths the user + configures, then in its data directory, then in the system data + directories, then in the content directories, and writes the flash back to + the data directory (core/oslib/oslib.cpp:118-196, + core/stdclass.cpp:95-125). The data directory is $XDG_DATA_HOME/flycast/, + else ~/.local/share/flycast/, on Linux (core/linux-dist/main.cpp:74-99), and + the data/ folder of the configuration directory on Windows, Android and + macOS (core/windows/winmain.cpp:73-78, core/windows/winmain.cpp:86-89, + shell/android-studio/flycast/src/main/jni/src/Android.cpp:149-159, + shell/apple/emulator-osx/emulator-osx/osx-main.mm:97-101). + standalone_path is relative to the data directory. + Only the Dreamcast reads flat files. The prefix is dc_ for Dreamcast (core/hw/flashrom/nvmem.cpp:35-50), and the boot ROM is searched as {prefix}boot.bin then {prefix}bios.bin (core/hw/flashrom/nvmem.cpp:304). @@ -65,6 +77,7 @@ files: # ------------------------------------------------------- - name: "dc_boot.bin" path: "dc/dc_boot.bin" + standalone_path: "dc_boot.bin" aliases: ["dc_bios.bin"] system: sega-dreamcast required: false @@ -75,6 +88,7 @@ files: - name: "dc_nvmem.bin" path: "dc/dc_nvmem.bin" + standalone_path: "dc_nvmem.bin" system: sega-dreamcast required: false hle_fallback: true @@ -87,6 +101,7 @@ files: # ------------------------------------------------------- - name: "naomi.zip" path: "dc/naomi.zip" + standalone_path: "naomi.zip" system: sega-naomi required: true category: bios_zip @@ -132,6 +147,7 @@ files: - name: "airlbios.zip" path: "dc/airlbios.zip" + standalone_path: "airlbios.zip" system: sega-naomi required: false category: bios_zip @@ -149,6 +165,7 @@ files: - name: "f355bios.zip" path: "dc/f355bios.zip" + standalone_path: "f355bios.zip" system: sega-naomi required: false category: bios_zip @@ -170,6 +187,7 @@ files: - name: "f355dlx.zip" path: "dc/f355dlx.zip" + standalone_path: "f355dlx.zip" system: sega-naomi required: false category: bios_zip @@ -191,6 +209,7 @@ files: - name: "hod2bios.zip" path: "dc/hod2bios.zip" + standalone_path: "hod2bios.zip" system: sega-naomi required: false category: bios_zip @@ -215,6 +234,7 @@ files: # ------------------------------------------------------- - name: "naomi2.zip" path: "dc/naomi2.zip" + standalone_path: "naomi2.zip" system: sega-naomi2 required: true category: bios_zip @@ -259,6 +279,7 @@ files: # ------------------------------------------------------- - name: "awbios.zip" path: "dc/awbios.zip" + standalone_path: "awbios.zip" system: sega-atomiswave required: true category: bios_zip @@ -274,6 +295,7 @@ files: # ------------------------------------------------------- - name: "segasp.zip" path: "dc/segasp.zip" + standalone_path: "segasp.zip" system: sega-systemsp required: true category: bios_zip diff --git a/tests/test_flycast_layout.py b/tests/test_flycast_layout.py new file mode 100644 index 00000000..81e60b69 --- /dev/null +++ b/tests/test_flycast_layout.py @@ -0,0 +1,31 @@ +"""Flycast's two builds read the same files from two directories. + +The libretro port looks in /dc/ (shell/libretro/libretro.cpp:2249-2250). +The standalone emulator looks in its data directory, whose root holds the +files with no dc/ level (core/oslib/oslib.cpp:118-196, core/stdclass.cpp:95-125). +Without standalone_path the standalone layout of a pack fell back on path: +and put every file one directory too deep. +""" + +from __future__ import annotations + +import unittest +from pathlib import Path + +import yaml + +PROFILE = Path(__file__).resolve().parent.parent / "emulators" / "flycast.yml" + + +class FlycastStandaloneLayout(unittest.TestCase): + def test_every_entry_has_the_data_directory_layout(self) -> None: + profile = yaml.safe_load(PROFILE.read_text(encoding="utf-8")) + self.assertEqual(profile["type"], "standalone + libretro") + for entry in profile["files"]: + with self.subTest(entry=entry["name"]): + self.assertTrue(entry["path"].startswith("dc/")) + self.assertEqual(entry.get("standalone_path"), entry["path"][len("dc/"):]) + + +if __name__ == "__main__": + unittest.main() diff --git a/wiki/testing-guide.md b/wiki/testing-guide.md index 52742fce..83621b18 100644 --- a/wiki/testing-guide.md +++ b/wiki/testing-guide.md @@ -39,7 +39,7 @@ Every module in `tests/`, by the area it guards. |------|----------|---------| | Core regression suites | synthetic | `test_e2e`, `test_audit_regressions`, `test_tool_messages` | | File resolution and identity | synthetic + collection | `test_adler32_resolution`, `test_agnostic_scan`, `test_agnostic_scan_mode`, `test_agnostic_scope`, `test_agnostic_substitute`, `test_archive_choice`, `test_bios_companions`, `test_bundled_identity`, `test_casefold_cache`, `test_db_aliases`, `test_declared_names`, `test_derivations`, `test_directory_entries`, `test_extra_identity`, `test_foreign_tree`, `test_foreign_tree_names`, `test_frontend_digest`, `test_game_data_homonyms`, `test_hashing`, `test_identity`, `test_md5_pack_context`, `test_name_tiebreak`, `test_nec_filing`, `test_owner_resolution`, `test_path_suffix`, `test_resolution_evidence`, `test_shipped_content`, `test_twin_declarations`, `test_validated_choice`, `test_variant_choice`, `test_variant_identity`, `test_zipped_member` | -| Verification, cross-reference and profile checks | synthetic + profiles | `test_crypto_aes`, `test_crypto_unique_data`, `test_crypto_verify`, `test_case_insensitive_platforms`, `test_checks_have_values`, `test_core_file_fills_declaration`, `test_core_size_refusal`, `test_cross_reference_sources`, `test_file_consumers`, `test_fileless_audit`, `test_gap_settlement`, `test_hidden_extras`, `test_launcher_aliases`, `test_launcher_layer`, `test_native_mode`, `test_no_case_collisions`, `test_path_conflict_status`, `test_path_null_copies`, `test_profile_checks`, `test_profile_repositories`, `test_romm_hash_rule`, `test_shared_groups`, `test_standalone_mode`, `test_undeclared_dedup`, `test_unplaced_extras`, `test_validation_scope`, `test_verify_all_dir`, `test_verify_emulator_evidence`, `test_verify_listing`, `test_verify_pack_choice`, `test_verify_release_asset` | +| Verification, cross-reference and profile checks | synthetic + profiles | `test_crypto_aes`, `test_crypto_unique_data`, `test_crypto_verify`, `test_case_insensitive_platforms`, `test_checks_have_values`, `test_core_file_fills_declaration`, `test_core_size_refusal`, `test_cross_reference_sources`, `test_file_consumers`, `test_fileless_audit`, `test_gap_settlement`, `test_hidden_extras`, `test_flycast_layout`, `test_launcher_aliases`, `test_launcher_layer`, `test_native_mode`, `test_no_case_collisions`, `test_path_conflict_status`, `test_path_null_copies`, `test_profile_checks`, `test_profile_repositories`, `test_romm_hash_rule`, `test_shared_groups`, `test_standalone_mode`, `test_undeclared_dedup`, `test_unplaced_extras`, `test_validation_scope`, `test_verify_all_dir`, `test_verify_emulator_evidence`, `test_verify_listing`, `test_verify_pack_choice`, `test_verify_release_asset` | | Packs and manifests | synthetic + real packs | `test_artifact_lock`, `test_atomic_artifacts`, `test_batocera_cores`, `test_deterministic_zip`, `test_emulator_pack_counts`, `test_finalize_pack_names`, `test_large_file_cache`, `test_libretro_layout`, `test_manifest_entry`, `test_pack_conformance_dirs`, `test_pack_core_extras`, `test_pack_counts`, `test_pack_destinations`, `test_pack_failures`, `test_pack_file_names`, `test_pack_grouping`, `test_pack_integrity`, `test_pack_member_identity`, `test_pack_readme`, `test_platform_inheritance`, `test_platform_keys`, `test_required_only_archives`, `test_required_only_twins`, `test_split_extras`, `test_split_names`, `test_split_pack`, `test_system_pack_extras`, `test_system_pack_parity`, `test_torrentzip`, `test_which_pack` | | Slots and regions | synthetic + profiles | `test_region`, `test_region_agnostic_scan`, `test_region_groups`, `test_region_held`, `test_region_platform_index`, `test_region_required_only`, `test_slot`, `test_slots`, `test_slots_shipped_file` | | Hardware targets and systems | synthetic + target files | `test_extract_targets`, `test_lakka_targets`, `test_libretro_slugs`, `test_system_ids`, `test_system_selection`, `test_target_names`, `test_target_scrapers`, `test_target_systems`, `test_target_verify` |