chore: merge the device and emulator screens

This commit is contained in:
Abdessamad Derraz committed 2026-11-11 11:10:44 +01:00
commit 0addf9055e
50 files changed
+2703 -602

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@
# and Invoke-WebRequest throws on a cut transfer before the hash is read:
# the site's build rewrites this line there (scripts/sitebootstrap.py).
$mismatchAdvice = "The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that."
$defaultInstallSha256 = "37442a7c0d307b0b95a40d7630cf57025a6498fea6223076892371b2d6363147"
$defaultInstallSha256 = "67036bbe4db401de213d9a8ab44161b8b64bfde91834b438fff47802b5a461aa"
# The pin above refuses any changed byte: this cap only stops a body that
# cannot be the installer (a sign-in portal's page) before it is hashed.
$maximumInstallerBytes = 8MB
+490 -74
View File
@@ -4734,7 +4734,8 @@ def local_targets(plat: str) -> "dict | None":
sys.exit(1)
def fetch_targets(plat: str) -> "dict | None":
def fetch_targets(plat: str, attempts: int = MAX_RETRIES,
timeout: float | None = None) -> "dict | None":
"""The target core list: the one this run has at hand, else downloaded.
Empty on 404, where the platform publishes no targets. None when the list
@@ -4757,7 +4758,8 @@ def fetch_targets(plat: str) -> "dict | None":
sys.exit(2)
url = TARGETS_URL.format(platform=plat)
try:
return _validate_targets(_fetch_json(url, MAX_TARGETS_BYTES, "target list"))
return _validate_targets(_fetch_json(url, MAX_TARGETS_BYTES, "target list",
attempts=attempts, timeout=timeout))
except urllib.error.HTTPError as exc:
if exc.code == 404:
return {}
@@ -12443,7 +12445,12 @@ class PackSource:
self.close()
raise ValueError(f"{path.name} cannot be read as a ZIP ({exc}). A download "
"cut short gives this: download it again.") from exc
self.listed = self._pack_list()
try:
self.listed = self._pack_list()
except ValueError:
# A ZIP that is no pack: its archives closed, not left open.
self.close()
raise
self.by_sha1 = {f["sha1"]: f["path"] for f in self.listed}
@staticmethod
@@ -18622,6 +18629,22 @@ class TuiState:
# Opened on the plan of the one setup found: its Change where row
# goes back to the setups.
self.opened_on_plan: bool = False
# The device chosen on Device (--target, "" for the board the run
# reads itself) or every board (--every-board), the emulators
# ticked on Emulators (--emulator), and the packs read in place of
# the network (--from-pack).
self.target: str = ""
self.every_board: bool = False
self.emulators: list[str] = []
self.packs: list[Path] = []
# The platform's board list, as fetched for Device: None until it
# comes or when it could not be read, and why.
self.targets: dict | None = None
self.targets_for: str = ""
self.targets_problem: str = ""
# (platform, folder, board, applied, here) of the last board event:
# kept across checks, a --target chosen since stops the event.
self.board_seen: tuple | None = None
@property
def screen(self) -> str:
@@ -19064,6 +19087,12 @@ def _tui_system_flags(state: TuiState) -> list[tuple[str, str | None]]:
def tui_flags(state: TuiState) -> list[tuple[str, str | None]]:
"""The options that carry the choices made on screen, CLI ones included."""
flags = list(state.passthrough)
if state.target:
flags.append(("--target", state.target))
if state.every_board:
flags.append(("--every-board", None))
flags += [("--emulator", name) for name in state.emulators]
flags += [("--from-pack", str(pack)) for pack in state.packs]
flags += _tui_system_flags(state)
flags += [("--core", name) for name in tui_core_flags(state)]
if state.regions:
@@ -19202,7 +19231,7 @@ class TuiRow(NamedTuple):
# Screens that scroll text instead of listing rows.
_PAGERS = ("preview", "command", "result", "misplaced", "unreadable", "games", "details")
_CHECKLISTS = ("systems", "cores")
_CHECKLISTS = ("systems", "cores", "emulators")
def _needed(row: dict) -> int:
@@ -19476,6 +19505,14 @@ def tui_rows(state: TuiState) -> list[TuiRow]:
return _system_rows(state)
if screen == "options":
return _option_rows(state)
if screen == "device":
return _device_rows(state)
if screen == "emulators":
needle = state.filters.get(screen, "")
return [_filter_row(state)] + [
TuiRow("emulator", f"[{'x' if name in state.emulators else ' '}] {name}",
value=name) for name in tui_emulators(state) if _matches(name, needle)
] + _REGIONS_END
if screen == "regions":
known = sorted(set(tui_choices(state)["regions"]) | set(state.cli_regions),
key=region_title)
@@ -19580,10 +19617,10 @@ def _plan_rows(state: TuiState) -> list[TuiRow]:
rows = [TuiRow("systems", TUI_SYSTEMS_ROW)]
game_data = _game_data_row(state)
return rows + ([game_data] if game_data is not None else []) + [
*_details_row(state), TuiRow("options", "Options"), TuiRow("quit", "Quit")]
*_details_row(state), TuiRow("options", "Change"), TuiRow("quit", "Quit")]
if not (_needed(check) if check else 0) and not check.get("data_missing"):
return [TuiRow("close", "Close"), *_games_rows(state), *_record_rows(state),
*_where_rows(state), *_details_row(state), TuiRow("options", "Options")]
*_where_rows(state), *_details_row(state), TuiRow("options", "Change")]
# The size the headline says: "Install 6.6 GB" stood under "14930 files
# to download, 6.8 GB", the copies counted in one and the data files in
# the other.
@@ -19639,7 +19676,7 @@ def _plan_rows(state: TuiState) -> list[TuiRow]:
rows.append(TuiRow("misplaced", "Show the files in the wrong place"))
if state.unreadable:
rows.append(TuiRow("unreadable", "Show the files that could not be read"))
return rows + [*_where_rows(state), *_details_row(state), TuiRow("options", "Options"),
return rows + [*_where_rows(state), *_details_row(state), TuiRow("options", "Change"),
TuiRow("quit", "Quit")]
@@ -19904,8 +19941,11 @@ _REGIONS_END = [TuiRow("none", "None"), TuiRow("done", "Done")]
def _first_item(state: TuiState) -> int:
"""The row a list of ticks opens on: its first item."""
return next((i for i, row in enumerate(tui_rows(state)) if row.action in TUI_TICK_ROWS), 0)
"""The row a list of ticks opens on: its first item. Elsewhere the
first row that is no heading."""
rows = tui_rows(state)
return next((i for i, row in enumerate(rows) if row.action in TUI_TICK_ROWS),
next((i for i, row in enumerate(rows) if row.action != "heading"), 0))
def _shared_needed(state: TuiState) -> dict[str, tuple[int, int]]:
@@ -19930,56 +19970,170 @@ def _yes(value: bool) -> str:
return "yes" if value else "no"
# The Change screen's groups, in the order they are drawn: a heading is no
# row Up and Down stop on, and a group with no row is not drawn.
TUI_CHANGE_GROUPS = ("What to install", "Where", "For which device", "How", "See")
def _option_rows(state: TuiState) -> list[TuiRow]:
"""The Change screen: every option one level below the plan, grouped by
what it decides, each row with its value and its flag."""
manifest = state.manifest or {}
choices = tui_choices(state)
rows = []
what: list[TuiRow] = []
if choices["regions"]:
rows.append(TuiRow("regions", "Preferred regions: "
what.append(TuiRow("regions", "Preferred regions: "
+ (", ".join(map(region_title, state.regions)) or "any"),
"--region"))
if manifest.get("categorized"):
# "Leave out game data: no" was a double negative.
rows.append(TuiRow("game_data", "Game data: "
+ ("left out" if state.no_game_data else "kept"),
"--no-game-data"))
found = len(state.facts.get("cores", []))
if _cores_dir_given(state):
rows.append(TuiRow("installed", "Installed cores only: yes, implied by --cores-dir",
"--cores-dir"))
elif state.platform in CORE_PLATFORMS and "system_cores" in manifest and found:
rows.append(TuiRow("installed", f"Installed cores only: "
f"{_yes(state.installed_only)} ({found} found)",
"--installed-cores"))
if len(choices["cores"]) > 1:
# The plan's words, the noun left to the row's own label. A list
# with one core asks nothing, as line mode does not.
rows.append(TuiRow("cores", "Cores: " + cores_words(
what.append(TuiRow("cores", "Cores: " + cores_words(
tui_cores_ticked(state), len(_core_base(state))).replace(" cores", ""), "--core"))
if "system_cores" in manifest and tui_emulators(state):
what.append(TuiRow("emulators", f"Emulators: {_emulator_words(state)}",
"--emulator"))
if manifest.get("categorized"):
# "Leave out game data: no" was a double negative.
what.append(TuiRow("game_data", "Game data: "
+ ("left out" if state.no_game_data else "kept"),
"--no-game-data"))
copied = ", ".join(str(folder) for folder in state.from_folders) or "none"
if manifest.get("save_directory"):
# The interface said "Name it with --save-dir" and had no row for it.
rows.append(TuiRow("savedir", f"Save folder: {_save_folder_words(state)}",
"--save-dir", style="path"))
if not LOCAL.offline:
# From a pack, or offline, nothing is downloaded.
rows.append(TuiRow("jobs", f"Parallel downloads: {state.jobs}", "--jobs"))
rows += [
where = [
# The plan's folder: AmberELEC's read the games part here and its
# bios folder on the plan, two values under one name. --dest stays the
# games part, as the command line takes it.
TuiRow("folder", "BIOS folder: "
+ _short_path(_plan_folder(state, state.dest), state.home),
"--dest", style="path"),
TuiRow("from", f"Copy from: {copied}", "--from", style="path"),
*([TuiRow("addapp", f"Put RetroBIOS on this card: {_yes(state.add_app)}",
"--add-app")] if _tui_app_ready(state) else []),
TuiRow("preview", "Preview the file list", "--dry-run"),
TuiRow("command", "Show the command line"),
]
if manifest.get("save_directory"):
# The interface said "Name it with --save-dir" and had no row for it.
where.append(TuiRow("savedir", f"Save folder: {_save_folder_words(state)}",
"--save-dir", style="path"))
where += [TuiRow("from", f"Copy from: {copied}", "--from", style="path"),
TuiRow("pack", "From a pack: "
+ (state.packs[0].name if state.packs else "none"), "--from-pack")]
device: list[TuiRow] = []
if state.platform in TARGET_PLATFORMS:
device.append(TuiRow("device", f"Device: {_device_words(state)}", "--target"))
found = len(state.facts.get("cores", []))
if _cores_dir_given(state):
device.append(TuiRow("installed", "Installed cores only: yes, implied by --cores-dir",
"--cores-dir"))
elif state.platform in CORE_PLATFORMS and "system_cores" in manifest and found:
device.append(TuiRow("installed", f"Installed cores only: "
f"{_yes(state.installed_only)} ({found} found)",
"--installed-cores"))
if _tui_app_ready(state):
device.append(TuiRow("addapp", f"Put RetroBIOS on this card: {_yes(state.add_app)}",
"--add-app"))
# Parallel downloads below: the row goes once nothing is downloaded,
# and the focus stays on the row just changed.
how = [TuiRow("offline", "Without the internet: "
+ ("yes, a pack is read" if LOCAL.pack is not None
else _yes(offline_requested())), "--offline")]
if not LOCAL.offline:
# From a pack, or offline, nothing is downloaded.
how.append(TuiRow("jobs", f"Parallel downloads: {state.jobs}", "--jobs"))
see = [TuiRow("preview", "Preview the file list", "--dry-run"),
TuiRow("command", "Show the command line")]
rows: list[TuiRow] = []
for heading, group in zip(TUI_CHANGE_GROUPS, (what, where, device, how, see)):
if group:
rows += [TuiRow("heading", heading, style="head"), *group]
return rows
def tui_emulators(state: TuiState) -> list[str]:
"""The emulators Emulators lists: each profile once, by the name the
list gives it, as --list-emulators names them."""
if state.manifest is None:
return []
return sorted(set(emulator_names(state.manifest, *tui_run_files(state)).values()))
def _emulator_words(state: TuiState) -> str:
"""The emulators chosen, as the Change row says them."""
if not state.emulators:
return "all"
if len(state.emulators) <= 2:
return ", ".join(state.emulators)
return f"{len(state.emulators)} chosen"
def _board_seen(state: TuiState) -> tuple[str, bool, bool] | None:
"""(board, applied, here) the last check read for this setup and folder."""
seen = state.board_seen
if seen is None or seen[:2] != (state.platform, state.dest):
return None
return seen[2], seen[3], seen[4]
def _board_where(here: bool) -> str:
return "this device" if here else "named by the card"
def _device_choice(state: TuiState) -> str:
"""The board the runs install for, "" for every board."""
if state.every_board:
return ""
if state.target:
return state.target
seen = _board_seen(state)
return seen[0] if seen is not None and seen[1] else ""
def _device_words(state: TuiState) -> str:
"""The device as the Change row says it: the board, where the run read
it when it chose it itself."""
chosen = _device_choice(state)
if not chosen:
return "every board"
seen = _board_seen(state)
if not state.target and seen is not None:
return f"{chosen}, {_board_where(seen[2])}"
return chosen
def _device_rows(state: TuiState) -> list[TuiRow]:
"""Device: the platform's boards by the names its list gives them, the
one read here or named by the card first, then every board."""
targets = state.targets if state.targets_for == state.platform else None
if not targets:
return []
seen = _board_seen(state)
chosen = _device_choice(state)
names = sorted(targets)
if seen is not None and seen[0] in targets:
names.remove(seen[0])
names.insert(0, seen[0])
rows = []
for name in names:
images = [str(image) for image in targets[name].get("images") or ()]
# The device names an image carries say what a board code means.
right = (_board_where(seen[2]) if seen is not None and name == seen[0]
else ", ".join(images))
narrow = (images[0] + (f" +{len(images) - 1}" if len(images) > 1 else ""),) \
if images and right != images[0] and name != (seen or ("",))[0] else ()
rows.append(TuiRow("board", f"({'x' if name == chosen else ' '}) {name}", right,
name, narrow=narrow))
rows.append(TuiRow("board", f"({' ' if chosen else 'x'}) Every board", value=""))
return rows
def _choose_board(state: TuiState, name: str) -> list[tuple]:
"""A board chosen on Device, back on Change. The board the run reads
itself is no option: the command stays the one with nothing chosen.
Every board is always said, the run otherwise reading a board itself
(one started with --target or --every-board read none)."""
seen = _board_seen(state)
read = seen[0] if seen is not None and seen[1] else ""
state.every_board = not name
state.target = "" if not name or name == read else name
return _back(state)
def _save_dir_given(state: TuiState) -> Path | None:
return next((Path(value) for flag, value in state.passthrough
if flag == "--save-dir" and value), None)
@@ -20183,15 +20337,19 @@ def _tui_record_take(state: TuiState) -> None:
state.games_folder = parsed.for_games if isinstance(parsed.for_games, Path) else None
state.required_only = bool(parsed.required_only) and state.for_games
state.games_choice = "record"
# Device and Emulators show these: a choice no row showed was kept apart.
state.target = str(parsed.target or "")
state.every_board = bool(parsed.every_board)
state.emulators = list(parsed.emulator or [])
state.record_flags = [(flag, value) for flag, value in record_options(setup["choice"])
if flag in ("--target", "--emulator", "--every-board",
"--declared-only")]
if flag == "--declared-only"]
def _tui_record_drop(state: TuiState) -> None:
"""The recorded choice given up: the choice nothing narrows."""
state.cli_systems, state.cores, state.regions, state.record_flags = [], [], [], []
state.no_game_data = state.installed_only = state.required_only = False
state.target, state.every_board, state.emulators = "", False, []
state.for_games, state.games_folder, state.games_choice = None, None, ""
# The systems of the recorded check go, and with them its ticks.
state.systems, state.ticked, state.keep_ticked, state.widened_from = [], set(), None, None
@@ -20263,6 +20421,37 @@ def _closed_status(state: TuiState, closed: tuple[Path, str]) -> TuiLine:
"or give this user access to it.", "refusal")
def _set_platform(state: TuiState, plat: str) -> None:
"""Another setup chosen: its list names other boards and emulators, so
the ones chosen for the setup before are not carried over."""
if state.platform and plat != state.platform:
state.target, state.every_board, state.emulators = "", False, []
state.targets, state.targets_for, state.targets_problem = None, "", ""
state.platform = plat
def _known_choice(state: TuiState, plat: str, manifest: dict) -> None:
"""The emulators and the board chosen, kept to those the list now read
names, the others left out and said in one line: a pack's list can
lack what the network's held, and the check then failed."""
names = emulator_names(manifest, manifest.get("files") or [],
manifest.get("omitted_files") or [])
gone = [f"--emulator {name}" for name in state.emulators if name not in names]
state.emulators = [name for name in state.emulators if name in names]
boards = state.targets if state.targets_for == plat else None
if LOCAL.manifest is not None or LOCAL.pack is not None:
try:
boards = local_targets(plat)
except SystemExit:
# A board list beside the list that is no list: the check says so.
boards = None
if state.target and boards is not None and state.target not in boards:
gone.append(f"--target {state.target}")
state.target = ""
if gone:
state.status = f"Left out, not in this list: {', '.join(gone)}."
def _choose(state: TuiState, plat: str, dest: Path, proposed: bool = False) -> list[tuple]:
"""dest taken for plat. proposed is a folder the installer offered (its
usual or detected folder, a share drive's bios): created without the
@@ -20273,7 +20462,8 @@ def _choose(state: TuiState, plat: str, dest: Path, proposed: bool = False) -> l
if closed is not None:
# Nothing can be checked or written there: the folder is asked
# again, on the screen that chose it when it can say why.
state.platform, state.status = plat, _closed_status(state, closed)
_set_platform(state, plat)
state.status = _closed_status(state, closed)
if state.stack and state.screen in ("folder", "browse", "path"):
return []
# The folder found here is the one the frontend reads: the built-in
@@ -20285,7 +20475,8 @@ def _choose(state: TuiState, plat: str, dest: Path, proposed: bool = False) -> l
if card and card[0] == plat and card[1] != dest:
# A muOS or KNULLI card's root: that system reads a folder below it.
dest = card[1]
state.platform, state.dest = plat, dest
_set_platform(state, plat)
state.dest = dest
if plat in CARD_PLATFORMS and card_platform(dest) is None:
# The engine refuses a folder inside the card, the list writing
# Bios/<TAG>/ below the root: the root is offered instead.
@@ -20575,10 +20766,13 @@ def _os_folder(path: Path, folder: Path) -> bool:
def _browse_to(state: TuiState, folder: Path | None, came_from: Path | None = None) -> None:
state.browse_dir = folder
copying = state.browse_for == "from"
state.browse_rows, state.status = _browse_rows(folder, copying,
state.browse_rows, state.status = _browse_rows(folder, copying or state.browse_for == "pack",
_dest_card(state) if copying else None)
if state.browse_for == "pack":
# A pack is a ZIP, never a folder.
state.browse_rows = [row for row in state.browse_rows if row.action != "use"]
focus = 0
if (folder is not None and state.browse_for != "from"
if (folder is not None and state.browse_for not in ("from", "pack")
and folder in (state.home, Path(folder.anchor))):
# No frontend reads its BIOS files from HOME or a drive's root: A
# pressed through the screens installed into ~. A goes into a
@@ -20719,7 +20913,7 @@ def _activate(state: TuiState, row: TuiRow) -> list[tuple]:
# Taken as the system chosen, as --platform with --dest takes it.
plat, mount = row.value
state.card_bound = card_bound(plat, plat)
state.platform = plat
_set_platform(state, plat)
choice = card_choice(plat, plat, mount)
if choice.refused:
_card_status(state, choice)
@@ -20732,7 +20926,7 @@ def _activate(state: TuiState, row: TuiRow) -> list[tuple]:
return _choose(state, plat, path, proposed=True)
if screen == "platforms":
plat, system = row.value if action == "cardsystem" else (row.value, row.value)
state.platform = plat
_set_platform(state, plat)
state.card_bound = card_bound(system, plat)
if state.dest_given is not None:
# Read as --platform with --dest reads it, for the same folder:
@@ -20791,6 +20985,9 @@ def _activate(state: TuiState, row: TuiRow) -> list[tuple]:
state.path_selected = bool(state.path_text)
return _push(state, "path")
if screen == "browse":
if action == "zip" and state.browse_for == "pack":
# Opened in this process (perform), which answers pack_opened.
return [("pack", row.value)]
if action == "use" and state.browse_for == "save":
state.passthrough = [flag for flag in state.passthrough
if flag[0] != "--save-dir"]
@@ -20913,6 +21110,8 @@ def _activate(state: TuiState, row: TuiRow) -> list[tuple]:
return _checklist(state, row)
if screen == "options":
return _option(state, action, 0)
if screen == "device" and action == "board":
return _choose_board(state, str(row.value))
if screen == "confirm":
if action == "install":
state.stack.pop()
@@ -20959,6 +21158,10 @@ def _checklist(state: TuiState, row: TuiRow) -> list[tuple]:
state.cores, state.no_cores = [], False
elif action == "none" and screen == "regions":
state.regions = []
elif action == "none" and screen == "emulators":
# None ticked installs for every emulator, as Preferred regions keeps
# every region with none ticked.
state.emulators = []
elif action == "none":
if screen == "systems":
state.ticked = set()
@@ -21437,6 +21640,30 @@ def _option(state: TuiState, action: str, step: int) -> list[tuple]:
state.jobs = max(1, min(32, state.jobs + (step or 1)))
elif action == "addapp":
state.add_app = step > 0 if step else not state.add_app
elif action == "offline" and LOCAL.pack is None:
on = step > 0 if step else not offline_requested()
if (on and state.target and not state.manifest_given and not state.packs
and not (state.targets_for == state.platform and state.targets)):
# The run reads the board list beside the file list it is
# given, written only once Device has read it: refused before
# a check that would stop on it.
state.status = _styled("Open Device once first: without the internet the "
"board list has to be read already.", "refusal")
return []
# Set in this process by the effect, as --offline sets it: the
# runs started next inherit it, and their command names it.
return [("offline", on)]
elif action == "device":
_push(state, "device")
state.focus["device"] = _device_focus(state)
if state.targets_for != state.platform:
state.targets, state.targets_problem = None, ""
return [("targets", state.platform)]
elif action == "emulators":
_push(state, action)
state.focus[action] = _first_item(state)
elif action == "pack":
return _open_pack_browser(state)
elif action == "cores":
# On the first core: the filter row under A opened a field a
# controller cannot type into.
@@ -21468,6 +21695,59 @@ def _option(state: TuiState, action: str, step: int) -> list[tuple]:
return []
def _device_focus(state: TuiState) -> int:
"""Device opens on the board chosen: the one read here or named by
the card when nothing else was."""
rows = _device_rows(state)
chosen = _device_choice(state)
return next((index for index, row in enumerate(rows) if row.value == chosen), 0)
def _open_pack_browser(state: TuiState) -> list[tuple]:
"""The browser, to name a pack downloaded before: it opens where a
download lands."""
state.browse_for = "pack"
_push(state, "browse")
downloads = state.home / "Downloads"
start = (state.packs[0].parent if state.packs
else downloads if _is_dir(downloads) else state.home)
_browse_to(state, _nearest_folder(start))
return []
def open_tui_pack(path: Path, plat: str) -> tuple[PackSource | None, str]:
"""The pack the screen chose, opened as --from-pack opens it, and why
it is refused, "" when it is not. The run reads it only once the screen
takes it (use_tui_pack): a Back before then leaves the run as it was."""
try:
pack = PackSource([path])
except ValueError as exc:
return None, str(exc)
spelled = pack.platforms()
if spelled and plat not in spelled:
pack.close()
return None, (f"{pack.name} is a pack for "
f"{' and '.join(display_name(name) for name in spelled)}, "
f"not {display_name(plat)}.")
return pack, ""
def use_tui_pack(pack: PackSource) -> None:
"""The pack the screen took, read by this run in place of the network."""
if LOCAL.pack is not None and LOCAL.pack is not pack:
LOCAL.pack.close()
LOCAL.pack = pack
def set_offline(on: bool) -> None:
"""Without the internet on or off for this process and the runs it
starts, as --offline sets it."""
if on:
os.environ[OFFLINE_VARIABLE] = OFFLINE_FLAG_VALUE
else:
os.environ.pop(OFFLINE_VARIABLE, None)
def _open_from_browser(state: TuiState) -> list[tuple]:
"""The browser, to name an old card, a backup or a ZIP to copy from."""
state.browse_for = "from"
@@ -21546,10 +21826,18 @@ def _tick(state: TuiState, row: TuiRow, on: bool | None) -> None:
chosen = state.ticked
elif state.screen == "cores" and row.action == "core":
chosen = {name for name in tui_choices(state)["cores"] if core_ticked(state, name)}
elif state.screen == "emulators" and row.action == "emulator":
chosen = set(state.emulators)
else:
return
value = row.value
want = (value not in chosen) if on is None else on
if state.screen == "emulators":
if want and value not in state.emulators:
state.emulators.append(str(value))
elif not want and value in state.emulators:
state.emulators.remove(str(value))
return
if state.screen == "systems":
if want:
state.ticked.add(value)
@@ -21793,7 +22081,9 @@ def tui_update(state: TuiState, key: TuiKey) -> list[tuple]:
elif name in ("esc", "backspace"):
return _back(state)
elif (name == "tab" or char == "o") and screen in ("plan", "systems", "confirm"):
return _push(state, "options")
_push(state, "options")
state.focus["options"] = _first_item(state)
return []
elif char == "/" and any(item.action == "filter" for item in rows):
# The focus goes to the field typed into: it stayed on None, and the
# bar named None's keys while letters went into the filter.
@@ -21808,7 +22098,7 @@ def tui_update(state: TuiState, key: TuiKey) -> list[tuple]:
elif screen == "regions" and name in ("right", "shift-down"):
_move_region(state, row.value, 1)
return []
elif screen in _CHECKLISTS and row.action in ("system", "core") and (
elif screen in _CHECKLISTS and row.action in ("system", "core", "emulator") and (
char == " " or name in ("right", "left")):
_tick(state, row, None if char == " " else name == "right")
return []
@@ -21822,12 +22112,25 @@ def tui_update(state: TuiState, key: TuiKey) -> list[tuple]:
return _checklist(state, named[0]) if named else []
elif screen == "options" and name in ("left", "right"):
return _option(state, row.action, 1 if name == "right" else -1) \
if row.action in ("game_data", "installed", "jobs", "addapp") \
if row.action in ("game_data", "installed", "jobs", "addapp", "offline") \
or (row.action == "from" and name == "left") else []
elif char == " ":
return _space(state, row)
elif name == "enter":
return _activate(state, row)
if rows[focus].action == "heading":
# A heading names the rows under it and is never in focus: the
# move goes on to the next row its way, else stays where it was.
step = -1 if name in ("up", "pgup", "end") or char in ("k", "G") else 1
moved = name in ("up", "down") or char in ("k", "j")
if not moved and focus in (0, last):
# A page or Home clamped to an end: the first row from that end.
step = 1 if focus == 0 else -1
order = list(range(focus, -1, -1) if step < 0 else range(focus, len(rows)))
if wrap and moved:
order += range(last, focus, -1) if step < 0 else range(0, focus)
focus = next((index for index in order if rows[index].action != "heading"),
max(0, min(state.focus.get(screen, 0), last)))
state.focus[screen] = focus
return []
@@ -21906,6 +22209,8 @@ def tui_mouse(state: TuiState, event: TuiMouse) -> list[tuple]:
screen = state.screen
row = rows[index]
seen = (screen, len(state.stack), row.action, row.value)
if row.action == "heading":
return []
if state.editing:
state.editing = ""
elif row.action in TUI_TICK_ROWS:
@@ -21929,7 +22234,7 @@ def tui_mouse(state: TuiState, event: TuiMouse) -> list[tuple]:
# The rows a tap ticks or unticks, as Space does: the lists of ticks.
TUI_TICK_ROWS = ("system", "core", "region")
TUI_TICK_ROWS = ("system", "core", "region", "emulator")
def _bar_commits(state: TuiState, key: TuiKey) -> bool:
@@ -22069,7 +22374,12 @@ def _wheel(state: TuiState, hits: TuiHits, step: int) -> list[tuple]:
state.list_top[screen] = top
rows = tui_rows(state)
focus = max(0, min(state.focus.get(screen, 0), len(rows) - 1))
state.focus[screen] = max(top, min(focus, top + shown - 1))
focus = max(top, min(focus, top + shown - 1))
if rows and rows[focus].action == "heading":
# Never on a heading: the row under it, else the one above.
below = focus + 1 < min(len(rows), top + shown)
focus = focus + 1 if below or focus == 0 else focus - 1
state.focus[screen] = focus
elif hits.scroll == "error":
state.focus["error_text"] = max(0, state.focus.get("error_text", 0) + lines)
elif hits.scroll == "pager":
@@ -22170,6 +22480,10 @@ def _take_event(state: TuiState, event: dict) -> None:
state.set_aside = set(event["dests"])
elif kind == "board" and state.run == "check":
state.board = str(event["message"])
# Read on the device itself, or named by the card in a reader: a
# computer preparing a card has no board of its own.
state.board_seen = (state.platform, state.dest, str(event["board"]),
bool(event["applied"]), detect_board(state.platform) is not None)
elif kind == "version" and state.run == "check":
state.version = str(event["message"])
state.version_short = (f"List for {display_name(state.platform, state.manifest)} "
@@ -22236,9 +22550,37 @@ def tui_receive(state: TuiState, message: tuple) -> list[tuple]:
if plat != state.platform or state.screen != "checking":
return []
state.manifest, state.manifest_for = manifest, plat
_known_choice(state, plat, manifest)
if state.games_probed is None:
state.games_probed = (plat, state.dest)
return _start_run(state, "check")
if kind == "targets":
_kind, plat, targets, problem = message
if plat == state.platform:
# Held only once read: a list that could not be read is fetched
# again the next time Device opens.
state.targets, state.targets_problem = targets, problem
state.targets_for = plat if targets is not None else ""
if state.screen == "device":
state.focus["device"] = _device_focus(state)
return []
if kind == "pack_opened":
_kind, path, pack, problem = message
if state.screen != "browse" or state.browse_for != "pack":
# Left before it opened: never read, closed.
return [("close_pack", pack)] if pack is not None else []
if problem:
state.status = _styled(problem, "refusal")
return []
# The pack's own list, as --from-pack reads it: the list fetched
# before is fetched again from the pack.
state.packs = [path]
state.browse_for = "dest"
state.manifest, state.manifest_for = None, ""
if not state.manifest_given:
state.manifest_path = None
state.targets_for = ""
return [("use_pack", pack), *_start_check(state)]
if kind == "app":
# Put from the result screen, in this process.
state.app, state.app_running = message[1], False
@@ -22414,6 +22756,8 @@ _ROW_VERBS = {
"cardsystem": "Choose", "rescan": "Look again", "elsewhere": "Choose", "drive": "Choose",
"recheck": "Check again", "gamedata": "Change", "share": "Choose", "morecores": "Show",
"games": "Change", "startonly": "Change", "gameslist": "Show", "details": "Show",
"device": "Open", "emulators": "Open", "emulator": "Tick", "board": "Choose",
"pack": "Choose", "offline": "Change",
}
@@ -22423,6 +22767,9 @@ TUI_ROW_SPREAD = 78
def _row_line(row: TuiRow, focused: bool, width: int) -> TuiLine:
if row.action == "heading":
# Flush left of the rows it names, never in focus.
return _styled(fit(row.label, width), row.style)
left = ("> " if focused else " ") + row.label
if row.align and (row.right or row.narrow or row.cut):
# One column for the list: the longest form that fits after it, a
@@ -22901,7 +23248,7 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list:
lines += _unavailable_brief(state) + _details(_unavailable_lines(state))
if check is state.check:
lines += _unplaced_lines(_choice_summary(state), state,
"Choose it in Options, Save folder (--save-dir).")
"Choose it in Change, Save folder (--save-dir).")
if check is state.check:
lines += _found_lines(state)
if check is state.check and state.from_folders:
@@ -23193,10 +23540,13 @@ def _view_browse(state: TuiState, width: int, height: int) -> tuple:
elif state.browse_for == "save":
hint = [f"Choose {display_name(state.platform)}'s save directory: some "
"cores read files there."]
elif state.browse_for == "pack":
hint = ["Choose the pack ZIP downloaded before: nothing is downloaded then."]
# The hint is kept whole under the list, which scrolls: three folders at
# 40x12 cut it to "Choose the old card, a backup folder".
return ("Copy from" if state.browse_for == "from"
else "Save folder" if state.browse_for == "save" else "Browse for a folder",
else "Save folder" if state.browse_for == "save"
else "From a pack" if state.browse_for == "pack" else "Browse for a folder",
_compose(state, width, height, [where], tui_rows(state), _status_after(state),
hint),
("A/Enter Open", "B/Esc Back", "? Help"))
@@ -23489,7 +23839,7 @@ def _view_plan(state: TuiState, width: int, height: int) -> tuple:
name = display_name(state.platform, state.manifest)
return (f"{system} ({name} files)" if system and system != name else name,
_plan_body(state, width, height, lines, rows),
("A/Enter Install", "B/Esc Back", "Y/o Options", "? Help"))
("A/Enter Install", "B/Esc Back", "Y/o Change", "? Help"))
def _plan_body(state: TuiState, width: int, height: int, lines: list,
@@ -23678,24 +24028,51 @@ def _view_systems(state: TuiState, width: int, height: int) -> tuple:
def _view_options(state: TuiState, width: int, height: int) -> tuple:
rows = tui_rows(state)
before = ["Each row is an option of the command line, named on the right."]
# The headings say what each group decides, the flags on the right
# which option of the command line each row is.
after: list = []
if width < 60:
# A value cut to make room for its flag said nothing: the flag of
# the focused row is named below instead.
focus = rows[min(state.focus.get("options", 0), len(rows) - 1)]
before = []
after = [f"Sets {focus.right}"] if focus.right else []
rows = [row._replace(right="") for row in rows]
# A folder is cut in its middle, its last levels being what tells it apart.
room = width - 4 - (0 if width < 60 else 2 + max(cell_width(r.right) for r in rows))
rows = [_middle_label(row, room) if row.style == "path" else row for row in rows]
if state.status:
after.append(state.status)
return ("Options", _compose(state, width, height, before, rows, after),
# In the lines the rows leave: below them it went once they filled
# the screen.
return ("Change", _compose(state, width, height, [], rows, after, _status_footer(state)),
("A/Enter Change", "B/Esc Back", "Left/Right Set", "? Help"))
def _view_device(state: TuiState, width: int, height: int) -> tuple:
# "Board" is the list's word, glossed once: a beginner knows the device.
pinned = ["Choose the board, the chip of the device the games run on. "
"Every board fits any device."]
rows = tui_rows(state)
if state.targets_problem:
after = [state.targets_problem]
elif state.targets_for != state.platform:
after = ["Reading the board list..."]
elif not rows:
after = [f"{display_name(state.platform)} publishes no board list: every "
"board's files are installed."]
else:
after = []
return ("Device", _compose(state, width, height, [], rows, after,
_status_footer(state), pinned),
("A/Enter Choose", "B/Esc Back", "? Help") if rows else ("B/Esc Back", "? Help"))
def _view_emulators(state: TuiState, width: int, height: int) -> tuple:
pinned = ["Tick the emulators to install for. With none ticked, every "
"emulator's files are installed."]
return ("Emulators", _compose(state, width, height, [], tui_rows(state), [],
_status_footer(state), pinned),
_CHECKLIST_BAR)
def _middle_label(row: TuiRow, room: int) -> TuiRow:
"""A row naming a folder, the folder cut in its middle to fit."""
name, _sep, value = row.label.partition(": ")
@@ -24399,6 +24776,7 @@ _TUI_VIEWS: dict[str, Callable[[TuiState, int, int], tuple]] = {
"installing": _view_installing, "result": _view_result, "error": _view_error,
"quit": _view_quit, "cardroot": _view_cardroot, "misplaced": _view_misplaced,
"unreadable": _view_unreadable, "games": _view_games, "details": _view_details,
"device": _view_device, "emulators": _view_emulators,
}
@@ -24446,22 +24824,22 @@ def _help_lines(state: TuiState) -> list[tuple[str, str]]:
# How a tap acts, before what the list says elsewhere: last, it was the
# line a 40x12 help cut.
keys += _tap_help(state)
if screen in _CHECKLISTS:
if screen in ("systems", "cores"):
keys.append(("a, n", "All, none"))
elif screen == "regions":
elif screen in ("regions", "emulators"):
keys.append(("n", "None"))
if screen == "platforms":
keys.append(("/", "Filter by setup or device name"))
elif screen in ("systems", "cores"):
elif screen in _CHECKLISTS:
keys.append(("/", "Filter"))
if screen in ("platforms", "systems", "cores") and state.filters.get(screen):
if screen in ("platforms", *_CHECKLISTS) and state.filters.get(screen):
keys.append((names["esc"], "Clear the filter"))
if screen == "regions":
keys.append(("Left/Right", "Move a region up or down the order"))
if screen == "options":
keys.append(("Left/Right", "Change a value"))
if screen in ("plan", "systems", "confirm"):
keys.append((names["options"], "Options"))
keys.append((names["options"], "Change"))
if screen in ("plan", "confirm"):
keys.append(("d", "Details"))
elif screen == "result" and not tui_rows(state):
@@ -24510,6 +24888,8 @@ def _primary(state: TuiState) -> str | None:
verb = _ROW_VERBS.get(row.action)
if row.action == "filter" and _only_match(state) is not None:
verb = "Choose"
if row.action == "zip" and state.browse_for == "pack":
verb = "Use this pack"
return f"A/Enter {verb}" if verb else None
@@ -24544,7 +24924,7 @@ def _view_help(state: TuiState, width: int, height: int) -> tuple:
# Cut from its end at 40x12, the help lost Quit and This help with no
# sign: what a list's keys also say elsewhere goes first.
for spare in ("First, last", "All, none", "extra", "Filter by setup or device name",
"Filter", "Options", "Details", "This help", "gap", "definitions"):
"Filter", "Change", "Details", "This help", "gap", "definitions"):
if (len(drawn(keys)) + len(extra) + len(defined)
+ len(_text_lines(more, width)) <= height):
break
@@ -24752,17 +25132,13 @@ def tell_apart(setups: list[tuple]) -> list[tuple]:
def _tui_passthrough(args: argparse.Namespace) -> list[tuple[str, str | None]]:
"""Options the screens do not change, handed to every run unchanged."""
flags: list[tuple[str, str | None]] = []
for flag in ("--target", "--romm-firmware"):
value = getattr(args, flag[2:].replace("-", "_"))
if value is not None:
flags.append((flag, str(value)))
if args.romm_firmware is not None:
flags.append(("--romm-firmware", str(args.romm_firmware)))
for flag in ("--save-dir", "--cores-dir"):
value = getattr(args, flag[2:].replace("-", "_"))
if value is not None:
flags.append((flag, str(Path(value).absolute())))
flags += [("--emulator", name) for name in args.emulator or []]
flags += [("--from-pack", str(Path(pack).absolute())) for pack in args.from_pack or []]
for flag in ("--every-board", "--standalone-copies", "--verbose"):
for flag in ("--standalone-copies", "--verbose"):
if getattr(args, flag[2:].replace("-", "_")):
flags.append((flag, None))
return flags
@@ -24843,6 +25219,10 @@ def tui_start(
state.jobs = args.jobs
state.add_app = bool(getattr(args, "add_app", False))
state.passthrough = _tui_passthrough(args)
state.target = str(args.target or "")
state.every_board = bool(args.every_board)
state.emulators = list(args.emulator or [])
state.packs = [Path(pack).absolute() for pack in args.from_pack or []]
if getattr(args, "for_games", None) is not None:
state.for_games, state.games_choice = True, "cli"
if isinstance(args.for_games, Path):
@@ -25102,6 +25482,23 @@ def _tui_fetch(plat: str, inbox: queue.Queue, log: io.StringIO) -> None:
inbox.put(("manifest_failed", plat, title, lines, 1))
def _tui_fetch_targets(plat: str, inbox: queue.Queue, log: io.StringIO) -> None:
"""The platform's board list for Device, fetched in this process as a
--target run reads it: the pack's or the list's beside --manifest, else
the network's. None and why when it cannot be read."""
mark = len(log.getvalue())
try:
# One attempt and the list's own timeout, as the file list is
# fetched here: Device says Reading while it waits.
targets = fetch_targets(plat, attempts=1, timeout=TUI_FETCH_TIMEOUT)
except SystemExit:
targets = None
said = [line.strip() for line in log.getvalue()[mark:].splitlines() if line.strip()]
problem = "" if targets is not None else (
f"The board list could not be read{': ' + said[0] if said else '.'}")
inbox.put(("targets", plat, targets, problem))
def _tui_fetch_games(plat: str, inbox: queue.Queue, log: io.StringIO, dest: Path | None,
look: bool) -> None:
"""The file list, then, once it came, a look at the card for games
@@ -25405,7 +25802,7 @@ ROMM_SCAN_WORDS = ("RomM lists the firmware its library scan has recorded: start
# The result's remedy for the save files left out, as the terminal says it
# once the screen is gone: Options are not there to choose from
# Line mode's words.
TUI_SAVE_REMEDY = "Choose it in Options, Save folder (--save-dir), next time."
TUI_SAVE_REMEDY = "Choose it in Change, Save folder (--save-dir), next time."
LINE_SAVE_REMEDY = "Name it with --save-dir next time."
@@ -25533,12 +25930,19 @@ def run_tui(args: argparse.Namespace) -> int:
for run in current:
run.stop(0)
current.clear()
if not state.manifest_given and not args.from_pack and state.manifest:
if not state.manifest_given and not state.packs and state.manifest:
if state.platform not in written:
path = work / f"{state.platform}.json"
path.write_text(json.dumps(state.manifest), encoding="utf-8")
written[state.platform] = path
state.manifest_path = written[state.platform]
if state.targets_for == state.platform and state.targets:
# Beside the list, where a --target run without the
# internet reads the boards (local_targets).
boards = work / "targets" / f"{state.platform}.json"
boards.parent.mkdir(exist_ok=True)
boards.write_text(json.dumps(state.targets), encoding="utf-8")
written[f"targets:{state.platform}"] = boards
log = work / f"{effect[1]}.log"
if effect[1] == "install":
state.log = str(log)
@@ -25561,6 +25965,18 @@ def run_tui(args: argparse.Namespace) -> int:
elif kind == "app":
threading.Thread(target=_tui_put_app, args=(effect[1], inbox),
daemon=True).start()
elif kind == "targets":
threading.Thread(target=_tui_fetch_targets, args=(effect[1], inbox, buffer),
daemon=True).start()
elif kind == "pack":
inbox.put(("pack_opened", effect[1],
*open_tui_pack(effect[1], state.platform)))
elif kind == "use_pack":
use_tui_pack(effect[1])
elif kind == "close_pack":
effect[1].close()
elif kind == "offline":
set_offline(effect[1])
def on_interrupt(_signum: int, _frame: object) -> None:
flags["interrupt"] = True
+1 -1
View File
@@ -23,7 +23,7 @@ DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/ins
# curl or wget stop on a cut transfer before the hash is read: the site's
# build rewrites this line there (scripts/sitebootstrap.py).
MISMATCH_ADVICE="The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that."
DEFAULT_INSTALL_SHA256="37442a7c0d307b0b95a40d7630cf57025a6498fea6223076892371b2d6363147"
DEFAULT_INSTALL_SHA256="67036bbe4db401de213d9a8ab44161b8b64bfde91834b438fff47802b5a461aa"
# The pin above refuses any changed byte: this cap only stops a body that
# cannot be the installer (a sign-in portal's page) before it is hashed.
MAX_INSTALLER_BYTES=8388608
+8 -8
View File
@@ -27,8 +27,8 @@
{
"dest": "roms/ports/retrobios/install.py",
"repo_path": "install.py",
"size": 1201450,
"sha256": "37442a7c0d307b0b95a40d7630cf57025a6498fea6223076892371b2d6363147",
"size": 1220974,
"sha256": "67036bbe4db401de213d9a8ab44161b8b64bfde91834b438fff47802b5a461aa",
"executable": false
},
{
@@ -71,8 +71,8 @@
"muos": {
"archive": {
"dest": "ARCHIVE/RetroBIOS.muxapp",
"size": 1255075,
"sha256": "23393eb6b88f7a7875bd034e79c02d81e80409c9403290bb96a75166bf7a60b6"
"size": 1274599,
"sha256": "8ea3d79c9bbcec747555c3970edef0692db5a00ada0ff45892aaf68dcfc38eb6"
},
"members": [
{
@@ -92,8 +92,8 @@
{
"name": "RetroBIOS/install.py",
"repo_path": "install.py",
"size": 1201450,
"sha256": "37442a7c0d307b0b95a40d7630cf57025a6498fea6223076892371b2d6363147",
"size": 1220974,
"sha256": "67036bbe4db401de213d9a8ab44161b8b64bfde91834b438fff47802b5a461aa",
"executable": false
},
{
@@ -166,8 +166,8 @@
{
"dest": ".config/modules/retrobios/install.py",
"repo_path": "install.py",
"size": 1201450,
"sha256": "37442a7c0d307b0b95a40d7630cf57025a6498fea6223076892371b2d6363147",
"size": 1220974,
"sha256": "67036bbe4db401de213d9a8ab44161b8b64bfde91834b438fff47802b5a461aa",
"executable": false
},
{
+1 -1
View File
@@ -66,7 +66,7 @@ ACTIONS = {
"page": "Page",
"ok": "Continue, open, choose",
"tick": "Tick, untick",
"options": "Options",
"options": "Change",
"back": "Back, and quit on the first screen",
"stop": "Stop an install",
"kill": "Quit at once",
@@ -1,14 +1,21 @@
RetroBIOS: Options
Each row is an option of the command line, named on the right.
RetroBIOS: Change
What to install
> Preferred regions: North America, Japan --region
Game data: kept --no-game-data
Installed cores only: no (1 found) --installed-cores
Cores: all --core
Parallel downloads: 8 --jobs
Emulators: all --emulator
Game data: kept --no-game-data
Where
BIOS folder: ~/.config/retroarch/system --dest
Copy from: none --from
From a pack: none --from-pack
For which device
Device: every board --target
Installed cores only: no (1 found) --installed-cores
How
Without the internet: no --offline
Parallel downloads: 8 --jobs
See
Preview the file list --dry-run
Show the command line
@@ -24,13 +31,6 @@
@@ -1,12 +1,12 @@
RetroBIOS: Options
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
1-8 of 18
A Open B Back <> Set ? Help
@@ -1,15 +1,15 @@
RetroBIOS: Options
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
Sets --region
From a pack: none
For which device
Device: every board
1-11 of 18
A Open B Back <> Set ? Help
@@ -1,24 +1,24 @@
RetroBIOS: Options
Each row is an option of the command line, named on the right.
RetroBIOS: Change
What to install
> Preferred regions: North America, Japan --region
Game data: kept --no-game-data
Installed cores only: no (1 found) --installed-cores
Cores: all --core
Parallel downloads: 8 --jobs
Emulators: all --emulator
Game data: kept --no-game-data
Where
BIOS folder: ~/.config/retroarch/system --dest
Copy from: none --from
From a pack: none --from-pack
For which device
Device: every board --target
Installed cores only: no (1 found) --installed-cores
How
Without the internet: no --offline
Parallel downloads: 8 --jobs
See
Preview the file list --dry-run
Show the command line
A/Enter Open B/Esc Back Left/Right Set ? Help
+105 -23
View File
@@ -336,7 +336,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -366,7 +366,7 @@
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Change ^[[0;2m?^[[0m Help
=== plan-in-place
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: RetroArch^[[0m
@@ -375,7 +375,7 @@
^[[0;7;35m > Close^[[0m
Details
Options
Change
@@ -407,7 +407,7 @@
^[[0;1;35mA/Enter^[[0m Close ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA/Enter^[[0m Close ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Change ^[[0;2m?^[[0m Help
=== systems
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Choose systems^[[0m
@@ -449,18 +449,25 @@
^[[0;1m ^[[0;1;35m2^[[0;1m of ^[[0;1;35m3^[[0;1m systems: ^[[0;1;35m4^[[0;1m files, ^[[0;1;35m1.0 MB^[[0m
^[[0;1;35mA/Enter^[[0m Tick ^[[0;2mB/Esc^[[0m Done ^[[0;2m?^[[0m Help
=== options
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Options^[[0m
=== change
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Change^[[0m
Each row is an option of the command line, named on the right.
^[[0;1m What to install^[[0m
^[[0;7;35m > Preferred regions: North America, Japan --region^[[0m
Game data: kept --no-game-data
Installed cores only: no (1 found) --installed-cores
Cores: all --core
Parallel downloads: 8 --jobs
Emulators: all --emulator
Game data: kept --no-game-data
^[[0;1m Where^[[0m
BIOS folder: ^[[0;1m~/.config/retroarch/system^[[0m --dest
Copy from: none --from
From a pack: none --from-pack
^[[0;1m For which device^[[0m
Device: every board --target
Installed cores only: no (1 found) --installed-cores
^[[0;1m How^[[0m
Without the internet: no --offline
Parallel downloads: 8 --jobs
^[[0;1m See^[[0m
Preview the file list --dry-run
Show the command line
@@ -476,13 +483,6 @@
@@ -490,6 +490,88 @@
^[[0;1;35mA/Enter^[[0m Open ^[[0;2mB/Esc^[[0m Back ^[[0;2mLeft/Right^[[0m Set ^[[0;2m?^[[0m Help
=== device
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Device^[[0m
Choose the board, the chip of the device the games run on. Every board fits any device.
^[[0;7;35m > (x) h700 named by the card^[[0m
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
^[[0;1;35mA/Enter^[[0m Choose ^[[0;2mB/Esc^[[0m Back ^[[0;2m?^[[0m Help
=== emulators
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Emulators^[[0m
Tick the emulators to install for. With none ticked, every emulator's files are installed.
Filter (/ to type)
[ ] mesen
^[[0;7;35m > [x] pcsx_rearmed^[[0m
None
^[[0;32mDone^[[0m
^[[0;1;35mA/Enter^[[0m Tick ^[[0;2mB/Esc^[[0m Done ^[[0;2m?^[[0m Help
=== regions
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Preferred regions^[[0m
@@ -864,7 +946,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
Y, o, Tab Change
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
@@ -914,7 +996,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -940,7 +1022,7 @@
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Change ^[[0;2m?^[[0m Help
=== games
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Games on this card^[[0m
@@ -1077,7 +1159,7 @@
Copy the BIOS files from an old card or a folder
Change where
Details
Options
Change
Quit
@@ -1104,4 +1186,4 @@
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Change ^[[0;2m?^[[0m Help
+40 -14
View File
@@ -112,9 +112,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Change ^[[0;2m?^[[0m Help
=== plan-in-place
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: RetroArch^[[0m
@@ -124,10 +124,10 @@
^[[0;7;35m > Close^[[0m
Details
Options
Change
^[[0;1;35mA^[[0m Close ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA^[[0m Close ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Change ^[[0;2m?^[[0m Help
=== systems
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Choose systems^[[0m
@@ -141,19 +141,45 @@
place
^[[0;1m ^[[0;1;35m2^[[0;1m of ^[[0;1;35m3^[[0;1m systems: ^[[0;1;35m4^[[0;1m files, ^[[0;1;35m1.0 MB^[[0m
^[[0;1;35mA^[[0m Tick ^[[0;2mB^[[0m Done ^[[0;2m?^[[0m Help
=== options
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Options^[[0m
=== change
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Change^[[0m
^[[0;1m What to install^[[0m
^[[0;7;35m > Preferred regions: North America, Ja^[[0m
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
^[[0;1m Where^[[0m
BIOS folder: ^[[0;1m~/.config...ch/system^[[0m
Copy from: none
Preview the file list
Show the command line
^[[0;2m 1-8 of 18^[[0m
^[[0;1;35mA^[[0m Open ^[[0;2mB^[[0m Back ^[[0;2m<>^[[0m Set ^[[0;2m?^[[0m Help
=== device
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Device^[[0m
Choose the board, the chip of the
device the games run on. Every board
fits any device.
^[[0;7;35m > (x) h700 named by the card^[[0m
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
^[[0;1;35mA^[[0m Choose ^[[0;2mB^[[0m Back ^[[0;2m?^[[0m Help
=== emulators
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Emulators^[[0m
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (/ to type)
[ ] mesen
^[[0;7;35m > [x] pcsx_rearmed^[[0m
None
^[[0;32mDone^[[0m
^[[0;1;35mA^[[0m Tick ^[[0;2mB^[[0m Done ^[[0;2m?^[[0m Help
=== regions
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Preferred regions^[[0m
@@ -276,7 +302,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
Y, o, Tab Change
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
@@ -296,7 +322,7 @@
Only what starts: no
Show the games
^[[0;2m 1-4 of 9^[[0m
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Change ^[[0;2m?^[[0m Help
=== games
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Games on this card^[[0m
@@ -348,4 +374,4 @@
Choose systems
Copy from an old card or a folder
^[[0;2m 1-4 of 8^[[0m
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Change ^[[0;2m?^[[0m Help
+51 -19
View File
@@ -137,11 +137,11 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Change ^[[0;2m?^[[0m Help
=== plan-in-place
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: RetroArch^[[0m
@@ -151,13 +151,13 @@
^[[0;7;35m > Close^[[0m
Details
Options
Change
^[[0;1;35mA^[[0m Close ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA^[[0m Close ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Change ^[[0;2m?^[[0m Help
=== systems
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Choose systems^[[0m
@@ -174,22 +174,54 @@
place
^[[0;1m ^[[0;1;35m2^[[0;1m of ^[[0;1;35m3^[[0;1m systems: ^[[0;1;35m4^[[0;1m files, ^[[0;1;35m1.0 MB^[[0m
^[[0;1;35mA^[[0m Tick ^[[0;2mB^[[0m Done ^[[0;2m?^[[0m Help
=== options
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Options^[[0m
=== change
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Change^[[0m
^[[0;1m What to install^[[0m
^[[0;7;35m > Preferred regions: North America, Ja^[[0m
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
^[[0;1m Where^[[0m
BIOS folder: ^[[0;1m~/.config...ch/system^[[0m
Copy from: none
Preview the file list
Show the command line
Sets --region
From a pack: none
^[[0;1m For which device^[[0m
Device: every board
^[[0;2m 1-11 of 18^[[0m
^[[0;1;35mA^[[0m Open ^[[0;2mB^[[0m Back ^[[0;2m<>^[[0m Set ^[[0;2m?^[[0m Help
=== device
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Device^[[0m
Choose the board, the chip of the
device the games run on. Every board
fits any device.
^[[0;7;35m > (x) h700 named by the card^[[0m
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
^[[0;1;35mA^[[0m Choose ^[[0;2mB^[[0m Back ^[[0;2m?^[[0m Help
=== emulators
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Emulators^[[0m
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (/ to type)
[ ] mesen
^[[0;7;35m > [x] pcsx_rearmed^[[0m
None
^[[0;32mDone^[[0m
^[[0;1;35mA^[[0m Tick ^[[0;2mB^[[0m Done ^[[0;2m?^[[0m Help
=== regions
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Preferred regions^[[0m
@@ -339,7 +371,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
Y, o, Tab Change
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
@@ -363,9 +395,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Change ^[[0;2m?^[[0m Help
=== games
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Games on this card^[[0m
@@ -427,6 +459,6 @@
Copy from an old card or a folder
Change where
Details
Options
Change
Quit
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Change ^[[0;2m?^[[0m Help
+73 -23
View File
@@ -208,7 +208,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -222,7 +222,7 @@
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Change ^[[0;2m?^[[0m Help
=== plan-in-place
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: RetroArch^[[0m
@@ -231,7 +231,7 @@
^[[0;7;35m > Close^[[0m
Details
Options
Change
@@ -247,7 +247,7 @@
^[[0;1;35mA/Enter^[[0m Close ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA/Enter^[[0m Close ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Change ^[[0;2m?^[[0m Help
=== systems
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Choose systems^[[0m
@@ -273,31 +273,81 @@
^[[0;1m ^[[0;1;35m2^[[0;1m of ^[[0;1;35m3^[[0;1m systems: ^[[0;1;35m4^[[0;1m files, ^[[0;1;35m1.0 MB^[[0m
^[[0;1;35mA/Enter^[[0m Tick ^[[0;2mB/Esc^[[0m Done ^[[0;2m?^[[0m Help
=== options
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Options^[[0m
=== change
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Change^[[0m
Each row is an option of the command line, named on the right.
^[[0;1m What to install^[[0m
^[[0;7;35m > Preferred regions: North America, Japan --region^[[0m
Game data: kept --no-game-data
Installed cores only: no (1 found) --installed-cores
Cores: all --core
Parallel downloads: 8 --jobs
Emulators: all --emulator
Game data: kept --no-game-data
^[[0;1m Where^[[0m
BIOS folder: ^[[0;1m~/.config/retroarch/system^[[0m --dest
Copy from: none --from
From a pack: none --from-pack
^[[0;1m For which device^[[0m
Device: every board --target
Installed cores only: no (1 found) --installed-cores
^[[0;1m How^[[0m
Without the internet: no --offline
Parallel downloads: 8 --jobs
^[[0;1m See^[[0m
Preview the file list --dry-run
Show the command line
^[[0;1;35mA/Enter^[[0m Open ^[[0;2mB/Esc^[[0m Back ^[[0;2mLeft/Right^[[0m Set ^[[0;2m?^[[0m Help
=== device
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Device^[[0m
Choose the board, the chip of the device the games run on. Every board fits
any device.
^[[0;7;35m > (x) h700 named by the card^[[0m
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
^[[0;1;35mA/Enter^[[0m Choose ^[[0;2mB/Esc^[[0m Back ^[[0;2m?^[[0m Help
=== emulators
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Emulators^[[0m
Tick the emulators to install for. With none ticked, every emulator's files
are installed.
Filter (/ to type)
[ ] mesen
^[[0;7;35m > [x] pcsx_rearmed^[[0m
None
^[[0;32mDone^[[0m
^[[0;1;35mA/Enter^[[0m Tick ^[[0;2mB/Esc^[[0m Done ^[[0;2m?^[[0m Help
=== regions
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Preferred regions^[[0m
@@ -528,7 +578,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
Y, o, Tab Change
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
@@ -562,7 +612,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -572,7 +622,7 @@
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Change ^[[0;2m?^[[0m Help
=== games
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Games on this card^[[0m
@@ -661,7 +711,7 @@
Copy the BIOS files from an old card or a folder
Change where
Details
Options
Change
Quit
@@ -672,4 +722,4 @@
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Options ^[[0;2m?^[[0m Help
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Change ^[[0;2m?^[[0m Help
+40
View File
@@ -0,0 +1,40 @@
RetroBIOS: Device
Choose the board, the chip of the device the games run on. Every board fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A/Enter Choose B/Esc Back ? Help
+12
View File
@@ -0,0 +1,12 @@
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose B Back ? Help
+15
View File
@@ -0,0 +1,15 @@
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose B Back ? Help
+24
View File
@@ -0,0 +1,24 @@
RetroBIOS: Device
Choose the board, the chip of the device the games run on. Every board fits
any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A/Enter Choose B/Esc Back ? Help
+40
View File
@@ -0,0 +1,40 @@
RetroBIOS: Emulators
Tick the emulators to install for. With none ticked, every emulator's files are installed.
Filter (/ to type)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A/Enter Tick B/Esc Done ? Help
+12
View File
@@ -0,0 +1,12 @@
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (/ to type)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick B Done ? Help
+15
View File
@@ -0,0 +1,15 @@
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (/ to type)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick B Done ? Help
+24
View File
@@ -0,0 +1,24 @@
RetroBIOS: Emulators
Tick the emulators to install for. With none ticked, every emulator's files
are installed.
Filter (/ to type)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A/Enter Tick B/Esc Done ? Help
+1 -1
View File
@@ -2,7 +2,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
Y, o, Tab Change
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
+1 -1
View File
@@ -2,7 +2,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
Y, o, Tab Change
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
+1 -1
View File
@@ -2,7 +2,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
Y, o, Tab Change
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
+1 -1
View File
@@ -2,7 +2,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
Y, o, Tab Change
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
+2 -2
View File
@@ -7,7 +7,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -37,4 +37,4 @@
A/Enter Install B/Esc Back Y/o Options ? Help
A/Enter Install B/Esc Back Y/o Change ? Help
+2 -2
View File
@@ -7,6 +7,6 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options ? Help
A Install B Back Y Change ? Help
+2 -2
View File
@@ -8,8 +8,8 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options ? Help
A Install B Back Y Change ? Help
+2 -2
View File
@@ -7,7 +7,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -21,4 +21,4 @@
A/Enter Install B/Esc Back Y/o Options ? Help
A/Enter Install B/Esc Back Y/o Change ? Help
+2 -2
View File
@@ -11,7 +11,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -37,4 +37,4 @@
A/Enter Install B/Esc Back Y/o Options ? Help
A/Enter Install B/Esc Back Y/o Change ? Help
+1 -1
View File
@@ -9,4 +9,4 @@
Only what starts: no
Show the games
1-4 of 9
A Install B Back Y Options ? Help
A Install B Back Y Change ? Help
+2 -2
View File
@@ -10,6 +10,6 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options ? Help
A Install B Back Y Change ? Help
+2 -2
View File
@@ -11,7 +11,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -21,4 +21,4 @@
A/Enter Install B/Esc Back Y/o Options ? Help
A/Enter Install B/Esc Back Y/o Change ? Help
+2 -2
View File
@@ -5,7 +5,7 @@
> Close
Details
Options
Change
@@ -37,4 +37,4 @@
A/Enter Close B/Esc Back Y/o Options ? Help
A/Enter Close B/Esc Back Y/o Change ? Help
+2 -2
View File
@@ -6,7 +6,7 @@
> Close
Details
Options
Change
A Close B Back Y Options ? Help
A Close B Back Y Change ? Help
+2 -2
View File
@@ -6,10 +6,10 @@
> Close
Details
Options
Change
A Close B Back Y Options ? Help
A Close B Back Y Change ? Help
+2 -2
View File
@@ -5,7 +5,7 @@
> Close
Details
Options
Change
@@ -21,4 +21,4 @@
A/Enter Close B/Esc Back Y/o Options ? Help
A/Enter Close B/Esc Back Y/o Change ? Help
+2 -2
View File
@@ -10,7 +10,7 @@
Copy the BIOS files from an old card or a folder
Change where
Details
Options
Change
Quit
@@ -37,4 +37,4 @@
A/Enter Install B/Esc Back Y/o Options ? Help
A/Enter Install B/Esc Back Y/o Change ? Help
+1 -1
View File
@@ -9,4 +9,4 @@
Choose systems
Copy from an old card or a folder
1-4 of 8
A Install B Back Y Options ? Help
A Install B Back Y Change ? Help
+2 -2
View File
@@ -10,6 +10,6 @@
Copy from an old card or a folder
Change where
Details
Options
Change
Quit
A Install B Back Y Options ? Help
A Install B Back Y Change ? Help
+2 -2
View File
@@ -10,7 +10,7 @@
Copy the BIOS files from an old card or a folder
Change where
Details
Options
Change
Quit
@@ -21,4 +21,4 @@
A/Enter Install B/Esc Back Y/o Options ? Help
A/Enter Install B/Esc Back Y/o Change ? Help
+164 -56
View File
@@ -411,9 +411,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan 40x15
RetroBIOS: RetroArch
@@ -425,11 +425,11 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan 80x24
RetroBIOS: RetroArch
@@ -440,7 +440,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -454,7 +454,7 @@
A Install B Back Y Options
A Install B Back Y Change
=== plan-in-place 40x12
RetroBIOS: RetroArch
@@ -464,10 +464,10 @@
> Close
Details
Options
Change
A Close B Back Y Options
A Close B Back Y Change
=== plan-in-place 40x15
RetroBIOS: RetroArch
@@ -477,13 +477,13 @@
> Close
Details
Options
Change
A Close B Back Y Options
A Close B Back Y Change
=== plan-in-place 80x24
RetroBIOS: RetroArch
@@ -492,7 +492,7 @@
> Close
Details
Options
Change
@@ -508,7 +508,7 @@
A Close B Back Y Options
A Close B Back Y Change
=== systems 40x12
RetroBIOS: Choose systems
@@ -563,60 +563,168 @@
2 of 3 systems: 4 files, 1.0 MB
A Tick B Done
=== options 40x12
RetroBIOS: Options
=== change 40x12
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
1-8 of 18
A Open B Back <> Set
=== options 40x15
RetroBIOS: Options
=== change 40x15
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
Sets --region
From a pack: none
For which device
Device: every board
1-11 of 18
A Open B Back <> Set
=== options 80x24
RetroBIOS: Options
Each row is an option of the command line, named on the right.
=== change 80x24
RetroBIOS: Change
What to install
> Preferred regions: North America, Japan --region
Game data: kept --no-game-data
Installed cores only: no (1 found) --installed-cores
Cores: all --core
Parallel downloads: 8 --jobs
Emulators: all --emulator
Game data: kept --no-game-data
Where
BIOS folder: ~/.config/retroarch/system --dest
Copy from: none --from
From a pack: none --from-pack
For which device
Device: every board --target
Installed cores only: no (1 found) --installed-cores
How
Without the internet: no --offline
Parallel downloads: 8 --jobs
See
Preview the file list --dry-run
Show the command line
A Open B Back Left/Right Set
=== device 40x12
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose B Back
=== device 40x15
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose B Back
=== device 80x24
RetroBIOS: Device
Choose the board, the chip of the device the games run on. Every board fits
any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose B Back
=== emulators 40x12
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick B Done
=== emulators 40x15
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick B Done
=== emulators 80x24
RetroBIOS: Emulators
Tick the emulators to install for. With none ticked, every emulator's files
are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick B Done
=== regions 40x12
RetroBIOS: Preferred regions
@@ -976,7 +1084,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
Y, Tab, o Change
d Details
B, Select, Esc Back
q, Ctrl-C Quit
@@ -989,7 +1097,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
Y, Tab, o Change
d Details
B, Select, Esc Back
q, Ctrl-C Quit
@@ -1005,7 +1113,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
Y, Tab, o Change
d Details
B, Select, Esc Back
q, Ctrl-C Quit
@@ -1037,7 +1145,7 @@
Only what starts: no
Show the games
1-4 of 9
A Install B Back Y Options
A Install B Back Y Change
=== plan-games 40x15
RetroBIOS: RetroArch
@@ -1051,9 +1159,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan-games 80x24
RetroBIOS: RetroArch
@@ -1068,7 +1176,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -1078,7 +1186,7 @@
A Install B Back Y Options
A Install B Back Y Change
=== games 80x24
RetroBIOS: Games on this card
@@ -1195,7 +1303,7 @@
Choose systems
Copy from an old card or a folder
1-4 of 8
A Install B Back Y Options
A Install B Back Y Change
=== plan-record 40x15
RetroBIOS: RetroArch
@@ -1209,9 +1317,9 @@
Copy from an old card or a folder
Change where
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan-record 80x24
RetroBIOS: RetroArch
@@ -1225,7 +1333,7 @@
Copy the BIOS files from an old card or a folder
Change where
Details
Options
Change
Quit
@@ -1236,4 +1344,4 @@
A Install B Back Y Options
A Install B Back Y Change
+164 -56
View File
@@ -411,9 +411,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan 40x15
RetroBIOS: RetroArch
@@ -425,11 +425,11 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan 80x24
RetroBIOS: RetroArch
@@ -440,7 +440,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -454,7 +454,7 @@
A Install B Back Y Options
A Install B Back Y Change
=== plan-in-place 40x12
RetroBIOS: RetroArch
@@ -464,10 +464,10 @@
> Close
Details
Options
Change
A Close B Back Y Options
A Close B Back Y Change
=== plan-in-place 40x15
RetroBIOS: RetroArch
@@ -477,13 +477,13 @@
> Close
Details
Options
Change
A Close B Back Y Options
A Close B Back Y Change
=== plan-in-place 80x24
RetroBIOS: RetroArch
@@ -492,7 +492,7 @@
> Close
Details
Options
Change
@@ -508,7 +508,7 @@
A Close B Back Y Options
A Close B Back Y Change
=== systems 40x12
RetroBIOS: Choose systems
@@ -563,60 +563,168 @@
2 of 3 systems: 4 files, 1.0 MB
A Tick B Done
=== options 40x12
RetroBIOS: Options
=== change 40x12
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
1-8 of 18
A Open B Back <> Set
=== options 40x15
RetroBIOS: Options
=== change 40x15
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
Sets --region
From a pack: none
For which device
Device: every board
1-11 of 18
A Open B Back <> Set
=== options 80x24
RetroBIOS: Options
Each row is an option of the command line, named on the right.
=== change 80x24
RetroBIOS: Change
What to install
> Preferred regions: North America, Japan --region
Game data: kept --no-game-data
Installed cores only: no (1 found) --installed-cores
Cores: all --core
Parallel downloads: 8 --jobs
Emulators: all --emulator
Game data: kept --no-game-data
Where
BIOS folder: ~/.config/retroarch/system --dest
Copy from: none --from
From a pack: none --from-pack
For which device
Device: every board --target
Installed cores only: no (1 found) --installed-cores
How
Without the internet: no --offline
Parallel downloads: 8 --jobs
See
Preview the file list --dry-run
Show the command line
A Open B Back Left/Right Set
=== device 40x12
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose B Back
=== device 40x15
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose B Back
=== device 80x24
RetroBIOS: Device
Choose the board, the chip of the device the games run on. Every board fits
any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose B Back
=== emulators 40x12
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick B Done
=== emulators 40x15
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick B Done
=== emulators 80x24
RetroBIOS: Emulators
Tick the emulators to install for. With none ticked, every emulator's files
are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick B Done
=== regions 40x12
RetroBIOS: Preferred regions
@@ -976,7 +1084,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
Y, Tab, o Change
d Details
B, Select, Esc Back
q, Ctrl-C Quit
@@ -989,7 +1097,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
Y, Tab, o Change
d Details
B, Select, Esc Back
q, Ctrl-C Quit
@@ -1005,7 +1113,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
Y, Tab, o Change
d Details
B, Select, Esc Back
q, Ctrl-C Quit
@@ -1037,7 +1145,7 @@
Only what starts: no
Show the games
1-4 of 9
A Install B Back Y Options
A Install B Back Y Change
=== plan-games 40x15
RetroBIOS: RetroArch
@@ -1051,9 +1159,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan-games 80x24
RetroBIOS: RetroArch
@@ -1068,7 +1176,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -1078,7 +1186,7 @@
A Install B Back Y Options
A Install B Back Y Change
=== games 80x24
RetroBIOS: Games on this card
@@ -1195,7 +1303,7 @@
Choose systems
Copy from an old card or a folder
1-4 of 8
A Install B Back Y Options
A Install B Back Y Change
=== plan-record 40x15
RetroBIOS: RetroArch
@@ -1209,9 +1317,9 @@
Copy from an old card or a folder
Change where
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan-record 80x24
RetroBIOS: RetroArch
@@ -1225,7 +1333,7 @@
Copy the BIOS files from an old card or a folder
Change where
Details
Options
Change
Quit
@@ -1236,4 +1344,4 @@
A Install B Back Y Options
A Install B Back Y Change
+164 -56
View File
@@ -411,9 +411,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan 40x15
RetroBIOS: RetroArch
@@ -425,11 +425,11 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan 80x24
RetroBIOS: RetroArch
@@ -440,7 +440,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -454,7 +454,7 @@
A Install B Back Y Options
A Install B Back Y Change
=== plan-in-place 40x12
RetroBIOS: RetroArch
@@ -464,10 +464,10 @@
> Close
Details
Options
Change
A Close B Back Y Options
A Close B Back Y Change
=== plan-in-place 40x15
RetroBIOS: RetroArch
@@ -477,13 +477,13 @@
> Close
Details
Options
Change
A Close B Back Y Options
A Close B Back Y Change
=== plan-in-place 80x24
RetroBIOS: RetroArch
@@ -492,7 +492,7 @@
> Close
Details
Options
Change
@@ -508,7 +508,7 @@
A Close B Back Y Options
A Close B Back Y Change
=== systems 40x12
RetroBIOS: Choose systems
@@ -563,60 +563,168 @@
2 of 3 systems: 4 files, 1.0 MB
A Tick B Done
=== options 40x12
RetroBIOS: Options
=== change 40x12
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
1-8 of 18
A Open B Back <> Set
=== options 40x15
RetroBIOS: Options
=== change 40x15
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
Sets --region
From a pack: none
For which device
Device: every board
1-11 of 18
A Open B Back <> Set
=== options 80x24
RetroBIOS: Options
Each row is an option of the command line, named on the right.
=== change 80x24
RetroBIOS: Change
What to install
> Preferred regions: North America, Japan --region
Game data: kept --no-game-data
Installed cores only: no (1 found) --installed-cores
Cores: all --core
Parallel downloads: 8 --jobs
Emulators: all --emulator
Game data: kept --no-game-data
Where
BIOS folder: ~/.config/retroarch/system --dest
Copy from: none --from
From a pack: none --from-pack
For which device
Device: every board --target
Installed cores only: no (1 found) --installed-cores
How
Without the internet: no --offline
Parallel downloads: 8 --jobs
See
Preview the file list --dry-run
Show the command line
A Open B Back Left/Right Set
=== device 40x12
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose B Back
=== device 40x15
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose B Back
=== device 80x24
RetroBIOS: Device
Choose the board, the chip of the device the games run on. Every board fits
any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose B Back
=== emulators 40x12
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick B Done
=== emulators 40x15
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick B Done
=== emulators 80x24
RetroBIOS: Emulators
Tick the emulators to install for. With none ticked, every emulator's files
are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick B Done
=== regions 40x12
RetroBIOS: Preferred regions
@@ -976,7 +1084,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
Y, Tab, o Change
d Details
B, Select, Esc Back
q, Ctrl-C Quit
@@ -989,7 +1097,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
Y, Tab, o Change
d Details
B, Select, Esc Back
q, Ctrl-C Quit
@@ -1005,7 +1113,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
Y, Tab, o Change
d Details
B, Select, Esc Back
q, Ctrl-C Quit
@@ -1037,7 +1145,7 @@
Only what starts: no
Show the games
1-4 of 9
A Install B Back Y Options
A Install B Back Y Change
=== plan-games 40x15
RetroBIOS: RetroArch
@@ -1051,9 +1159,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan-games 80x24
RetroBIOS: RetroArch
@@ -1068,7 +1176,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -1078,7 +1186,7 @@
A Install B Back Y Options
A Install B Back Y Change
=== games 80x24
RetroBIOS: Games on this card
@@ -1195,7 +1303,7 @@
Choose systems
Copy from an old card or a folder
1-4 of 8
A Install B Back Y Options
A Install B Back Y Change
=== plan-record 40x15
RetroBIOS: RetroArch
@@ -1209,9 +1317,9 @@
Copy from an old card or a folder
Change where
Details
Options
Change
Quit
A Install B Back Y Options
A Install B Back Y Change
=== plan-record 80x24
RetroBIOS: RetroArch
@@ -1225,7 +1333,7 @@
Copy the BIOS files from an old card or a folder
Change where
Details
Options
Change
Quit
@@ -1236,4 +1344,4 @@
A Install B Back Y Options
A Install B Back Y Change
+164 -56
View File
@@ -386,9 +386,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
Enter Install Esc Back o Options
Enter Install Esc Back o Change
=== plan 40x15
RetroBIOS: RetroArch
@@ -400,11 +400,11 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
Enter Install Esc Back o Options
Enter Install Esc Back o Change
=== plan 80x24
RetroBIOS: RetroArch
@@ -415,7 +415,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -429,7 +429,7 @@
Enter Install Esc Back o Options ? Help
Enter Install Esc Back o Change ? Help
=== plan-in-place 40x12
RetroBIOS: RetroArch
@@ -439,10 +439,10 @@
> Close
Details
Options
Change
Enter Close Esc Back o Options
Enter Close Esc Back o Change
=== plan-in-place 40x15
RetroBIOS: RetroArch
@@ -452,13 +452,13 @@
> Close
Details
Options
Change
Enter Close Esc Back o Options
Enter Close Esc Back o Change
=== plan-in-place 80x24
RetroBIOS: RetroArch
@@ -467,7 +467,7 @@
> Close
Details
Options
Change
@@ -483,7 +483,7 @@
Enter Close Esc Back o Options ? Help
Enter Close Esc Back o Change ? Help
=== systems 40x12
RetroBIOS: Choose systems
@@ -538,60 +538,168 @@
2 of 3 systems: 4 files, 1.0 MB
Enter Tick Esc Done ? Help
=== options 40x12
RetroBIOS: Options
=== change 40x12
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
1-8 of 18
Enter Open Esc Back <> Set ? Help
=== options 40x15
RetroBIOS: Options
=== change 40x15
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
Sets --region
From a pack: none
For which device
Device: every board
1-11 of 18
Enter Open Esc Back <> Set ? Help
=== options 80x24
RetroBIOS: Options
Each row is an option of the command line, named on the right.
=== change 80x24
RetroBIOS: Change
What to install
> Preferred regions: North America, Japan --region
Game data: kept --no-game-data
Installed cores only: no (1 found) --installed-cores
Cores: all --core
Parallel downloads: 8 --jobs
Emulators: all --emulator
Game data: kept --no-game-data
Where
BIOS folder: ~/.config/retroarch/system --dest
Copy from: none --from
From a pack: none --from-pack
For which device
Device: every board --target
Installed cores only: no (1 found) --installed-cores
How
Without the internet: no --offline
Parallel downloads: 8 --jobs
See
Preview the file list --dry-run
Show the command line
Enter Open Esc Back Left/Right Set ? Help
=== device 40x12
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
Enter Choose Esc Back ? Help
=== device 40x15
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
Enter Choose Esc Back ? Help
=== device 80x24
RetroBIOS: Device
Choose the board, the chip of the device the games run on. Every board fits
any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
Enter Choose Esc Back ? Help
=== emulators 40x12
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (/ to type)
[ ] mesen
> [x] pcsx_rearmed
None
Done
Enter Tick Esc Done ? Help
=== emulators 40x15
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (/ to type)
[ ] mesen
> [x] pcsx_rearmed
None
Done
Enter Tick Esc Done ? Help
=== emulators 80x24
RetroBIOS: Emulators
Tick the emulators to install for. With none ticked, every emulator's files
are installed.
Filter (/ to type)
[ ] mesen
> [x] pcsx_rearmed
None
Done
Enter Tick Esc Done ? Help
=== regions 40x12
RetroBIOS: Preferred regions
@@ -1083,7 +1191,7 @@
Up/Down, k/j Move
Enter Install
o, Tab Options
o, Tab Change
d Details
Esc, Backspace Back
q, Ctrl-C Quit
@@ -1096,7 +1204,7 @@
Up/Down, k/j Move
Enter Install
o, Tab Options
o, Tab Change
d Details
Esc, Backspace Back
q, Ctrl-C Quit
@@ -1112,7 +1220,7 @@
Up/Down, k/j Move
Enter Install
o, Tab Options
o, Tab Change
d Details
Esc, Backspace Back
q, Ctrl-C Quit
@@ -1144,7 +1252,7 @@
Only what starts: no
Show the games
1-4 of 9
Enter Install Esc Back o Options
Enter Install Esc Back o Change
=== plan-games 40x15
RetroBIOS: RetroArch
@@ -1158,9 +1266,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
Enter Install Esc Back o Options
Enter Install Esc Back o Change
=== plan-games 80x24
RetroBIOS: RetroArch
@@ -1175,7 +1283,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -1185,7 +1293,7 @@
Enter Install Esc Back o Options ? Help
Enter Install Esc Back o Change ? Help
=== games 40x12
RetroBIOS: Games on this card
@@ -1360,7 +1468,7 @@
Choose systems
Copy from an old card or a folder
1-4 of 8
Enter Install Esc Back o Options
Enter Install Esc Back o Change
=== plan-record 40x15
RetroBIOS: RetroArch
@@ -1374,9 +1482,9 @@
Copy from an old card or a folder
Change where
Details
Options
Change
Quit
Enter Install Esc Back o Options
Enter Install Esc Back o Change
=== plan-record 80x24
RetroBIOS: RetroArch
@@ -1390,7 +1498,7 @@
Copy the BIOS files from an old card or a folder
Change where
Details
Options
Change
Quit
@@ -1401,4 +1509,4 @@
Enter Install Esc Back o Options ? Help
Enter Install Esc Back o Change ? Help
+164 -56
View File
@@ -440,9 +440,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install R2 Back L2 Options
A Install R2 Back L2 Change
=== plan 40x15
RetroBIOS: RetroArch
@@ -454,11 +454,11 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install R2 Back L2 Options
A Install R2 Back L2 Change
=== plan 80x24
RetroBIOS: RetroArch
@@ -469,7 +469,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -483,7 +483,7 @@
A Install R2 Back L2 Options
A Install R2 Back L2 Change
=== plan-in-place 40x12
RetroBIOS: RetroArch
@@ -493,10 +493,10 @@
> Close
Details
Options
Change
A Close R2 Back L2 Options
A Close R2 Back L2 Change
=== plan-in-place 40x15
RetroBIOS: RetroArch
@@ -506,13 +506,13 @@
> Close
Details
Options
Change
A Close R2 Back L2 Options
A Close R2 Back L2 Change
=== plan-in-place 80x24
RetroBIOS: RetroArch
@@ -521,7 +521,7 @@
> Close
Details
Options
Change
@@ -537,7 +537,7 @@
A Close R2 Back L2 Options
A Close R2 Back L2 Change
=== systems 40x12
RetroBIOS: Choose systems
@@ -592,60 +592,168 @@
2 of 3 systems: 4 files, 1.0 MB
A Tick R2 Done
=== options 40x12
RetroBIOS: Options
=== change 40x12
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
1-8 of 18
A Open R2 Back <> Set
=== options 40x15
RetroBIOS: Options
=== change 40x15
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
Sets --region
From a pack: none
For which device
Device: every board
1-11 of 18
A Open R2 Back <> Set
=== options 80x24
RetroBIOS: Options
Each row is an option of the command line, named on the right.
=== change 80x24
RetroBIOS: Change
What to install
> Preferred regions: North America, Japan --region
Game data: kept --no-game-data
Installed cores only: no (1 found) --installed-cores
Cores: all --core
Parallel downloads: 8 --jobs
Emulators: all --emulator
Game data: kept --no-game-data
Where
BIOS folder: ~/.config/retroarch/system --dest
Copy from: none --from
From a pack: none --from-pack
For which device
Device: every board --target
Installed cores only: no (1 found) --installed-cores
How
Without the internet: no --offline
Parallel downloads: 8 --jobs
See
Preview the file list --dry-run
Show the command line
A Open R2 Back Left/Right Set
=== device 40x12
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose R2 Back
=== device 40x15
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose R2 Back
=== device 80x24
RetroBIOS: Device
Choose the board, the chip of the device the games run on. Every board fits
any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
A Choose R2 Back
=== emulators 40x12
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick R2 Done
=== emulators 40x15
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick R2 Done
=== emulators 80x24
RetroBIOS: Emulators
Tick the emulators to install for. With none ticked, every emulator's files
are installed.
Filter (with a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
A Tick R2 Done
=== regions 40x12
RetroBIOS: Preferred regions
@@ -1079,7 +1187,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
L2, Tab, o Options
L2, Tab, o Change
d Details
R2, Esc, Backspace Back
q, Ctrl-C Quit
@@ -1092,7 +1200,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
L2, Tab, o Options
L2, Tab, o Change
d Details
R2, Esc, Backspace Back
q, Ctrl-C Quit
@@ -1108,7 +1216,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
L2, Tab, o Options
L2, Tab, o Change
d Details
R2, Esc, Backspace Back
q, Ctrl-C Quit
@@ -1140,7 +1248,7 @@
Only what starts: no
Show the games
1-4 of 9
A Install R2 Back L2 Options
A Install R2 Back L2 Change
=== plan-games 40x15
RetroBIOS: RetroArch
@@ -1154,9 +1262,9 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
A Install R2 Back L2 Options
A Install R2 Back L2 Change
=== plan-games 80x24
RetroBIOS: RetroArch
@@ -1171,7 +1279,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -1181,7 +1289,7 @@
A Install R2 Back L2 Options
A Install R2 Back L2 Change
=== games 80x24
RetroBIOS: Games on this card
@@ -1298,7 +1406,7 @@
Choose systems
Copy from an old card or a folder
1-4 of 8
A Install R2 Back L2 Options
A Install R2 Back L2 Change
=== plan-record 40x15
RetroBIOS: RetroArch
@@ -1312,9 +1420,9 @@
Copy from an old card or a folder
Change where
Details
Options
Change
Quit
A Install R2 Back L2 Options
A Install R2 Back L2 Change
=== plan-record 80x24
RetroBIOS: RetroArch
@@ -1328,7 +1436,7 @@
Copy the BIOS files from an old card or a folder
Change where
Details
Options
Change
Quit
@@ -1339,4 +1447,4 @@
A Install R2 Back L2 Options
A Install R2 Back L2 Change
+152 -44
View File
@@ -440,7 +440,7 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
Start Install B Back
=== plan 40x15
@@ -454,7 +454,7 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
@@ -469,7 +469,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -493,7 +493,7 @@
> Close
Details
Options
Change
Start Close B Back
@@ -506,7 +506,7 @@
> Close
Details
Options
Change
@@ -521,7 +521,7 @@
> Close
Details
Options
Change
@@ -592,60 +592,168 @@
2 of 3 systems: 4 files, 1.0 MB
Start Tick B Done
=== options 40x12
RetroBIOS: Options
=== change 40x12
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
1-8 of 18
Start Open B Back <> Set
=== options 40x15
RetroBIOS: Options
=== change 40x15
RetroBIOS: Change
What to install
> Preferred regions: North America, Ja
Game data: kept
Installed cores only: no (1 found)
Cores: all
Parallel downloads: 8
Emulators: all
Game data: kept
Where
BIOS folder: ~/.config...ch/system
Copy from: none
Preview the file list
Show the command line
Sets --region
From a pack: none
For which device
Device: every board
1-11 of 18
Start Open B Back <> Set
=== options 80x24
RetroBIOS: Options
Each row is an option of the command line, named on the right.
=== change 80x24
RetroBIOS: Change
What to install
> Preferred regions: North America, Japan --region
Game data: kept --no-game-data
Installed cores only: no (1 found) --installed-cores
Cores: all --core
Parallel downloads: 8 --jobs
Emulators: all --emulator
Game data: kept --no-game-data
Where
BIOS folder: ~/.config/retroarch/system --dest
Copy from: none --from
From a pack: none --from-pack
For which device
Device: every board --target
Installed cores only: no (1 found) --installed-cores
How
Without the internet: no --offline
Parallel downloads: 8 --jobs
See
Preview the file list --dry-run
Show the command line
Start Open B Back Left/Right Set
=== device 40x12
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
Start Choose B Back
=== device 40x15
RetroBIOS: Device
Choose the board, the chip of the
device the games run on. Every board
fits any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
Start Choose B Back
=== device 80x24
RetroBIOS: Device
Choose the board, the chip of the device the games run on. Every board fits
any device.
> (x) h700 named by the card
( ) bcm2711 rpi4
( ) x86_64
( ) Every board
Start Choose B Back
=== emulators 40x12
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (Select shows a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
Start Tick B Done
=== emulators 40x15
RetroBIOS: Emulators
Tick the emulators to install for.
With none ticked, every emulator's
files are installed.
Filter (Select shows a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
Start Tick B Done
=== emulators 80x24
RetroBIOS: Emulators
Tick the emulators to install for. With none ticked, every emulator's files
are installed.
Filter (Select shows a keyboard)
[ ] mesen
> [x] pcsx_rearmed
None
Done
Start Tick B Done
=== regions 40x12
RetroBIOS: Preferred regions
@@ -1137,7 +1245,7 @@
D-pad, Up/Down Move
Start, Enter Install
o, Tab Options
o, Tab Change
d Details
B, Esc, Backspace Back
q, Ctrl-C Quit
@@ -1150,7 +1258,7 @@
D-pad, Up/Down Move
Start, Enter Install
o, Tab Options
o, Tab Change
d Details
B, Esc, Backspace Back
q, Ctrl-C Quit
@@ -1166,7 +1274,7 @@
D-pad, Up/Down Move
Start, Enter Install
o, Tab Options
o, Tab Change
d Details
B, Esc, Backspace Back
q, Ctrl-C Quit
@@ -1212,7 +1320,7 @@
Choose systems
Copy from an old card or a folder
Details
Options
Change
Quit
Start Install B Back
=== plan-games 80x24
@@ -1229,7 +1337,7 @@
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Change
Quit
@@ -1428,7 +1536,7 @@
Copy from an old card or a folder
Change where
Details
Options
Change
Quit
Start Install B Back
=== plan-record 80x24
@@ -1444,7 +1552,7 @@
Copy the BIOS files from an old card or a folder
Change where
Details
Options
Change
Quit
+6 -2
View File
@@ -66,7 +66,11 @@ JOURNEY_WORDS = {
"update": 70,
"one_system": 179,
"one_system_again": 67,
"what_is_missing": 207,
# Change holds every option one level down now (design 4.4): its
# headings and the rows for Emulators, a pack, the device and the
# internet, the intro sentence dropped, 10 words on each of its two
# frames.
"what_is_missing": 227,
# The plan of the setup found comes first now (33 words), the setups
# one Esc away, and the last plan names Change where (2 words).
"another_device": 265,
@@ -319,7 +323,7 @@ class Journeys(unittest.TestCase):
term = self.start(repo)
term.wait("files to download", timeout=40)
self.press(term, "o")
term.wait("Each row is an option")
term.wait("RetroBIOS: Change")
self.press(term, END, UP)
term.wait("> Preview the file list")
self.press(term, "\r")
+667 -24
View File
@@ -450,6 +450,12 @@ class HoldingPack:
return (None, entry)
def option_focus(state: install.TuiState, action: str) -> None:
"""The focus on the Change row doing action: its rows sit under
headings, which no index of the flat list kept."""
state.focus["options"] = [row.action for row in install.tui_rows(state)].index(action)
def press(state: install.TuiState, *pressed) -> list:
effects = []
for key in pressed:
@@ -495,6 +501,7 @@ class Flow(unittest.TestCase):
state = checked_state()
state.passthrough = started.passthrough
state.from_folders = started.from_folders
state.packs = started.packs
for run in ("check", "install"):
argv = install.tui_argv(state, run, "py", "i.py")
for folder in ("old-card", "backup.zip"):
@@ -556,9 +563,12 @@ class Flow(unittest.TestCase):
press(state, "o")
self.assertEqual(state.screen, "options")
rows = install.tui_rows(state)
self.assertEqual([row.right for row in rows], [
"--region", "--no-game-data", "--installed-cores", "--core", "--jobs", "--dest",
"--from", "--dry-run", ""])
# Grouped by what each decides, a heading above each group.
self.assertEqual([row.right or row.label for row in rows], [
"What to install", "--region", "--core", "--emulator", "--no-game-data",
"Where", "--dest", "--from", "--from-pack",
"For which device", "--target", "--installed-cores",
"How", "--offline", "--jobs", "See", "--dry-run", "Show the command line"])
press(state, "enter")
self.assertEqual(state.screen, "regions")
# Ticked in the order of preference, then moved up.
@@ -574,7 +584,10 @@ class Flow(unittest.TestCase):
["[2] Japan", "[1] North America", "None", "Done"])
press(state, "shift-down")
self.assertEqual(state.regions, ["japan", "north-america"])
press(state, "esc", "down", "enter", "down", "right", "down", "down", "right", "right")
# Down passes the headings: Game data, Installed cores only, then
# Parallel downloads.
press(state, "esc", "down", "down", "down", "enter", *["down"] * 5, "right",
"down", "down", "right", "right")
self.assertTrue(state.no_game_data)
self.assertTrue(state.installed_only)
self.assertEqual(state.jobs, 10)
@@ -586,7 +599,7 @@ class Flow(unittest.TestCase):
def test_cores_checklist(self):
state = checked_state()
press(state, "o")
state.focus["options"] = 3
option_focus(state, "cores")
press(state, "enter")
self.assertEqual(state.screen, "cores")
self.assertEqual([row.value for row in install.tui_rows(state)
@@ -610,7 +623,7 @@ class Flow(unittest.TestCase):
def test_preview_is_a_dry_run(self):
state = checked_state()
press(state, "o")
state.focus["options"] = 7
option_focus(state, "preview")
self.assertEqual(press(state, "enter"), [("run", "preview")])
self.assertEqual(install.tui_argv(state, "preview", "py", "i.py")[-2:],
["--dry-run", "--events"])
@@ -805,7 +818,7 @@ class Flow(unittest.TestCase):
def test_events_of_an_abandoned_run_are_ignored(self):
state = checked_state()
press(state, "o")
state.focus["options"] = 7
option_focus(state, "preview")
press(state, "enter")
old = state.run_id
self.assertEqual(press(state, "esc"), [("kill",)])
@@ -983,8 +996,15 @@ def golden_states() -> dict:
states["systems"] = systems
options = checked_state()
options.stack.append("options")
options.focus["options"] = 1
options.regions = ["north-america", "japan"]
states["options"] = options
states["change"] = options
states["device"] = device_state()
emulators = checked_state()
emulators.stack += ["options", "emulators"]
emulators.emulators = ["pcsx_rearmed"]
emulators.focus["emulators"] = 2
states["emulators"] = emulators
regions = checked_state()
regions.stack += ["options", "regions"]
regions.regions = ["north-america"]
@@ -1046,6 +1066,30 @@ def golden_states() -> dict:
return states
# A board list as install/targets/ publishes it, read by Device: the boards
# by the names the list gives them, the device images each one runs.
TARGETS = {"bcm2711": {"cores": ["pcsx_rearmed"], "images": ["rpi4"]},
"h700": {"cores": ["pcsx_rearmed", "mesen"],
"images": ["anbernic-rg35xx-plus", "anbernic-rg40xx-h"]},
"x86_64": {"cores": None}}
def device_state(board: str = "h700") -> install.TuiState:
"""Device opened from Change, the board list in, the card in the reader
naming its board (the check's board event)."""
state = checked_state()
if board:
state.board_seen = ("retroarch", state.dest, board, True, False)
state.stack.append("options")
install.tui_update(state, Key("home"))
rows = install.tui_rows(state)
state.focus["options"] = [row.action for row in rows].index("device")
effects = install.tui_update(state, Key("enter"))
assert effects == [("targets", "retroarch")], effects
install.tui_receive(state, ("targets", "retroarch", TARGETS, ""))
return state
def record_setup(choice: list[str] | None = None) -> dict:
"""A record kept on 3 November of this year: the plan says 3 Nov in
any year it runs."""
@@ -1465,12 +1509,12 @@ class PadRoutes(unittest.TestCase):
def test_the_plan_bar_of_each_route(self):
expected = {
"both": "A/Enter Install B/Esc Back Y/o Options ? Help",
"keys": "Enter Install Esc Back o Options ? Help",
"gptokeyb": "A Install B Back Y Options",
"evmapy": "A Install B Back Y Options",
"both": "A/Enter Install B/Esc Back Y/o Change ? Help",
"keys": "Enter Install Esc Back o Change ? Help",
"gptokeyb": "A Install B Back Y Change",
"evmapy": "A Install B Back Y Change",
"muxterm": "Start Install B Back",
"miyoo": "A Install R2 Back L2 Options",
"miyoo": "A Install R2 Back L2 Change",
}
for route, bar in expected.items():
with self.subTest(route=route):
@@ -2003,7 +2047,7 @@ class TicksBarAndPaths(unittest.TestCase):
ticked = set(state.ticked)
press(state, "esc", "o")
self.assertEqual(state.screen, "options")
state.focus["options"] = 1
option_focus(state, "game_data")
press(state, "enter")
self.assertTrue(state.no_game_data)
effects = press(state, "esc")
@@ -3257,12 +3301,12 @@ class SaveDirectoryLeftOut(unittest.TestCase):
plan = " ".join(" ".join(install.tui_view(state, 120, 40)).split())
# The plan names the row that sets it, the result the option a
# command carries.
self.assertIn(line + "Choose it in Options, Save folder (--save-dir). Its place is "
self.assertIn(line + "Choose it in Change, Save folder (--save-dir). Its place is "
"not known.", plan)
state.stack.append("result")
state.code = 0
state.summary = {**summary, "mode": "install", "installed": 5}
self.assertIn(line + "Choose it in Options, Save folder (--save-dir), next time.",
self.assertIn(line + "Choose it in Change, Save folder (--save-dir), next time.",
install.tui_outcome(state)[1])
@@ -4128,7 +4172,7 @@ class BackTicksAndSaveFolder(unittest.TestCase):
state.manifest = {**state.manifest, "save_directory": {"folder": "saves",
"by_core": True}}
lines = install._unplaced_lines(summary, state)
self.assertIn("Options, Save folder (--save-dir)", lines[0])
self.assertIn("Change, Save folder (--save-dir)", lines[0])
def test_a_size_keeps_its_unit(self):
for width in range(20, 45):
@@ -5095,7 +5139,7 @@ class EndToEnd(unittest.TestCase):
term = self.start("--platform", "knulli", "--dest", str(root))
term.wait("files to download")
term.send("o")
term.wait("Each row is an option")
term.wait("RetroBIOS: Change")
for _step in range(20):
if "> Put RetroBIOS on this card: no" in term.settle():
break
@@ -5231,13 +5275,17 @@ class EndToEnd(unittest.TestCase):
term = self.start("--platform", "retroarch", "--dest", str(repo.dest))
term.wait("6 files to download")
term.send("o")
screen = term.wait("Each row is an option")
screen = term.wait("RetroBIOS: Change")
self.assertIn("--no-game-data", screen)
term.send("\r")
term.wait("Preferred regions")
term.send(DOWN, "\r", "\x1b")
screen = term.wait("Preferred regions: North America")
term.send(DOWN, RIGHT, "\x1b")
for _step in range(8):
if "> Game data: kept" in term.settle():
break
term.send(DOWN)
term.send(RIGHT, "\x1b")
# Back on the plan, the check runs again under the new options.
screen = term.wait("4 files to download")
self.assertIn("> Install", screen)
@@ -10688,7 +10736,7 @@ class MouseEnabled(unittest.TestCase):
self.assertFalse(install.tui_wants_mouse(state, drawn(state, 80, 24)))
# The result takes a tap on its Exit: its text is printed again in
# the terminal once it closes, selected there as anywhere.
for name in ("setup", "platforms", "plan", "systems", "options", "error", "quit",
for name in ("setup", "platforms", "plan", "systems", "change", "error", "quit",
"result"):
with self.subTest(screen=name):
state = states[name]
@@ -10749,6 +10797,13 @@ class MouseOnEveryScreen(unittest.TestCase):
self.assertEqual(lines[line].style == "focus", index == focus)
# The whole width, the text's end or not.
effects = tap(state, cols - 1, line)
if row_list[index].action == "heading":
# A heading names the rows under it: no
# choice, nothing happens.
self.assertEqual(effects, [])
self.assertEqual((state.stack, state.focus),
(base.stack, base.focus))
continue
if row_list[index].action in install.TUI_TICK_ROWS:
# A tick is ticked as Space ticks it, focus
# moved there first.
@@ -13357,7 +13412,7 @@ class TheSettleRunsFromTheFrameShown(unittest.TestCase):
term.wait("> Install")
term.settle()
term.send("o")
term.wait("RetroBIOS: Options")
term.wait("RetroBIOS: Change")
term.settle()
time.sleep(1.0)
col, line = self.bar_entry(term, "Back")
@@ -15119,7 +15174,7 @@ SCREEN_WORDS = {
"folder-card": (52, 52, 50, 50), "browse": (17, 17, 15, 15), "path": (23, 23, 25, 25),
"create": (23, 23, 21, 21), "checking": (18, 18, 17, 17), "plan": (31, 31, 31, 31),
"plan-in-place": (22, 22, 19, 19), "systems": (36, 37, 36, 36),
"options": (40, 42, 59, 59), "regions": (45, 44, 42, 42), "cores": (47, 47, 45, 45),
"regions": (45, 44, 42, 42), "cores": (47, 47, 45, 45),
"preview": (29, 29, 28, 28), "command": (22, 22, 16, 16), "confirm": (22, 22, 20, 20),
"installing": (32, 32, 31, 31), "result": (33, 33, 31, 31), "error": (37, 34, 32, 31),
"quit": (14, 14, 12, 12), "help": (24, 26, 26, 26), "plan-games": (36, 46, 54, 54),
@@ -15129,6 +15184,13 @@ SCREEN_WORDS = {
# press back to the whole list) and Change where (back to the setups,
# the plan having opened on the one found).
"plan-record": (38, 42, 42, 42),
# Change holds every option one level below the plan (design 4.4): the
# rows for Emulators, a pack, the device and the internet, and the five
# headings that group them, the intro sentence they replace dropped.
"change": (32, 42, 72, 72),
# A list of boards and one of emulators, each with the line that says
# what the choice does and the gloss of "board".
"device": (35, 35, 33, 33), "emulators": (29, 29, 27, 27),
}
@@ -15313,7 +15375,7 @@ class WhatToActOnStaysOnTheScreen(unittest.TestCase):
state = checked_state()
state.manifest = {**MANIFEST, "save_directory": {"folder": "saves", "by_core": True}}
state.check_summary = {"skipped": [{"reason": "no_save_directory", "files": 2}]}
self.assertIn("Choose it in Options, Save folder (--save-dir).", self.text(state))
self.assertIn("Choose it in Change, Save folder (--save-dir).", self.text(state))
def test_files_that_could_not_be_read_are_counted_where_no_row_says_so(self):
unread = [{"event": "unreadable", "platform": "retroarch", "folder": "/media/OLD",
@@ -15340,5 +15402,586 @@ class WhatToActOnStaysOnTheScreen(unittest.TestCase):
self.assertNotIn("Read the first", text)
class DeviceAndEmulatorScreens(unittest.TestCase):
"""Device chooses --target and --every-board, Emulators --emulator, and
Change keeps every option one level below the plan, grouped by intent."""
def setUp(self):
patcher = mock.patch.object(install, "_mounted_cards", return_value=[])
patcher.start()
self.addCleanup(patcher.stop)
def text(self, state: install.TuiState, cols: int = 80, rows: int = 24) -> str:
return "\n".join(str(line) for line in install.tui_view(state, cols, rows))
def focus(self, state: install.TuiState, action: str, value: object = None) -> None:
rows = install.tui_rows(state)
state.focus[state.screen] = next(
index for index, row in enumerate(rows)
if row.action == action and (value is None or row.value == value))
def test_change_groups_every_option_under_headings_focus_skips(self):
state = checked_state()
press(state, "o")
rows = install.tui_rows(state)
headings = [row.label for row in rows if row.action == "heading"]
self.assertEqual(headings, list(install.TUI_CHANGE_GROUPS))
self.assertNotEqual(rows[state.focus["options"]].action, "heading")
flags = {row.right for row in rows if row.action != "heading"}
for flag in ("--region", "--core", "--emulator", "--no-game-data", "--dest", "--from",
"--from-pack", "--target", "--installed-cores", "--jobs", "--offline",
"--dry-run"):
self.assertIn(flag, flags)
# Down and Up never stop on a heading, Home neither.
for key in ("down",) * len(rows) + ("up",) * len(rows) + ("home", "end", "home"):
press(state, key)
self.assertNotEqual(install.tui_rows(state)[state.focus["options"]].action,
"heading", key)
# Up on the first row stays: the list does not wrap past its heading.
press(state, "home", "up")
self.assertEqual(state.focus["options"], 1)
# The plan's row and bar name the screen.
plan = checked_state()
self.assertIn("Change", [row.label for row in install.tui_rows(plan)])
self.assertIn("Y/o Change", self.text(plan))
def test_a_tap_on_a_heading_does_nothing(self):
state = checked_state()
press(state, "o")
before = dict(state.focus)
rows = install.tui_rows(state)
index = next(i for i, row in enumerate(rows) if row.action == "heading")
state.cols, state.rows = 80, 24
state.hits = install.tui_hits(state, install.tui_view(state, 80, 24))
number = next(target[0] for target in state.hits.targets
if target[3][:2] == ("row", index))
state.now = state.hits.shown + 1
install.tui_mouse(state, install.TuiMouse(0, 3, number + 1, False))
self.assertEqual(install.tui_mouse(state, install.TuiMouse(0, 3, number + 1, True)), [])
self.assertEqual(state.focus, before)
def test_device_lists_the_card_board_first_and_chosen(self):
state = device_state()
self.assertEqual(state.screen, "device")
rows = install.tui_rows(state)
self.assertEqual([row.label for row in rows],
["(x) h700", "( ) bcm2711", "( ) x86_64", "( ) Every board"])
self.assertEqual(rows[0].right, "named by the card")
self.assertEqual(rows[1].right, "rpi4")
self.assertEqual(state.focus["device"], 0)
# Chosen as it is: nothing to say on the command line.
press(state, "enter")
self.assertEqual(state.screen, "options")
self.assertEqual([flag for flag in install.tui_flags(state)
if flag[0] in ("--target", "--every-board")], [])
self.assertIn("Device: h700, named by the card", self.text(state))
def test_on_the_device_itself_it_says_this_device(self):
state = checked_state()
state.board_seen = ("retroarch", state.dest, "bcm2711", True, True)
state.stack.append("options")
self.assertIn("Device: bcm2711, this device", self.text(state))
def test_the_board_event_is_kept_across_checks(self):
state = checked_state()
state.run, state.running = "check", True
install.tui_receive(state, ("event", state.run_id, {
"event": "board", "platform": "retroarch", "board": "h700", "applied": True,
"message": "Board h700 found on the card (--every-board installs all boards)"}))
with mock.patch.object(install, "detect_board", return_value=None):
self.assertEqual(install._board_seen(state)[:2], ("h700", True))
install._start_run(state, "check")
self.assertEqual(install._board_seen(state)[:2], ("h700", True))
def test_another_board_and_every_board_set_their_flag(self):
state = device_state()
self.focus(state, "board", "bcm2711")
press(state, "enter")
self.assertIn(("--target", "bcm2711"), install.tui_flags(state))
self.assertIn("--target=bcm2711", install.tui_argv(state, "install", "py", "i.py"))
self.assertIn("Device: bcm2711", self.text(state))
# Back on the plan, counted again for the board.
self.assertEqual(press(state, "esc"), [("run", "check")])
state = device_state()
self.focus(state, "board", "")
press(state, "enter")
self.assertEqual([flag for flag in install.tui_flags(state)
if flag[0] in ("--target", "--every-board")],
[("--every-board", None)])
self.assertIn("Device: every board", self.text(state))
def test_with_no_board_read_every_board_is_the_choice(self):
state = device_state(board="")
self.assertEqual(install.tui_rows(state)[-1].label, "(x) Every board")
self.assertEqual(state.focus["device"], 3)
self.assertEqual([flag for flag in install.tui_flags(state)
if flag[0] in ("--target", "--every-board")], [])
# Chosen, it is said: a card put in the reader later names none.
press(state, "enter")
self.assertEqual([flag for flag in install.tui_flags(state)
if flag[0] in ("--target", "--every-board")],
[("--every-board", None)])
def test_a_board_list_that_cannot_be_read_is_said(self):
state = checked_state()
state.stack.append("options")
self.focus(state, "device")
press(state, "enter")
self.assertIn("Reading the board list...", self.text(state))
install.tui_receive(state, ("targets", "retroarch", None,
"The board list could not be read."))
self.assertIn("The board list could not be read.", self.text(state))
self.assertEqual(press(state, "esc"), [])
self.assertEqual(state.screen, "options")
def test_the_command_line_choice_is_the_one_shown(self):
args = _args(platform="retroarch", dest=SYSTEM_DIR, target="bcm2711",
emulator=["mesen"])
state, _effects = install.tui_start(args, "linux", detect=False)
self.assertEqual((state.target, state.emulators), ("bcm2711", ["mesen"]))
self.assertNotIn("--target", [flag for flag, _value in state.passthrough])
flags = install.tui_flags(state)
self.assertIn(("--target", "bcm2711"), flags)
self.assertIn(("--emulator", "mesen"), flags)
def test_emulators_ticked_are_the_emulator_flags(self):
state = checked_state()
state.stack.append("options")
self.focus(state, "emulators")
press(state, "enter")
self.assertEqual(state.screen, "emulators")
rows = install.tui_rows(state)
self.assertEqual([row.label for row in rows if row.action == "emulator"],
["[ ] mesen", "[ ] pcsx_rearmed"])
self.assertEqual(rows[state.focus["emulators"]].value, "mesen")
press(state, " ")
self.assertEqual(state.emulators, ["mesen"])
self.assertIn(("--emulator", "mesen"), install.tui_flags(state))
press(state, "n")
self.assertEqual(state.emulators, [])
press(state, " ", "esc")
self.assertEqual(state.screen, "options")
self.assertIn("Emulators: mesen", self.text(state))
def test_without_the_internet_sets_offline_for_the_runs(self):
state = checked_state()
state.stack.append("options")
self.focus(state, "offline")
with mock.patch.dict(os.environ, {}, clear=False):
os.environ.pop(install.OFFLINE_VARIABLE, None)
self.assertIn("Without the internet: no", self.text(state))
self.assertEqual(press(state, "enter"), [("offline", True)])
install.set_offline(True)
self.assertIn("Without the internet: yes", self.text(state))
self.assertIn(("--offline", None), install.tui_flags(state))
self.assertEqual(press(state, "left"), [("offline", False)])
install.set_offline(False)
self.assertNotIn(("--offline", None), install.tui_flags(state))
def test_from_a_pack_opens_the_browser_on_zips_and_reads_the_pack(self):
root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
self.addCleanup(shutil.rmtree, root, True)
(root / "Downloads").mkdir()
pack = root / "Downloads" / "RetroArch_BIOS_Pack.zip"
pack.write_bytes(b"PK")
(root / "Downloads" / "notes.txt").write_text("x")
state = checked_state()
state.home = root
state.stack.append("options")
self.focus(state, "pack")
press(state, "enter")
self.assertEqual((state.screen, state.browse_for), ("browse", "pack"))
rows = install.tui_rows(state)
self.assertNotIn("use", [row.action for row in rows])
self.assertEqual([row.label for row in rows if row.action == "zip"],
["RetroArch_BIOS_Pack.zip"])
self.focus(state, "zip")
self.assertIn("A/Enter Use this pack", self.text(state))
self.assertEqual(press(state, "enter"), [("pack", pack)])
# Refused: said, the browser stays.
install.tui_receive(state, ("pack_opened", pack, None, "Not a pack."))
self.assertEqual(state.screen, "browse")
self.assertIn("Not a pack.", self.text(state))
opened = mock.Mock()
effects = install.tui_receive(state, ("pack_opened", pack, opened, ""))
self.assertEqual(effects, [("use_pack", opened), ("fetch", "retroarch")])
self.assertEqual(state.packs, [pack])
self.assertIn(("--from-pack", str(pack)), install.tui_flags(state))
def test_open_tui_pack_refuses_another_platforms_pack(self):
root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
self.addCleanup(shutil.rmtree, root, True)
pack = root / "Batocera_BIOS_Pack.zip"
with zipfile.ZipFile(pack, "w") as archive:
archive.writestr("manifest.json", json.dumps({"platform": "batocera",
"files": []}))
previous = install.LOCAL.pack
opened, problem = install.open_tui_pack(pack, "retroarch")
self.assertIs(install.LOCAL.pack, previous)
self.assertIsNone(opened)
self.assertTrue(problem)
def _parity_list(root: Path) -> tuple[Path, Path]:
"""A RetroArch list whose cores per board pick other files, a shared
destination two cores want different bytes at, and the copies of every
file in a folder: an install from it needs no network."""
payloads = root / "copies"
payloads.mkdir()
entries = []
alternative = {}
for name, cores, systems in (("plat.bin", None, ["sony-playstation"]),
("pcsx/extra.bin", ["pcsx_rearmed"], ["sony-playstation"]),
("mesen/nes.bin", ["mesen"], ["nintendo-nes"]),
("nes_plat.bin", None, ["nintendo-nes"]),
("shared.bin", ["pcsx_rearmed"], ["sony-playstation"]),
("shared.bin#mesen", ["mesen"], ["nintendo-nes"])):
payload = name.encode() * 8
stored = name.replace("/", "_").replace("#", "_")
(payloads / stored).write_bytes(payload)
entry = {"dest": name.split("#")[0], "size": len(payload),
"sha1": hashlib.sha1(payload).hexdigest(),
"sha256": hashlib.sha256(payload).hexdigest(),
"repo_path": f"bios/{stored}", "cores": cores, "systems": systems}
if "#" in name:
alternative = {field: entry[field] for field in
("sha1", "sha256", "size", "repo_path", "cores")}
continue
entries.append(entry)
next(entry for entry in entries if entry["dest"] == "shared.bin")["alternatives"] = [
alternative]
listed = root / "list"
(listed / "targets").mkdir(parents=True)
manifest = {"manifest_version": 2, "platform": "retroarch", "files": entries,
"omitted_files": [],
"system_cores": {"sony-playstation": ["pcsx_rearmed"],
"nintendo-nes": ["mesen"]},
"core_names": {"pcsx_rearmed": ["pcsx_rearmed"], "mesen": ["mesen"]}}
(listed / "retroarch.json").write_text(json.dumps(manifest))
(listed / "targets" / "retroarch.json").write_text(json.dumps(
{"bcm2711": {"cores": ["pcsx_rearmed"]}, "h700": {"cores": ["mesen"]}}))
return listed / "retroarch.json", payloads
class ScreenChoicesInstallWhatTheirFlagInstalls(unittest.TestCase):
"""A board chosen on Device and an emulator ticked on Emulators install,
file for file and byte for byte, what --target and --emulator install."""
def setUp(self):
patcher = mock.patch.object(install, "_mounted_cards", return_value=[])
patcher.start()
self.addCleanup(patcher.stop)
self.root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
self.addCleanup(shutil.rmtree, self.root, True)
self.listed, self.copies = _parity_list(self.root)
self.manifest = json.loads(self.listed.read_text())
self.env = {**os.environ, "RETROBIOS_OS": "linux", "HOME": str(self.root),
install.OFFLINE_VARIABLE: "1"}
def run_install(self, argv: list[str]) -> dict[str, str]:
proc = subprocess.run(argv, env=self.env, capture_output=True, text=True,
timeout=120, check=False, stdin=subprocess.DEVNULL)
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
dest = Path(next(arg for arg in argv if arg.startswith("--dest=")).split("=", 1)[1])
return {path.relative_to(dest).as_posix(): hashlib.sha1(path.read_bytes()).hexdigest()
for path in dest.rglob("*") if path.is_file()}
def flag_run(self, name: str, *flags: str) -> dict[str, str]:
return self.run_install([sys.executable, str(REPO_ROOT / "install.py"),
"--platform=retroarch",
f"--dest={self.root / name / 'system'}",
f"--manifest={self.listed}", f"--from={self.copies}",
*flags, "--events", "--yes"])
def screen(self, name: str) -> install.TuiState:
"""Change, over the plan of the list as a check leaves it."""
state = install.TuiState(os_type="linux", home=self.root)
state.platform, state.dest = "retroarch", self.root / name / "system"
state.manifest, state.manifest_for = self.manifest, "retroarch"
state.manifest_path, state.manifest_given = self.listed, True
state.from_folders = [self.copies]
state.check = dict(CHECK)
state.stack = ["plan", "options"]
return state
def screen_run(self, state: install.TuiState) -> dict[str, str]:
return self.run_install(install.tui_argv(state, "install", sys.executable,
str(REPO_ROOT / "install.py")))
@staticmethod
def choose(state: install.TuiState, action: str, value: object = None) -> None:
rows = install.tui_rows(state)
state.focus[state.screen] = next(
index for index, row in enumerate(rows)
if row.action == action and (value is None or row.value == value))
def test_a_board_chosen_on_device_installs_what_target_installs(self):
targets = install._validate_targets(
json.loads((self.listed.parent / "targets" / "retroarch.json").read_text()))
everything = self.flag_run("all")
chosen = {}
for board in ("bcm2711", "h700"):
with self.subTest(board=board):
state = self.screen(f"screen-{board}")
self.choose(state, "device")
self.assertEqual(press(state, "enter"), [("targets", "retroarch")])
install.tui_receive(state, ("targets", "retroarch", targets, ""))
self.choose(state, "board", board)
press(state, "enter")
self.assertEqual(state.screen, "options")
chosen[board] = self.screen_run(state)
self.assertEqual(chosen[board],
self.flag_run(f"flag-{board}", "--target", board))
self.assertNotEqual(chosen[board], everything)
# The two boards' cores want other bytes at one destination.
self.assertNotEqual(chosen["bcm2711"]["shared.bin"], chosen["h700"]["shared.bin"])
def test_every_board_installs_what_every_board_installs(self):
targets = install._validate_targets(
json.loads((self.listed.parent / "targets" / "retroarch.json").read_text()))
state = self.screen("screen-every")
state.board_seen = ("retroarch", state.dest, "h700", True, True)
self.choose(state, "device")
press(state, "enter")
install.tui_receive(state, ("targets", "retroarch", targets, ""))
self.choose(state, "board", "")
press(state, "enter")
self.assertEqual(self.screen_run(state),
self.flag_run("flag-every", "--every-board"))
def test_an_emulator_ticked_installs_what_emulator_installs(self):
for emulator in ("mesen", "pcsx_rearmed"):
with self.subTest(emulator=emulator):
state = self.screen(f"screen-{emulator}")
self.choose(state, "emulators")
press(state, "enter")
self.choose(state, "emulator", emulator)
press(state, " ", "esc")
self.assertEqual(state.screen, "options")
chosen = self.screen_run(state)
self.assertEqual(chosen, self.flag_run(f"flag-{emulator}",
"--emulator", emulator))
# The files of its systems, and its own copy of the shared one.
shared = next(entry for entry in self.manifest["files"]
if entry["dest"] == "shared.bin")
self.assertEqual(chosen["shared.bin"], shared["sha1"] if emulator ==
"pcsx_rearmed" else shared["alternatives"][0]["sha1"])
self.assertNotIn("nes_plat.bin" if emulator == "pcsx_rearmed"
else "plat.bin", chosen)
class EmulatorChoiceMatchesTheEmulatorPack(unittest.TestCase):
"""An emulator ticked on Emulators against the pack generate_pack.py
--emulator builds for it, on the real lists: every file of the pack at
its destination with its bytes, and beside them only the files the
platform's own list names for the systems the emulator runs."""
CASES = (("retroarch", "handy"), ("retroarch", "beetle_psx"), ("retroarch", "mesen"),
("batocera", "mesen"), ("batocera", "handy"))
@classmethod
def setUpClass(cls):
db_path = REPO_ROOT / "database.json"
if not db_path.exists():
raise unittest.SkipTest("database.json is not built")
if sys.version_info < (3, 9):
# The builder's floor: install.py alone keeps 3.8.
raise unittest.SkipTest("generate_pack.py needs Python 3.9 or newer")
sys.path.insert(0, str(REPO_ROOT / "scripts"))
import generate_pack as gp
from common import build_zip_contents_index
cls.gp = gp
cls.db = json.loads(db_path.read_text(encoding="utf-8"))
cls.zip_contents = build_zip_contents_index(cls.db)
cls.by_path = {entry["path"]: sha1 for sha1, entry in cls.db["files"].items()}
def pack(self, emulator: str) -> dict[str, str]:
record = self.gp.PackRecord()
with contextlib.redirect_stdout(io.StringIO()):
self.gp.generate_emulator_pack(
[emulator], str(REPO_ROOT / "emulators"), self.db, str(REPO_ROOT / "bios"),
str(REPO_ROOT / "tmp"), zip_contents=self.zip_contents, offline=True,
record=record)
return {member: self.by_path[record.sources[member]] for member in record.files}
def test_each_case(self):
for plat, emulator in self.CASES:
with self.subTest(platform=plat, emulator=emulator):
manifest = json.loads((REPO_ROOT / "install" / f"{plat}.json").read_text())
state = install.TuiState(os_type="linux")
state.platform, state.dest = plat, Path("/x/bios")
state.manifest, state.manifest_for = manifest, plat
state.stack = ["plan", "options", "emulators"]
rows = install.tui_rows(state)
state.focus["emulators"] = next(i for i, row in enumerate(rows)
if row.value == emulator)
press(state, " ")
argv = install.tui_argv(state, "install", "py", "install.py")
args = install.build_parser().parse_args(argv[2:])
self.assertEqual(args.emulator, [emulator])
with contextlib.redirect_stdout(io.StringIO()):
files, _omitted = install._narrow_to_emulators(
args.emulator, manifest, plat, manifest["files"],
manifest["omitted_files"])
installed = {entry["dest"]: entry["sha1"] for entry in files}
packed = self.pack(emulator)
self.assertTrue(packed)
self.assertEqual({dest: installed.get(dest) for dest in packed}, packed)
for dest in set(installed) - set(packed):
entry = next(entry for entry in files if entry["dest"] == dest)
self.assertIsNone(entry.get("cores"), dest)
class DeviceAndEmulatorReview(unittest.TestCase):
"""The review of 2026-11-11: each finding, held."""
def setUp(self):
patcher = mock.patch.object(install, "_mounted_cards", return_value=[])
patcher.start()
self.addCleanup(patcher.stop)
@staticmethod
def focus(state: install.TuiState, action: str, value: object = None) -> None:
rows = install.tui_rows(state)
state.focus[state.screen] = next(
index for index, row in enumerate(rows)
if row.action == action and (value is None or row.value == value))
def text(self, state: install.TuiState) -> str:
return "\n".join(str(line) for line in install.tui_view(state, 80, 24))
def test_every_board_is_said_whatever_the_run_started_with(self):
for start in ({"target": "bcm2711"}, {"every_board": True}, {}):
with self.subTest(start=start):
state = device_state(board="")
state.target = start.get("target", "")
state.every_board = start.get("every_board", False)
self.focus(state, "board", "")
press(state, "enter")
self.assertEqual([flag for flag in install.tui_flags(state)
if flag[0] in ("--target", "--every-board")],
[("--every-board", None)])
# The board the run reads itself clears both.
state = device_state()
state.every_board = True
self.focus(state, "board", "h700")
press(state, "enter")
self.assertEqual((state.target, state.every_board), ("", False))
def test_a_board_list_that_failed_is_fetched_again(self):
state = checked_state()
state.stack.append("options")
self.focus(state, "device")
self.assertEqual(press(state, "enter"), [("targets", "retroarch")])
install.tui_receive(state, ("targets", "retroarch", None,
"The board list could not be read."))
self.assertIn("The board list could not be read.", self.text(state))
press(state, "esc")
self.assertEqual(press(state, "enter"), [("targets", "retroarch")])
self.assertIn("Reading the board list...", self.text(state))
def test_a_pack_is_read_only_once_the_screen_takes_it(self):
state = checked_state()
state.stack += ["options", "browse"]
state.browse_for = "pack"
pack = mock.Mock()
effects = install.tui_receive(state, ("pack_opened", Path("p.zip"), pack, ""))
self.assertEqual(effects[0], ("use_pack", pack))
# Left before it opened: closed, never read.
state = checked_state()
effects = install.tui_receive(state, ("pack_opened", Path("p.zip"), pack, ""))
self.assertEqual(effects, [("close_pack", pack)])
self.assertEqual(state.packs, [])
def test_open_tui_pack_leaves_the_run_s_pack_alone(self):
root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
self.addCleanup(shutil.rmtree, root, True)
pack = root / "RetroArch_BIOS_Pack.zip"
with zipfile.ZipFile(pack, "w") as archive:
archive.writestr("manifest.json", json.dumps({
"platform": "retroarch", "files": [
{"dest": "a.bin", "path": "a.bin", "sha1": "0" * 40, "size": 1}]}))
archive.writestr("a.bin", b"a")
previous = install.LOCAL.pack
opened, problem = install.open_tui_pack(pack, "retroarch")
self.addCleanup(lambda: opened is not None and opened.close())
self.assertEqual(problem, "", problem)
self.assertIsNotNone(opened)
self.assertIs(install.LOCAL.pack, previous)
def test_a_zip_that_is_no_pack_is_closed(self):
root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
self.addCleanup(shutil.rmtree, root, True)
notpack = root / "photos.zip"
with zipfile.ZipFile(notpack, "w") as archive:
archive.writestr("a.jpg", b"x")
opened = []
real = zipfile.ZipFile
def tracked(*args, **kwargs):
archive = real(*args, **kwargs)
opened.append(archive)
return archive
with mock.patch.object(install.zipfile, "ZipFile", side_effect=tracked):
with self.assertRaises(ValueError):
install.PackSource([notpack])
self.assertTrue(opened)
self.assertTrue(all(archive.fp is None for archive in opened))
def test_another_setup_drops_the_board_and_emulators(self):
state = checked_state()
state.target, state.every_board, state.emulators = "bcm2711", False, ["mesen"]
install._set_platform(state, "batocera")
self.assertEqual((state.target, state.every_board, state.emulators),
("", False, []))
state = checked_state()
state.target, state.emulators = "bcm2711", ["mesen"]
install._set_platform(state, "retroarch")
self.assertEqual((state.target, state.emulators), ("bcm2711", ["mesen"]))
def test_names_the_new_list_lacks_are_left_out_and_said(self):
state = checked_state()
state.emulators, state.target = ["mesen", "gone_core"], "nowhere"
state.targets, state.targets_for = dict(TARGETS), "retroarch"
install._known_choice(state, "retroarch", MANIFEST)
self.assertEqual((state.emulators, state.target), (["mesen"], ""))
self.assertEqual(state.status, "Left out, not in this list: --emulator gone_core, "
"--target nowhere.")
def test_page_up_and_home_never_rest_on_a_heading(self):
state = checked_state()
press(state, "o")
rows = install.tui_rows(state)
for start in (1, 6, len(rows) - 1):
for key in ("pgup", "home"):
with self.subTest(start=start, key=key):
state.focus["options"] = start
press(state, key)
self.assertEqual(state.focus["options"], 1)
def test_offline_is_refused_for_a_target_with_no_board_list_held(self):
state = checked_state()
state.target = "bcm2711"
state.stack.append("options")
self.focus(state, "offline")
with mock.patch.dict(os.environ, {}, clear=False):
os.environ.pop(install.OFFLINE_VARIABLE, None)
self.assertEqual(press(state, "enter"), [])
self.assertIn("Open Device once first", self.text(state))
state.targets, state.targets_for = dict(TARGETS), "retroarch"
self.assertEqual(press(state, "enter"), [("offline", True)])
def test_the_board_list_is_fetched_once_with_the_list_s_timeout(self):
inbox: queue.Queue = queue.Queue()
with mock.patch.object(install, "_fetch_json", return_value={}) as fetched, \
mock.patch.object(install, "local_targets", return_value=None):
install._tui_fetch_targets("retroarch", inbox, io.StringIO())
self.assertEqual(fetched.call_args.kwargs["attempts"], 1)
self.assertEqual(fetched.call_args.kwargs["timeout"], install.TUI_FETCH_TIMEOUT)
self.assertEqual(inbox.get_nowait(), ("targets", "retroarch", {}, ""))
if __name__ == "__main__":
unittest.main()
+31 -13
View File
@@ -186,18 +186,36 @@ keep, with the total to download under the list. It opens on its first system,
every system ticked: A or Enter ticks or unticks the one in focus, and **All** and
**None** (`a` and `n`) sit at the end of the list. B or Esc (**Done**) takes the
choice back to the plan, which counts it before **Install**: the list never installs
by itself, and Done with nothing ticked is refused. **Options** sets the
preferred regions, game data, the installed cores only, the cores, the parallel
downloads, the folder and, for a list with files read from the frontend's save
directory, that directory. Each row names the option it sets, and **Show the
command line** gives the same choice as one command for a script. **Preview the
file list** shows what would be downloaded and writes nothing. **Copy the BIOS
by itself, and Done with nothing ticked is refused. **Change** holds every
other option, grouped by what it decides. **What to install**: the preferred
regions, the cores, the emulators and the game data. **Where**: the BIOS
folder, the save directory for a list with files read there, a folder to copy
from, and **From a pack**, a pack ZIP downloaded before
([`--from-pack`](#option-from-pack)), whose own list is then read. **For which
device**: **Device** and the installed cores only. **How**: **Without the
internet** ([`--offline`](#option-offline)) and the parallel downloads.
**See**: the preview and the command line. Up and Down pass the headings. Each
row names the option it sets, and **Show the command line** gives the same
choice as one command for a script. **Preview the file list** shows what would
be downloaded and writes nothing.
**Device** lists the boards of the platform's board list by the names that
list gives them (`bcm2711`, `h700`), each with the devices it runs where the
list names them, then **Every board**. The board read on the device, or named
by the card in the reader (`boot/batocera.board`), comes first, marked
`this device` or `named by the card`, and is already chosen: it is what the
run does with nothing chosen. Another board sets
[`--target`](#option-target), **Every board** sets
[`--every-board`](#option-every-board) where a board was read. **Emulators**
ticks the emulators to install for, each one [`--emulator`](#option-emulator),
and none ticked installs for every emulator. A choice made on either screen
installs the files its option installs. **Copy the BIOS
files from an old card or a folder** opens the folder browser on the first
mounted card, and adds [`--from`](#copy-from-an-old-card): the plan then says how
many files that folder holds and how many are left to download, or that it
holds none of them. After an install
on a muOS, KNULLI or ROCKNIX card, the result offers **Put RetroBIOS on this
card (update from the console later)**, and **Options** has the same row
card (update from the console later)**, and **Change** has the same row
([On a handheld](#on-a-handheld-the-retrobios-port)). Files the BIOS
folder holds in the wrong place are counted on the plan, and **Show the files
in the wrong place** lists each one with its reason. Back on the plan
@@ -213,7 +231,7 @@ install starts.
| Continue, open, choose | Enter, and Space outside a list of ticks | A, Start |
| Tick, untick | Space, Right, Left | X, D-pad Right and Left (X alone inside muxterm, Y with muxterm's own keys) |
| Back | Esc, Backspace | B, Select (B alone with muxterm's own keys, where Select shows its keyboard) |
| Options | `o`, Tab | Y |
| Change | `o`, Tab | Y |
| Details | `d`, and `o` or Tab on the result | the Details row, Y on the result |
| Filter a long list | `/` then type, Enter on a lone match chooses it | muxterm: Select shows a keyboard. Elsewhere none (the setup list puts the SD cards after RetroArch) |
| Keys of the screen | `?`, F1 (F1 alone while typing a filter, where the help lists the keys that do not type) | |
@@ -844,7 +862,7 @@ counts them (`3 copied from the wrong place in the BIOS folder`).
By default the installer fetches everything the platform runs. At a terminal,
the plan screen of the [full-screen interface](#the-full-screen-interface)
offers **Install**, **Choose systems** and **Options**. With `--plain`, or where
offers **Install**, **Choose systems** and **Change**. With `--plain`, or where
the full screen cannot open (a terminal smaller than 40x12, `TERM=dumb`), the
same choice comes as questions after detection. A RetroArch run with `--plain`
prints this for the RetroArch list, `g` then pressed (written from install.py
@@ -889,7 +907,7 @@ game data, and the data files of an emulator's own data folder (Dolphin's
`g` leaves the game data of the choice out, as `--no-game-data` does, and
counts again (`g` then keeps it). The plan screen has the same choice as a row
of its own, **Game data: kept** with its count, one press toggling it to
**Game data: left out**, and Options keeps its **Game data** row too. Its
**Game data: left out**, and Change keeps its **Game data** row too. Its
count follows the systems chosen, as `g` counts them, and a choice that holds
no game data has no row. Given `--no-game-data`, neither the menu nor the plan
offers the game data back.
@@ -1591,7 +1609,7 @@ muxterm, as the muOS app does.
| A, Start | Choose, continue |
| B, Select | Back, and quit on the first screen |
| X | Tick or untick a system |
| Y | Options |
| Y | Change |
| L1, R1 | Page up, page down (not in muxterm, which keeps them for its own scrollback) |
| Hold Select, then Start | Quit at once (PortMaster port, muOS, Tools) |
@@ -1643,7 +1661,7 @@ gets the column that names both:
| Page | PgUp/PgDn | PgUp/PgDn, L1/R1 | L1/R1, PgUp/PgDn | D-pad Left/Right | D-pad Left/Right | L1/R1, PgUp/PgDn | L1/R1, PgUp/PgDn |
| Continue, open, choose | Enter | A/Enter, Start | A, Start, Enter | A, Start, Enter | Start, Enter | A, Start, Enter | A, Start, Enter |
| Tick, untick | Space, Right/Left | X, Space, Right/Left | X, Space, Right/Left | X, Space | Y, Space | X, Space, Right/Left | A, Space, Right/Left |
| Options | o, Tab | Y, o, Tab | Y, Tab, o | Y, Tab, o | o, Tab | Y, Tab, o | L2, Tab, o |
| Change | o, Tab | Y, o, Tab | Y, Tab, o | Y, Tab, o | o, Tab | Y, Tab, o | L2, Tab, o |
| Back, and quit on the first screen | Esc, Backspace | B/Esc, Backspace | B, Select, Esc | B, Select, Esc | B, Esc, Backspace | B, Select, Esc | R2, Esc, Backspace |
| Stop an install | Esc, Ctrl-C | B/Esc, Ctrl-C | B, Esc, Ctrl-C | B, Esc, Ctrl-C | B, Esc, Ctrl-C | B, Esc, Ctrl-C | R2, Esc, Ctrl-C |
| Quit at once | none | Select+Start | Select, then Start | Select, then Start | none | none | none |
@@ -1701,7 +1719,7 @@ This works for every system above too.
**The app, from a computer.** After an install on a muOS, KNULLI or ROCKNIX
card, the installer can put RetroBIOS on that card too, so the console checks
and updates its BIOS files later. The result screen offers **Put RetroBIOS on
this card (update from the console later)**, **Options** has the same row, the
this card (update from the console later)**, **Change** has the same row, the
line questions ask once (`[y/N]`), and a script adds
[`--add-app`](#option-add-app). Nothing is written without that choice.