diff --git a/scripts/sitediagrams.py b/scripts/sitediagrams.py index 1145102a..e6ae558c 100644 --- a/scripts/sitediagrams.py +++ b/scripts/sitediagrams.py @@ -739,11 +739,12 @@ def localize_images(markdown: str, page: Path) -> str: # system, the Windows form written by the same function as the setup pages'. OS_TABS = re.compile( - r"(?m)^(?P[ \t]*)[ \t]*\n" + r"(?m)^(?P[ \t]*)[ \t]*\n" r"(?P=indent)```(?:sh|bash)[^\n]*\n(?P.*?)\n(?P=indent)```[ \t]*$", re.S, ) OS_TAB_KEYS = ("windows", "macos", "linux", "steamdeck") +_OWN_ARGS = re.compile(r"(?:^|\s)(" + "|".join(OS_TAB_KEYS) + r"):\s*") FENCE_TITLES = {"sh": "Terminal", "powershell": "PowerShell"} @@ -758,10 +759,18 @@ def _installer_args(body: str) -> str: return match.group(1) or "" +def _own_args(text: str) -> dict[str, str]: + """`windows: ARGS macos: ARGS` -> the options each named tab uses.""" + parts = _OWN_ARGS.split(text.strip()) + if parts[0].strip(): + raise ValueError(f"an os-tabs marker names no system: {text.strip()!r}") + return {key: value.strip() for key, value in zip(parts[1::2], parts[2::2])} + + def expand_commands(markdown: str) -> str: """Each install command marked `` as Windows, macOS, - Linux and Steam Deck tabs. `` gives the - Windows tab its own options, for a path written another way there.""" + Linux and Steam Deck tabs. `` + gives a tab its own options, for a folder that differs there.""" if "os-tabs" not in markdown: return markdown from siteguides import TERMINALS, install_command @@ -769,10 +778,11 @@ def expand_commands(markdown: str) -> str: def replace(match: re.Match[str]) -> str: indent = match.group("indent") args = _installer_args(match.group("body")) + own_args = _own_args(match.group("own")) items = [] for key in OS_TAB_KEYS: label, _how, shell = TERMINALS[key] - own = match.group("windows") if key == "windows" and match.group("windows") else args + own = own_args.get(key) or args items.append((label, [f'```{shell} title="{FENCE_TITLES[shell]}"', install_command(shell, own), "```"])) return "\n".join(markup.tabs(items, indent)).rstrip("\n") diff --git a/tests/test_site_r3_wiki.py b/tests/test_site_r3_wiki.py index 8804d314..6a06f1ce 100644 --- a/tests/test_site_r3_wiki.py +++ b/tests/test_site_r3_wiki.py @@ -198,8 +198,9 @@ class SetupsWithoutAPage(unittest.TestCase): for words in ("--dest", "ES-DE", "LaunchBox", "iPhone"): with self.subTest(words=words): self.assertIn(words, section) - # No folder is written: the copy names none it cannot cite. - self.assertNotRegex(section, r"[~/\\][A-Za-z]") + # No folder is written: the copy names none it cannot cite. Link + # targets are pages of this site, not folders. + self.assertNotRegex(re.sub(r"\]\([^)]*\)", "]", section), r"[~/\\][A-Za-z]") def test_the_installer_fills_the_emulators_a_launcher_starts(self): installer = _flat(_wiki("installer.md")) @@ -234,11 +235,12 @@ class ExitStatusAndPinnedInstaller(unittest.TestCase): with self.subTest(field=field): self.assertIn(f'"{field}"', fields) - def test_a_packager_pins_installer_and_lists_to_one_tag(self): + def test_a_packager_pins_installer_and_lists_to_one_commit(self): + # No release carries the installer yet: the section pins a commit. section = _section(self.text, "One line") - self.assertIn("releases/download/", section) + self.assertIn("raw.githubusercontent.com/Abdess/retrobios/$commit", section) self.assertIn("$base/install.py", section) - self.assertIn("RETROBIOS_REF=", section) + self.assertIn("RETROBIOS_REF=$commit", section) self.assertIn('os.environ.get("RETROBIOS_REF"', self.source) diff --git a/tests/test_site_r4_wiki.py b/tests/test_site_r4_wiki.py index af97a7c3..ec3d4aa1 100644 --- a/tests/test_site_r4_wiki.py +++ b/tests/test_site_r4_wiki.py @@ -262,15 +262,18 @@ class PinnedInstaller(unittest.TestCase): self.assertIn(f"dist/{name}", upload) self.assertIn("install/*.json", upload) - def test_the_installer_page_names_the_release_addresses(self): + def test_the_installer_page_pins_one_commit(self): + # v2026.10.10 carries no install.py: until a release does, the + # section pins a commit, never a release address. text = _wiki("installer.md") section = _flat(_section(text, "For packagers")) - for words in ("releases/download/", "--manifest retroarch.json", - "sha256sum --check", "SHA256SUMS.txt", "releases/latest/download/", - "release-process.md#verifying-a-release"): + for words in ("raw.githubusercontent.com/Abdess/retrobios/$commit", + '"$base/install/retroarch.json"', "--manifest retroarch.json", + "sha256sum --check", "RETROBIOS_REF=$commit"): with self.subTest(words=words): self.assertIn(words, section) - self.assertNotIn("raw.githubusercontent.com/Abdess/retrobios//install.py", text) + self.assertNotIn("releases/download/", section) + self.assertNotIn("releases/latest/download/", section) terms = _front(text)["terms"] for word in ("manifest", "events", "exit code", "schema"): with self.subTest(term=word): diff --git a/tests/test_site_r5_wiki.py b/tests/test_site_r5_wiki.py new file mode 100644 index 00000000..e48e452c --- /dev/null +++ b/tests/test_site_r5_wiki.py @@ -0,0 +1,225 @@ +"""Wiki (round 5, package 5). + +Each class names the finding it holds: a "no setup" command that sent +macOS and Steam Deck files where RetroArch never reads, a level 0 rule that +every emulator needs one exact name, FAQ answers that ended at a ZIP most +setups lack, a packager section pointing at release assets that do not +exist, and two copy fixes. Every claim is held against install.py, the +setup guides or the generator that writes the page a link opens. +""" + +from __future__ import annotations + +import re +import sys +import unittest +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(REPO_ROOT)) +sys.path.insert(0, str(REPO_ROOT / "scripts")) + +import sitediagrams # noqa: E402 +from siteguides import INSTALL_BASE # noqa: E402 +from tests import sitepages # noqa: E402 + +WIKI = REPO_ROOT / "wiki" +FENCED = re.compile(r"^```.*?^```", re.S | re.M) +INSTALL_PY = (REPO_ROOT / "install.py").read_text(encoding="utf-8") + + +def _wiki(name: str) -> str: + return (WIKI / name).read_text(encoding="utf-8") + + +def _front(text: str) -> dict: + return yaml.safe_load(text.split("---", 2)[1]) + + +def _section(text: str, heading: str) -> str: + start = re.search(rf"(?m)^#+ {re.escape(heading)}(?:\s*\{{[^}}]*\}})?\n", text) + if start is None: + raise AssertionError(f"no section {heading!r}") + rest = text[start.end():] + blank = FENCED.sub(lambda m: re.sub(r"[^\n]", " ", m.group(0)), rest) + end = re.search(r"(?m)^#{1,2} ", blank) + return rest[: end.start()] if end else rest + + +def _flat(text: str) -> str: + return " ".join(text.split()) + + +def _guide_folders(name: str) -> dict[str, str]: + guides = yaml.safe_load((REPO_ROOT / "platforms" / "_guides.yml").read_text(encoding="utf-8")) + return {row["where"]: row["path"] for row in guides["guides"][name]["folders"]} + + +class NoSetupFoundGivesEachSystemItsFolder(unittest.TestCase): + """C3: macOS, Linux and Steam Deck shared `~/.config/retroarch/system`.""" + + def setUp(self): + self.section = _section(_wiki("troubleshooting.md"), "If the command fails") + + def test_the_label_is_the_installer_message(self): + self.assertIn("**`No supported platform detected`:**", self.section) + self.assertIn('print(" No supported platform detected.")', INSTALL_PY) + + def test_the_lead_names_the_menu(self): + self.assertIn("Not sure? Open RetroArch's **Settings > Directory > System/BIOS**", + _flat(self.section)) + + def test_each_tab_carries_the_folder_retroarch_reads_there(self): + expected = { + # install.py's Windows default and the hint it prints. + "Windows": "C:\\RetroArch-Win64\\system", + "macOS": "~/Documents/RetroArch/system", + "Linux": "~/.config/retroarch/system", + "Steam Deck": "~/.var/app/org.libretro.RetroArch/config/retroarch/system", + } + self.assertIn('_host_path("C:/RetroArch-Win64/system")', INSTALL_PY) + self.assertIn('"windows": "C:\\\\RetroArch-Win64\\\\system"', INSTALL_PY) + self.assertIn('return Path.home() / "Documents" / "RetroArch"', INSTALL_PY) + folders = _guide_folders("retroarch") + self.assertEqual(folders["macOS"], f"`{expected['macOS']}/`") + self.assertEqual(folders["Linux"], f"`{expected['Linux']}/`") + self.assertEqual(folders["Steam Deck and Linux Flatpak"], f"`{expected['Steam Deck']}/`") + + tabs = sitediagrams.expand_commands(self.section) + parts = re.split(r'', tabs) + bodies = dict(zip(parts[1::2], parts[2::2])) + self.assertEqual(set(bodies), set(expected)) + for label, folder in expected.items(): + with self.subTest(tab=label): + self.assertIn(f"--platform retroarch --dest {folder}\n", bodies[label]) + others = {f for key, f in expected.items() if key != label} + for other in others: + self.assertNotIn(f"--dest {other}\n", bodies[label]) + + def test_the_built_page_shows_four_folders(self): + sitepages.require() + built = sitepages.page("wiki/troubleshooting.md").read_text(encoding="utf-8") + self.assertNotIn("os-tabs", built) + for folder in ("C:\\RetroArch-Win64\\system", "~/Documents/RetroArch/system", + "~/.config/retroarch/system", + "~/.var/app/org.libretro.RetroArch/config/retroarch/system"): + with self.subTest(folder=folder): + self.assertIn(f"--dest {folder}", built) + + +class OsTabsTakeOptionsPerSystem(unittest.TestCase): + """The marker gives any tab its own options, and refuses words it cannot read.""" + + PAGE = ("\n" + f"```sh\ncurl -fsSL {INSTALL_BASE}/install.sh | sh -s -- --dest /l\n```\n") + + def test_each_named_tab_uses_its_own(self): + parts = re.split(r'', sitediagrams.expand_commands(self.PAGE)) + bodies = dict(zip(parts[1::2], parts[2::2])) + self.assertIn("--dest E:\\x\n", bodies["Windows"]) + self.assertIn("--dest /m\n", bodies["macOS"]) + self.assertIn("--dest /l\n", bodies["Linux"]) + self.assertIn("--dest /d\n", bodies["Steam Deck"]) + + def test_text_before_a_system_name_fails_the_build(self): + page = self.PAGE.replace("os-tabs windows:", "os-tabs typo windows:") + with self.assertRaises(ValueError): + sitediagrams.expand_commands(page) + + +class MostEmulatorsNotEvery(unittest.TestCase): + """C8: level 0 said every emulator needs one exact name.""" + + def test_the_rule_names_its_exception(self): + section = _flat(_section(_wiki("what-are-bios-files.md"), "The name must match")) + self.assertIn("Most emulators look for one exact name, like `scph5501.bin`.", section) + self.assertIn("A few, like DuckStation, read any name and check what the file holds.", + section) + profile = yaml.safe_load((REPO_ROOT / "emulators" / "duckstation.yml").read_text( + encoding="utf-8")) + self.assertEqual(profile["found_by"], "content") + + +class FaqAnswersLeadSomewhere(unittest.TestCase): + """C9: "Use the ZIP" for iOS, and a dead end for 12 setups without one.""" + + def setUp(self): + self.text = _wiki("faq.md") + self.generator = (REPO_ROOT / "scripts" / "generate_site.py").read_text(encoding="utf-8") + + def test_ios_goes_to_the_iphone_section(self): + section = _flat(_section(self.text, "My setup is not in the list")) + self.assertIn("iPhone, iPad (iOS): no terminal. [Pick your consoles and copy them " + "to the app](../which-pack.md#iphone-and-ipad).", section) + self.assertNotIn("Use the ZIP", section) + self.assertIn('"## iPhone and iPad { #iphone-and-ipad }"', self.generator) + + def test_antivirus_answer_covers_setups_without_a_zip(self): + section = _flat(_section(self.text, "Windows or my antivirus blocks the command")) + self.assertIn("Use your setup's ZIP. None? [Pick your consoles]" + "(../which-pack.md#only-some-consoles) and download their files.", + section) + self.assertNotIn("if any", section) + self.assertIn('"## Only some consoles?"', self.generator) + + +class PackagersPinACommit(unittest.TestCase): + """J4: the section fetched install.py from a release that carries none.""" + + def setUp(self): + self.section = _section(_wiki("installer.md"), "For packagers") + + def test_the_pin_read_is_the_one_install_sh_writes(self): + sed = re.search(r"sed -n '(s/\^DEFAULT_INSTALL_SHA256=.*?/p)'", self.section) + self.assertIsNotNone(sed) + bootstrap = (REPO_ROOT / "install.sh").read_text(encoding="utf-8") + lines = re.findall(r'(?m)^DEFAULT_INSTALL_SHA256="([0-9a-f]{64})"$', bootstrap) + self.assertEqual(len(lines), 1) + + def test_the_manifest_comes_from_the_same_commit(self): + self.assertIn('-O "$base/install/retroarch.json"', self.section) + self.assertTrue((REPO_ROOT / "install" / "retroarch.json").is_file()) + self.assertIn("RETROBIOS_REF=$commit python3 install.py --manifest retroarch.json", + self.section) + self.assertIn('"--manifest",', INSTALL_PY) + self.assertIn('"https://raw.githubusercontent.com/Abdess/retrobios/"', INSTALL_PY) + + def test_search_words(self): + terms = _front(_wiki("installer.md"))["terms"] + for word in ("packager", "pin", "reproducible"): + with self.subTest(word=word): + self.assertIn(word, terms) + + +class SearchWords(unittest.TestCase): + """Contract with the search gate: each page carries its own words.""" + + def test_troubleshooting(self): + terms = _front(_wiki("troubleshooting.md"))["terms"] + for word in ("check my bios folder", "check bios folder"): + with self.subTest(word=word): + self.assertIn(word, terms) + + def test_legal(self): + self.assertIn("license", _front(_wiki("legal.md"))["terms"]) + + +class SmallCopyFixes(unittest.TestCase): + """C-other: the RetroPie share had no name, and Mega CD two spellings.""" + + def test_retropie_share_is_named_with_its_source(self): + section = _section(_wiki("first-install.md"), "Error: Python 3.8 or newer is required.") + self.assertIn("share open, `\\\\RETROPIE\\bios` from Windows:", section) + self.assertIn("samba.sh:49-52", section) + self.assertIn("Transferring-Roms.md:29", section) + + def test_mega_cd_is_spelled_once(self): + for path in sorted(WIKI.glob("*.md")): + with self.subTest(page=path.name): + self.assertNotIn("Mega-CD", path.read_text(encoding="utf-8")) + + +if __name__ == "__main__": + unittest.main() diff --git a/wiki/faq.md b/wiki/faq.md index 41aeceef..0d0e642b 100644 --- a/wiki/faq.md +++ b/wiki/faq.md @@ -67,12 +67,13 @@ Read them first: [install.sh](https://github.com/Abdess/retrobios/blob/main/inst ## Windows or my antivirus blocks the command -Use your setup's ZIP, if any: [Get your files](../which-pack.md). +Use your setup's ZIP. None? [Pick your consoles](../which-pack.md#only-some-consoles) +and download their files. ## Why is there no ZIP for my setup? -ZIPs are made for the setups most people download. The command installs every -setup's files and checks each one. `--dry-run` shows the list first. +ZIPs exist for the most downloaded setups. The command installs and checks +every setup's files. ## Fill an SD card from a computer? @@ -99,7 +100,8 @@ Fill it from a computer, as above, or point `--from-pack` at your ZIP: [Without - Name your emulator's BIOS folder with `--dest`. - ES-DE, LaunchBox: the command fills their emulators' folders. -- iPhone, iPad (iOS): no terminal. Use the ZIP. +- iPhone, iPad (iOS): no terminal. + [Pick your consoles and copy them to the app](../which-pack.md#iphone-and-ipad). ## Why is a file missing from the ZIP? diff --git a/wiki/first-install.md b/wiki/first-install.md index 5d993b17..ef62456a 100644 --- a/wiki/first-install.md +++ b/wiki/first-install.md @@ -141,8 +141,12 @@ Choose **Another setup or folder**, then your setup and its folder. The message shows the command that installs Python. Run it, then run the installer again. -A RetroPie 4.8 image has Python 3.7.3, and its update command finds nothing -newer. Run it on a computer, the `bios` share open: +RetroPie 4.8 has Python 3.7.3, and its update finds nothing newer. Run it on +a computer, with RetroPie's `bios` share open, `\\RETROPIE\bios` from Windows: + + ```sh diff --git a/wiki/installer.md b/wiki/installer.md index 2abe2c5d..95167d5f 100644 --- a/wiki/installer.md +++ b/wiki/installer.md @@ -1,7 +1,7 @@ --- level: setup order: 100 -terms: manifest, events, exit code, schema +terms: manifest, events, exit code, schema, packager, pin, reproducible --- # All installer options @@ -47,24 +47,21 @@ python install.py ### For packagers { #for-packagers } A package that must not change under its users takes the installer and its -file list from one release. Each release carries `install.py`, `install.sh`, -`install.ps1` and the file list of every platform (`retroarch.json` and the -others from `install/`), all listed in the signed `SHA256SUMS.txt`: +file list from one commit. `install.sh` of that commit pins the SHA-256 of its +`install.py`, and `RETROBIOS_REF` makes the installer download every file from +the same commit: ```bash -base=https://github.com/Abdess/retrobios/releases/download/ -curl -fsSLO "$base/install.py" -O "$base/retroarch.json" -O "$base/SHA256SUMS.txt" -sha256sum --check --ignore-missing SHA256SUMS.txt -RETROBIOS_REF= python3 install.py --manifest retroarch.json +commit= +base=https://raw.githubusercontent.com/Abdess/retrobios/$commit +curl -fsSLO "$base/install.py" -O "$base/install/retroarch.json" +pin=$(curl -fsSL "$base/install.sh" | sed -n 's/^DEFAULT_INSTALL_SHA256="\(.*\)"$/\1/p') +echo "$pin install.py" | sha256sum --check +RETROBIOS_REF=$commit python3 install.py --manifest retroarch.json ``` -For contributors: [Verifying a release](release-process.md#verifying-a-release) -checks the signature of the list. Each release deletes the previous tag and its assets, -so `releases/latest/download/install.py` and the other files under -`releases/latest/download/` are the stable addresses: they serve the latest -release, whose `SHA256SUMS.txt` names the bytes. v2026.10.10 and the releases -before it carry neither the installer nor the file lists. What `RETROBIOS_REF` -does not pin is listed under [Environment overrides](#environment-overrides). +What `RETROBIOS_REF` does not pin is listed under +[Environment overrides](#environment-overrides). ## Passing options diff --git a/wiki/legal.md b/wiki/legal.md index 7f5d54ae..812115bd 100644 --- a/wiki/legal.md +++ b/wiki/legal.md @@ -1,6 +1,7 @@ --- level: start order: 55 +terms: license --- # Is it legal? diff --git a/wiki/testing-guide.md b/wiki/testing-guide.md index 42dd06a9..279087cf 100644 --- a/wiki/testing-guide.md +++ b/wiki/testing-guide.md @@ -50,14 +50,14 @@ 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_archive_recipes`, `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_sega_filing`, `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_bios_layout`, `test_cache_independent_counts`, `test_case_insensitive_platforms`, `test_checks_have_values`, `test_core_file_fills_declaration`, `test_core_size_refusal`, `test_cross_reference_sources`, `test_crypto_aes`, `test_crypto_unique_data`, `test_crypto_verify`, `test_entry_categories`, `test_file_consumers`, `test_fileless_audit`, `test_flycast_layout`, `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_links`, `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_bios_layout`, `test_cache_independent_counts`, `test_case_insensitive_platforms`, `test_checks_have_values`, `test_core_file_fills_declaration`, `test_core_size_refusal`, `test_cross_reference_sources`, `test_crypto_aes`, `test_crypto_unique_data`, `test_crypto_verify`, `test_entry_categories`, `test_file_consumers`, `test_fileless_audit`, `test_flycast_layout`, `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_names`, `test_profile_paths`, `test_profile_repositories`, `test_romm_hash_rule`, `test_shared_groups`, `test_standalone_links`, `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_minui_platform`, `test_no_game_data`, `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_release_packs`, `test_required_only_archives`, `test_required_only_twins`, `test_save_root`, `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` | | Installer and downloads | loopback server | `test_check_release`, `test_download`, `test_install`, `test_install_cores`, `test_install_experience`, `test_install_from`, `test_install_offline`, `test_install_selection`, `test_install_tui` | | Handheld launchers | a pty as the console, fake installer and firmware tools, stubbed PortMaster dialogs | `test_handheld_launchers`, `test_port` | | Truth and native export | synthetic + cached originals | `test_export_counts`, `test_export_native_layer`, `test_export_report`, `test_exporters`, `test_truth_data_dir_readers`, `test_truth_diff`, `test_truth_layout` | -| Scrapers, upstream and provenance | inline sources, synthetic | `test_batocera_md5_list`, `test_check_freshness`, `test_contributing_text`, `test_coverage_cache`, `test_data_dir_refs`, `test_dataclass_defaults`, `test_dedup`, `test_doc_pages`, `test_doc_surfaces`, `test_elec_scrapers`, `test_fbneo_parser`, `test_find_pinned_revision`, `test_hash_merge`, `test_launchers`, `test_launchers_emudeck`, `test_launchers_recalbox`, `test_launchers_retrobat`, `test_launchers_retrodeck`, `test_launchers_retropie`, `test_launchers_rocknix`, `test_mame_devices`, `test_mame_parser`, `test_mame_ref_audit`, `test_optional_dependencies`, `test_profile_sync`, `test_prose_style`, `test_provenance`, `test_refresh_data_dirs`, `test_refresh_failures`, `test_refresh_stale`, `test_retrodeck_scraper_errors`, `test_romset_recipes`, `test_schema_semantic_gate`, `test_scraper_contract`, `test_site_availability`, `test_site_content_data`, `test_site_copy`, `test_site_cores`, `test_site_emulator_page`, `test_site_exports`, `test_site_funnel`, `test_site_images`, `test_site_picker`, `test_site_platform_hashes`, `test_site_r2_components`, `test_site_r2_css`, `test_site_r2_emulators`, `test_site_r2_entry`, `test_site_r2_gate`, `test_site_r2_nav`, `test_site_r2_p0`, `test_site_r2_reference`, `test_site_r2_systems`, `test_site_r2_wiki`, `test_site_r3_collection`, `test_site_r3_components`, `test_site_r3_css`, `test_site_r3_emulators`, `test_site_r3_entry`, `test_site_r3_nav`, `test_site_r3_p0`, `test_site_r3_reference`, `test_site_r3_systems`, `test_site_r3_wiki`, `test_site_r4_components`, `test_site_r4_css`, `test_site_r4_emulators`, `test_site_r4_entry`, `test_site_r4_merge`, `test_site_r4_p0`, `test_site_r4_reference`, `test_site_r4_search`, `test_site_r4_systems`, `test_site_r4_wiki`, `test_site_r5_components`, `test_site_r5_reference`, `test_site_render`, `test_site_system_links`, `test_site_validation`, `test_site_weight`, `test_site_write`, `test_source_ref_targets`, `test_upstream`, `test_upstream_index`, `test_validate_pr`, `test_validate_pr_flow`, `test_validate_pr_hashes`, `test_workflow_paths` | +| Scrapers, upstream and provenance | inline sources, synthetic | `test_batocera_md5_list`, `test_check_freshness`, `test_contributing_text`, `test_coverage_cache`, `test_data_dir_refs`, `test_dataclass_defaults`, `test_dedup`, `test_doc_pages`, `test_doc_surfaces`, `test_elec_scrapers`, `test_fbneo_parser`, `test_find_pinned_revision`, `test_hash_merge`, `test_launchers`, `test_launchers_emudeck`, `test_launchers_recalbox`, `test_launchers_retrobat`, `test_launchers_retrodeck`, `test_launchers_retropie`, `test_launchers_rocknix`, `test_mame_devices`, `test_mame_parser`, `test_mame_ref_audit`, `test_optional_dependencies`, `test_profile_sync`, `test_prose_style`, `test_provenance`, `test_refresh_data_dirs`, `test_refresh_failures`, `test_refresh_stale`, `test_retrodeck_scraper_errors`, `test_romset_recipes`, `test_schema_semantic_gate`, `test_scraper_contract`, `test_site_availability`, `test_site_content_data`, `test_site_copy`, `test_site_cores`, `test_site_emulator_page`, `test_site_exports`, `test_site_funnel`, `test_site_images`, `test_site_picker`, `test_site_platform_hashes`, `test_site_r2_components`, `test_site_r2_css`, `test_site_r2_emulators`, `test_site_r2_entry`, `test_site_r2_gate`, `test_site_r2_nav`, `test_site_r2_p0`, `test_site_r2_reference`, `test_site_r2_systems`, `test_site_r2_wiki`, `test_site_r3_collection`, `test_site_r3_components`, `test_site_r3_css`, `test_site_r3_emulators`, `test_site_r3_entry`, `test_site_r3_nav`, `test_site_r3_p0`, `test_site_r3_reference`, `test_site_r3_systems`, `test_site_r3_wiki`, `test_site_r4_components`, `test_site_r4_css`, `test_site_r4_emulators`, `test_site_r4_entry`, `test_site_r4_merge`, `test_site_r4_p0`, `test_site_r4_reference`, `test_site_r4_search`, `test_site_r4_systems`, `test_site_r4_wiki`, `test_site_r5_components`, `test_site_r5_reference`, `test_site_r5_wiki`, `test_site_render`, `test_site_system_links`, `test_site_validation`, `test_site_weight`, `test_site_write`, `test_source_ref_targets`, `test_upstream`, `test_upstream_index`, `test_validate_pr`, `test_validate_pr_flow`, `test_validate_pr_hashes`, `test_workflow_paths` | ## Test architecture diff --git a/wiki/troubleshooting.md b/wiki/troubleshooting.md index d90b2ef0..6b39be82 100644 --- a/wiki/troubleshooting.md +++ b/wiki/troubleshooting.md @@ -1,7 +1,7 @@ --- level: start order: 40 -terms: bios not found, bios missing, firmware missing, bios error +terms: bios not found, bios missing, firmware missing, bios error, check my bios folder, check bios folder --- # When a game does not start @@ -33,7 +33,7 @@ Read the count line: - **`wrong hash`**: another region or model, or damaged. - **Fewer present than the total**: files are missing. -- **A line under a file**, like `still zipped`, says what to fix. +- **A line under a file**, like `still zipped`, says the fix. - **`0 wrong hash`, all present**: go to step 3. ## 2. Fix the files @@ -44,11 +44,11 @@ Without `--check`, it adds missing files and replaces wrong ones. - **Right folder?** [Get your files](../which-pack.md) names it. - **Exact name?** `scph5501.bin` and `SCPH-5501.BIN` differ. -- **A `bios` folder inside your BIOS folder?** Run the installer: it copies - those files up one level. +- **A `bios` folder inside your BIOS folder?** The installer copies those + files up one level. - **BIOS setting on a subfolder, like `system/dc`?** Point it at the BIOS folder. Emulators add their own, like `dc/` for Flycast. -- **A ZIP in parts, like `Pack.part1of2.zip`?** Unzip every part into one +- **A ZIP in parts, like `Pack.part1of2.zip`?** Unzip all parts into one folder. + + + ```sh curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --platform retroarch --dest ~/.config/retroarch/system ``` diff --git a/wiki/what-are-bios-files.md b/wiki/what-are-bios-files.md index 58ce9e67..985839f4 100644 --- a/wiki/what-are-bios-files.md +++ b/wiki/what-are-bios-files.md @@ -31,14 +31,15 @@ If step 3 fails, you see "BIOS missing" or a black screen. - **Better with it:** Dreamcast. Some games need it. - **Usually not:** NES, Super Nintendo, Mega Drive, Game Boy and Game Boy Advance. -- **Add-ons need their own:** the Famicom Disk System and the Mega-CD. +- **Add-ons need their own:** the Famicom Disk System and the Mega CD. Details: [Every console, one by one](../systems/index.md) ## The name must match -An emulator looks for one exact name. A PlayStation emulator looks for -`scph5501.bin`. A file with another name is not found. +Most emulators look for one exact name, like `scph5501.bin`. A file with +another name is not found. A few, like DuckStation, read any name and check +what the file holds. ## The version must match