From 235001babb057c34ff01d0d51a553be62f1e6350 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Fri, 23 Oct 2026 02:34:13 +0200 Subject: [PATCH] feat: cite terminals, shares and hardware --- platforms/_guides.yml | 180 +++++++++++++++++++++++++++++++++--------- scripts/siteguides.py | 80 ++++++++++++++++--- scripts/sitemarkup.py | 31 ++++++++ 3 files changed, 242 insertions(+), 49 deletions(-) diff --git a/platforms/_guides.yml b/platforms/_guides.yml index 1b109bcf6..d04de3e1a 100644 --- a/platforms/_guides.yml +++ b/platforms/_guides.yml @@ -7,6 +7,10 @@ # `extract_to` for that platform, the one place that folder is written. # `run` names the terminals below, in the order the page shows them. # `pack_manual` is the manual part of the README inside the pack, plain text. +# `{folders}` in it is the folder list of the page. +# `runs_on` names the hardware in the project's own words, cited beside it. +# `terminal` is how this system opens a terminal, from its own documentation. +# `lead` comes before the folder table, `finish` closes the guide. # `logo_owner` and `logo_licence` credit the logo the registry links: it is # shown only to name the platform, with the licence of the repository the # file was taken from when that repository states one. @@ -16,15 +20,17 @@ install_base: https://raw.githubusercontent.com/Abdess/retrobios/main -# Is it safe: shown under every command on a guide page. +# Is it safe: shown on the home page and under every command on a guide page. +# Where it writes follows install.py: the BIOS folder, the frontend's save +# folder for the entries a manifest files under `root: save` +# (place_save_entries), and retrobios-backup beside the folder written to. trust_items: - It checks each file before saving it. -- It writes only in your BIOS folder. -- It never touches games or saves. -- It never moves or deletes files it does not know. +- It writes in your BIOS folder. For some emulators it also adds files to your save folder. +- It never changes or deletes a game or a save. - >- - It keeps any file it replaces in a `retrobios-backup` folder, beside your BIOS folder. - The same command with `--restore` puts it back. + It keeps any file it replaces in a `retrobios-backup` folder, next to your BIOS folder. + `--restore` puts it back. # Order of the groups on the picker, in the navigation and on the home page. # `anchor` is the section id on the device picker: two keep the ids the page @@ -33,7 +39,7 @@ trust_items: # and use its pack. device_groups: computer: - label: Computers and Steam Deck + label: Computers (Windows, Mac, Linux) and Steam Deck description: An emulator app on a computer or a Steam Deck. anchor: computers-and-steam-deck also_runs: @@ -44,12 +50,11 @@ device_groups: runs_on: Windows folder: The folder of the RetroArch it starts gaming-os: - label: Game systems for PCs and small boards - description: >- - A system that starts straight into a game menu, on a PC or a Raspberry Pi. + label: Raspberry Pi and mini PCs + description: A system that starts straight into a game menu. anchor: game-systems handheld: - label: Handhelds and TV boxes + label: Handhelds and TV boxes (Anbernic, Retroid, Miyoo) description: The system your handheld or TV box starts. anchor: handhelds also_runs: @@ -137,8 +142,7 @@ guides: more_folders_detected: true more_folders_note: LaunchBox and other launchers use the folder RetroArch reads. run: [windows, macos, linux, steamdeck, android] - notes: - - >- + lead: >- **Not sure?** In RetroArch, open **Settings > Directory > System/BIOS**. It shows the folder. reference_notes: @@ -161,21 +165,27 @@ guides: Option C: Manual (handheld / SD card) ------------------------------------- - Anbernic, Retroid, Miyoo, Trimui, etc.: 1. Connect your SD card to your PC - 2. Find the BIOS folder (usually BIOS/ or system/) + 2. Find the BIOS folder in the list below 3. Extract all files from this archive directly into that folder 4. Eject SD card and reboot your device - Common paths by device: - Anbernic (ArkOS/JELOS): BIOS/ - Retroid (RetroArch): RetroArch/system/ - Miyoo Mini (Onion OS): BIOS/ - Steam Deck (RetroArch): ~/.config/retroarch/system/ + Folders by device: + {folders} logo_owner: libretro logo_licence: repository licence GPL-3.0 batocera: - summary: A system that starts straight into a game menu. + summary: A system that starts straight into a game menu, on a PC, a Raspberry Pi or a handheld. + # batocera.org/download: x86-64 desktop PC and laptop, Raspberry, Steam Deck OLED, + # Anbernic and AYN handhelds. + runs_on: PCs, Raspberry Pi, Steam Deck, handhelds + # wiki.batocera.org/access_the_batocera_via_ssh: root@batocera.local, password + # linux, SSH on by default. wiki.batocera.org (file manager): xterm in the + # Applications menu of the F1 file manager, Xorg builds (x86_64) only. + terminal: >- + On a PC, press **F1** on the system list, then open **xterm** in the Applications + menu. From a computer, open a terminal over the network with `ssh + root@batocera.local`, password `linux`. Paste the line. Press Enter. folders: - where: On Batocera - where: From Windows, over your home network @@ -185,7 +195,8 @@ guides: run: [device] download_hint: From a computer, use the network folder above. pack_manual: | - The terminal is F1 from the Batocera menu, or SSH. + The terminal is xterm, in the Applications menu of the F1 file + manager (PC only), or SSH: ssh root@batocera.local, password linux. Option B: Manual (network share) -------------------------------- @@ -206,8 +217,15 @@ guides: logo_owner: Batocera logo_licence: repository licence MIT recalbox: - summary: >- - A system that starts straight into a game menu and sets up its emulators. + summary: A system that starts straight into a game menu, on a board, a PC or a handheld. + # recalbox.com: "TV, PC or handheld?", 13 supported boards and consoles. + runs_on: Boards, PCs, handhelds + # wiki.recalbox.com/en/tutorials/system/access/root-access-terminal-cli: Alt+F2 with + # a keyboard. SSH user root, password recalboxroot or the one set when flashing 10.0+. + terminal: >- + With a keyboard, press **Alt+F2**. From a computer, open a terminal over the network + with `ssh root@recalbox`, password `recalboxroot` or the one you set. Paste the line. + Press Enter. folders: - where: On Recalbox - where: From Windows, over your home network @@ -219,7 +237,6 @@ guides: -------------------------------- 1. On your PC, open the Recalbox network share: - Windows: \\RECALBOX\share\bios\ - - Mac/Linux: smb://recalbox/share/bios/ 2. Extract all files from this archive directly into the share Option C: Manual (SD card) @@ -258,14 +275,23 @@ guides: summary: Installs your emulators and adds your games to Steam. folders: - where: Steam Deck and Linux + # EmuDeck functions/helperFunctions.sh:89-99 (getSDPath, the card's mount point) + # and functions/jsonToBashVars.sh:128-132 (biosPath "$globPath/Emulation/bios"), + # dragoonDorise/EmuDeck dddb3022. + - where: Steam Deck, installed on the SD card + path: '`Emulation/bios/` on the card, under `/run/media/`' - where: Windows, by default path: '`C:\Emulation\bios\`' - where: macOS run: [steamdeck, linux, windows, macos] notes: - >- - The `bios` folder is inside the Emulation folder you chose in EmuDeck. If you chose - the SD card, it is on the card. + The `bios` folder is inside the Emulation folder you chose in EmuDeck. + # emudeck-electron src/renderer/translations/en.json:416-418 (b06bfcf5): the + # "Bios files checker". + finish: >- + EmuDeck's **Bios files checker** shows the files too. Return to Gaming Mode and start + a game. reference_notes: - >- The installer reads the Emulation folder from EmuDeck's settings (`emulationPath` @@ -280,13 +306,24 @@ guides: logo_owner: EmuDeck logo_licence: repository licence GPL-3.0 lakka: - summary: A system that starts straight into RetroArch. + summary: A system that starts straight into RetroArch, on a Raspberry Pi or a small PC. + # lakka.tv: "transforms a small computer", "a Raspberry Pi". + runs_on: Raspberry Pi, small computers folders: - where: On Lakka + # lakka.tv/doc/Accessing-Lakka-filesystem: Samba in Settings > Services, share System. + - where: From Windows, over your home network + path: '`\\LAKKA\System\`' run: [device] notes: - >- - You can also reach the folder from a computer, over your home network. + For the network folder, turn on **Samba** in Lakka's **Settings > Services**. + # lakka.tv/doc/Accessing-Lakka-command-line-interface: SSH in Settings > Services, + # user root, password root. + terminal: >- + Turn on **SSH**, a terminal over the network, in Lakka's **Settings > Services**. From + a computer, connect with `ssh root@lakka`, password `root`. Paste the line. Press + Enter. reference_notes: - The folder is reachable over SSH and over the network share. pack_manual: | @@ -296,7 +333,6 @@ guides: -------------------------- 1. On Lakka: Settings > Services > Samba Enable 2. On your PC, open the System share: \\LAKKA\System\ - (macOS and Linux: smb://lakka/System) 3. Extract all files from this archive directly into that share Option C: Via SSH @@ -365,12 +401,20 @@ guides: logo_licence: GitHub organisation avatar of rommapp rocknix: summary: A system for handhelds. + # rocknix.org: "an immutable Linux distribution for handheld gaming devices". + runs_on: Handhelds folders: - where: On ROCKNIX run: [device] notes: - >- You can also reach the folder from a computer, over your home network. + # rocknix.org/faqs and rocknix.org/play/add-games: SSH off by default, turned on in + # Network Settings, user root, the password shown in System Settings > Security. + terminal: >- + Turn on **SSH**, a terminal over the network, in **Network Settings**. From a + computer, connect as `root`, with the password shown in **System Settings > + Security**. Paste the line. Press Enter. reference_notes: - >- ROCKNIX runs on ARM and x86 handhelds, among them the RG35XX (H700) and the RG552 @@ -379,9 +423,15 @@ guides: logo_licence: GitHub organisation avatar of ROCKNIX amberelec: summary: A system for Anbernic handhelds. + # amberelec.org: "optimized for the RG351P/M/V/MP and RG552". + runs_on: Anbernic RG351 and RG552 folders: - where: On AmberELEC run: [device] + # amberelec.org/guides/getting-to-know-amberelec.html: host AmberELEC, root, amberelec. + terminal: >- + From a computer, open a terminal over the network with `ssh root@AmberELEC`, password + `amberelec`. Paste the line. Press Enter. reference_notes: - >- AmberELEC runs on the RG351P, RG351V, RG351MP, RG353, RG503 and RG552. Its list @@ -391,9 +441,20 @@ guides: logo_licence: GitHub organisation avatar of AmberELEC emuelec: summary: A system for TV boxes and some handhelds. + # EmuELEC wiki Supported-Devices.md: Amlogic boxes, Odroid-GO Advance and Super, + # GameForce Chi. + runs_on: TV boxes, Odroid Go handhelds folders: - where: On EmuELEC + # EmuELEC wiki (35b92c78) Backup-and-Recovery-options.md:4 (\\EMUELEC, roms share) + # and Bios.md (/storage/roms/bios). + - where: From Windows, over your home network + path: '`\\EMUELEC\roms\`' run: [device] + # EmuELEC wiki How-to-SSH-into-EmuELEC.md:38-44: root, password emuelec. + terminal: >- + From a computer, open a terminal over the network with `ssh root@emuelec`, password + `emuelec`. Paste the line. Press Enter. reference_notes: - >- EmuELEC runs on Amlogic TV boxes, the Odroid Go Advance, GameForce and RK356x devices. @@ -402,10 +463,24 @@ guides: logo_owner: EmuELEC logo_licence: GitHub organisation avatar of EmuELEC knulli: - summary: A system for handhelds, based on Batocera. + summary: A system for handhelds and bartops, based on Batocera. + # knulli.org: "retro gaming devices (handhelds, bartops, etc.)". + runs_on: Handhelds, bartops folders: - where: On KNULLI + # knulli.org/play/add-games/network-transfer: \\KNULLI, its share folder, Samba + # off by default. The share is /userdata, so bios is under it. + - where: From Windows, over your home network + path: '`\\KNULLI\share\bios\`' run: [device] + notes: + - >- + For the network folder, turn on **SAMBA** in **System Settings > Services**. + # knulli.org/configure/ssh: SSH in System Settings > Services, root, password linux. + terminal: >- + Turn on **SSH**, a terminal over the network, in **System Settings > Services**. From + a computer, connect with `ssh root@KNULLI`, password `linux`. Paste the line. Press + Enter. reference_notes: - >- KNULLI runs on handhelds among them the RG35XX family (H700) and the TrimUI Smart @@ -413,14 +488,24 @@ guides: logo_owner: KNULLI logo_licence: GitHub organisation avatar of knulli-cfw retropie: - summary: Installs RetroArch and other emulators on a Raspberry Pi. + summary: Installs RetroArch and other emulators on a Raspberry Pi or a PC. + # RetroPie docs index.md (175f5b15): Raspberry Pi, PC and Odroid. + runs_on: Raspberry Pi, PC, Odroid folders: - where: On the Raspberry Pi + # RetroPie-Setup scriptmodules/supplementary/samba.sh:49-52 (46128cd2) shares + # $home/RetroPie/BIOS as "bios", and docs/Transferring-Roms.md:29 names \\RETROPIE. + - where: From Windows, over your home network + path: '`\\RETROPIE\bios\`' run: [device] + # RetroPie docs/FAQ.md:193 (F4 in EmulationStation, its game menu), 175f5b15. The computer route + # is the network folder above. + terminal: >- + Press **F4** in the game menu for a terminal. Paste the line. Press Enter. notes: - >- - Use the RetroPie pack, not the RetroArch one. On RetroPie 4.8, Python is too old - for the command: use the pack there. + Use the RetroPie pack, not RetroArch's. The command does not run on RetroPie 4.8: use + the pack there. reference_notes: - >- RetroPie also installs standalone emulators (openMSX, PCSX2, XRoar, Amiberry and @@ -469,13 +554,26 @@ guides: misterfpga: summary: >- A board that recreates old consoles in hardware, not in software. + # mister-devel.github.io/MkDocs_MiSTer: "an FPGA board called the DE10-Nano". + runs_on: Its own board folders: - where: On the MiSTer SD card path: '`/media/fat/games/`, one subfolder per core' + # mister-devel.github.io/MkDocs_MiSTer/setup/games: run samba_on from the Scripts + # menu, then \\MiSTer\sdcard\. + - where: From Windows, over your home network + path: '`\\MiSTer\sdcard\games\`' run: [device] + # mister-devel.github.io/MkDocs_MiSTer/advanced/network: F9 for a Linux terminal, + # SSH user root, password 1. + terminal: >- + Press **F9** in the MiSTer menu and log in as `root`, password `1`. Or from a + computer, open a terminal over the network as `root`. Paste the line. Press Enter. notes: - >- Each file goes in its console's folder, like `/media/fat/games/3DO/boot.rom`. + - >- + For the network folder, run **samba_on** from the MiSTer Scripts menu. reference_notes: - >- MiSTer runs FPGA cores on a DE10-Nano board. Only the files the MiSTer BIOS database @@ -484,7 +582,9 @@ guides: logo_owner: MiSTer FPGA logo_licence: GitHub organisation avatar of MiSTer-devel minui: - summary: A simple game menu for handhelds. It runs from the SD card. + summary: A simple game menu for Anbernic, Miyoo and Trimui handhelds, on the SD card. + # shauninman/MinUI README (dbf89435) lines 3, 65-91. + runs_on: Anbernic, Miyoo, Trimui handhelds folders: - where: On the SD card path: '`Bios//`, one folder per console' @@ -506,8 +606,9 @@ guides: logo_owner: Shaun Inman logo_licence: GitHub avatar of shauninman, the author of MinUI nextui: - summary: >- - A game menu for handhelds, based on MinUI. It runs from the SD card. + summary: A game menu for TrimUI handhelds, based on MinUI, on the SD card. + # LoveRetro/NextUI README (5291344b) lines 37, 89-94. + runs_on: TrimUI Brick, Smart Pro folders: - where: On the SD card path: '`Bios//`, one folder per console' @@ -524,8 +625,9 @@ guides: logo_owner: LoveRetro logo_licence: GitHub organisation avatar of LoveRetro myminui: - summary: >- - A game menu for handhelds, based on MinUI. It runs from the SD card. + summary: A game menu for Anbernic and Miyoo handhelds, based on MinUI, on the SD card. + # Turro75/MyMinUI README (72ed4f3d) lines 9-35. + runs_on: Anbernic, Miyoo handhelds folders: - where: On the SD card path: '`Bios//`, one folder per console' diff --git a/scripts/siteguides.py b/scripts/siteguides.py index f0f4fe5ba..991e1e215 100644 --- a/scripts/siteguides.py +++ b/scripts/siteguides.py @@ -38,7 +38,12 @@ class Guide: and open the Reference part of the page. `download_hint` is one more bullet under the pack download. `pack_manual` is the manual part of the README inside the pack. `logo_owner` and `logo_licence` credit the - logo the registry links. + logo the registry links. `runs_on` names the hardware, in the project's + own words, for its tile and its row. `terminal` replaces the shared + "On the device" text with how this system opens a terminal. `lead` comes + before the folder table, `finish` closes the guide. `{folders}` in + `pack_manual` is the folder list of the page, so the README and the page + cannot name two folders for one device. """ summary: str @@ -56,6 +61,10 @@ class Guide: pack_manual: str = "" logo_owner: str = "" logo_licence: str = "" + runs_on: str = "" + terminal: str = "" + lead: str = "" + finish: str = "" _GUIDE_KEYS = {f.name for f in fields(Guide)} @@ -130,7 +139,7 @@ def _guide(name: str, data: object, extract_to: str, terminals: dict) -> Guide: if key in data: values[key] = _texts(data[key], f"{where}.{key}") for key in ("dest", "download_hint", "more_folders_title", "more_folders_note", - "logo_owner", "logo_licence"): + "logo_owner", "logo_licence", "runs_on", "terminal", "lead", "finish"): if key in data: values[key] = _text(data[key], f"{where}.{key}") for key in ("on_card", "more_folders_detected"): @@ -213,6 +222,7 @@ def load_guides(path: Path = GUIDES_FILE, registry: dict | None = None) -> dict: # The order the file is written in, which the pages do not depend on. guides = {name: guides[name] for name in raw_guides} return { + "registry": registry, "install_base": _text(data.get("install_base"), f"{path.name}: install_base"), "trust_items": _texts(data.get("trust_items"), f"{path.name}: trust_items"), "device_groups": groups, @@ -231,6 +241,43 @@ ALSO_RUNS: dict[str, tuple[tuple[str, str, str, str, str], ...]] = _LOADED["also TERMINALS: dict[str, tuple[str, str, str]] = _LOADED["terminals"] TRUST_ITEMS: tuple[str, ...] = _LOADED["trust_items"] GUIDES: dict[str, Guide] = _LOADED["guides"] +_REGISTRY: dict = _LOADED["registry"] + + +def detected_systems(registry: dict, platform: str) -> list[tuple[str, str]]: + """Named systems a platform runs on, handhelds that run RetroArch for one, + with the BIOS folder the installer writes on each, read from the + detection rules of the registry.""" + seen: dict[str, str] = {} + for rule in (registry.get(platform, {}).get("install") or {}).get("detect") or []: + name, folder = rule.get("name"), rule.get("bios_path") + if name and folder and rule.get("installs", True) and name not in seen: + seen[name] = folder + return sorted(seen.items(), key=lambda item: item[0].casefold()) + + +def folder_list(name: str, registry: dict | None = None) -> list[tuple[str, str]]: + """Every folder a guide names, as (where, path): the common ones, the + rarer builds, and the systems the registry detects when the guide says so.""" + guide = GUIDES[name] + rows = [*guide.folders, *guide.more_folders] + if guide.more_folders_detected: + rows += [(system, f"`{folder}`") + for system, folder in detected_systems(registry or _REGISTRY, name)] + return rows + + +def _plain_folders(name: str) -> str: + rows = folder_list(name) + width = max(len(where) for where, _path in rows) + 1 + return "\n".join(f"{where + ':':<{width}} {path.replace('`', '')}" for where, path in rows) + + +def setup_count() -> int: + """Setups the site covers: every registered platform, archived ones + included, since each keeps its page, its pack and its command. The one + count every page states.""" + return len(GUIDES) def pack_manual(name: str) -> str: @@ -238,7 +285,12 @@ def pack_manual(name: str) -> str: guide = GUIDES.get(name) if not guide or not guide.pack_manual: return "" - return textwrap.indent(guide.pack_manual.rstrip("\n"), " ") + "\n\n" + text = guide.pack_manual.rstrip("\n") + if "{folders}" in text: + indent = next(line[: len(line) - len(line.lstrip())] + for line in text.splitlines() if "{folders}" in line) + text = text.replace("{folders}", textwrap.indent(_plain_folders(name), indent).lstrip()) + return textwrap.indent(text, " ") + "\n\n" def install_command(shell: str, args: str = "") -> str: @@ -254,21 +306,29 @@ def install_command(shell: str, args: str = "") -> str: return f"irm {INSTALL_BASE}/install.ps1 | iex" -def _fence(shell: str, command: str, indent: str) -> list[str]: - return [f"{indent}```{shell}", f"{indent}{command}", f"{indent}```"] +# The title over each command block, by the shell it is written for. +FENCE_TITLES = {"sh": "Terminal", "powershell": "PowerShell"} + + +def fence(shell: str, command: str, indent: str = "") -> list[str]: + """A command block with its title, as every command on the site is shown.""" + return [f'{indent}```{shell} title="{FENCE_TITLES[shell]}"', f"{indent}{command}", f"{indent}```"] def terminal_tabs( tabs: tuple[str, ...], args: dict[str, str], indent: str = "", steps: bool = True, + how: dict[str, str] | None = None, ) -> list[str]: """Content tabs, one per place the command runs, each with how to open the terminal there. Tab labels are shared across the site, so the reader's - choice carries from page to page.""" + choice carries from page to page. *how* replaces the shared text of a tab + for one setup.""" items = [] for key in tabs: - label, how, shell = TERMINALS[key] - body = [how, ""] if steps else [] - items.append((label, body + _fence(shell, install_command(shell, args.get(key, "")), ""))) + label, shared, shell = TERMINALS[key] + text = (how or {}).get(key) or shared + body = [text, ""] if steps else [] + items.append((label, body + fence(shell, install_command(shell, args.get(key, ""))))) return markup.tabs(items, indent) @@ -287,7 +347,7 @@ def platform_args(name: str, guide: Guide) -> dict[str, str]: def trust_box(prefix: str, indent: str = "") -> list[str]: - """Is it safe, under every command on a guide page: five plain facts.""" + """Is it safe, under every command on a guide page: the trust items.""" return markup.details("note", "Is it safe?", [ *(f"- {item}" for item in TRUST_ITEMS), "", diff --git a/scripts/sitemarkup.py b/scripts/sitemarkup.py index 7218e8806..207636bfd 100644 --- a/scripts/sitemarkup.py +++ b/scripts/sitemarkup.py @@ -184,6 +184,37 @@ def card_grid(cards: list[tuple[str, list[str]]]) -> list[str]: return [*lines, "", ""] +def initials(name: str) -> str: + """Up to two capitals of a name, for a setup that has no logo: MyMinUI is MM.""" + capitals = [c for c in name if c.isupper()] + return "".join(capitals[:2]) or name[:2].upper() + + +def _tile_mark(name: str, logo: str) -> str: + if logo: + return f'' + # The same square as a logo, drawn on white like the logos are. + return ('') + + +def platform_tiles(tiles: list[tuple[str, str, str, str]]) -> list[str]: + """One link per setup: its logo (or its initials), its name and where it + runs. *tiles* are (name, target, logo, runs_on). + + One HTML line: a grid of links is a menu, not prose, and the Markdown + around it reads none of it.""" + links = "".join( + f'' + f"{_tile_mark(name, logo)}" + f'{html.escape(name)}' + f'{inline_html(runs_on)}' + for name, target, logo, runs_on in tiles + ) + return [f'
{links}
', ""] + + def steps(items: list[str]) -> list[str]: """An ordered list drawn as numbered steps.""" return [block_open("div", 'class="rb-steps"'), "",