chore: merge installer phase 1

This commit is contained in:
Abdessamad Derraz committed 2026-11-10 01:50:12 +01:00
commit efaae29085
64 files changed
+2423 -797

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 = "185fa0951fa9a7cce617547c72770819ef4df08ebb3b29857126713083179fe7"
$defaultInstallSha256 = "61176387d38c67e596c1669fee2bb3b981333ab0b808dd2e280a5a3870957738"
# 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
+417 -168
View File
File diff suppressed because it is too large. Load diff
+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="185fa0951fa9a7cce617547c72770819ef4df08ebb3b29857126713083179fe7"
DEFAULT_INSTALL_SHA256="61176387d38c67e596c1669fee2bb3b981333ab0b808dd2e280a5a3870957738"
# 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": 1132111,
"sha256": "185fa0951fa9a7cce617547c72770819ef4df08ebb3b29857126713083179fe7",
"size": 1143258,
"sha256": "61176387d38c67e596c1669fee2bb3b981333ab0b808dd2e280a5a3870957738",
"executable": false
},
{
@@ -71,8 +71,8 @@
"muos": {
"archive": {
"dest": "ARCHIVE/RetroBIOS.muxapp",
"size": 1185736,
"sha256": "cb245dc33b894a9720ae3734bf55512c2b0fca99e0bd33e8c12a72738085f5cc"
"size": 1196883,
"sha256": "2c84115b87ed4bcd4fdf4d64d56fc1221485cbd9822cfeb0770b07b9b4226e51"
},
"members": [
{
@@ -92,8 +92,8 @@
{
"name": "RetroBIOS/install.py",
"repo_path": "install.py",
"size": 1132111,
"sha256": "185fa0951fa9a7cce617547c72770819ef4df08ebb3b29857126713083179fe7",
"size": 1143258,
"sha256": "61176387d38c67e596c1669fee2bb3b981333ab0b808dd2e280a5a3870957738",
"executable": false
},
{
@@ -166,8 +166,8 @@
{
"dest": ".config/modules/retrobios/install.py",
"repo_path": "install.py",
"size": 1132111,
"sha256": "185fa0951fa9a7cce617547c72770819ef4df08ebb3b29857126713083179fe7",
"size": 1143258,
"sha256": "61176387d38c67e596c1669fee2bb3b981333ab0b808dd2e280a5a3870957738",
"executable": false
},
{
+16 -10
View File
@@ -444,14 +444,14 @@ class Sandbox:
with zipfile.ZipFile(system / "PSX BIOS.zip", "w") as archive:
archive.writestr("extra.bin", data["pcsx/extra.bin"])
# A check that flags a file ends on 3, as documented.
return self.run("retroarch", "--check", "--manifest", str(listed), home=home,
codes=(3,))
return self.run("retroarch", "--check", "--verbose", "--manifest", str(listed),
home=home, codes=(3,))
old = home / "old"
old.mkdir()
for dest in ("scph5500.bin", "scph5501.bin"):
(old / dest).write_bytes(data[dest])
return self.run("retroarch", "--yes", "--manifest", str(listed), "--from", str(old),
home=home, landed=True)
return self.run("retroarch", "--yes", "--verbose", "--manifest", str(listed),
"--from", str(old), home=home, landed=True)
def run(self, platform: str, *argv: str, landed: bool = False,
held: bool = False, home: Path | None = None, codes: tuple = (0,)) -> str:
@@ -551,7 +551,7 @@ BULK_SYSTEM = "sony-playstation"
def _bulk_line(box: Sandbox | _LazySandbox) -> str:
run = box.run("retroarch", "--dry-run", "--system", BULK_SYSTEM)
run = box.run("retroarch", "--dry-run", "--verbose", "--system", BULK_SYSTEM)
found = [line for line in run.splitlines() if re.match(r" \S+ pulls ", line)]
if len(found) != 1:
raise SampleFailed(f"a RetroArch run with --system {BULK_SYSTEM} names no core "
@@ -560,11 +560,17 @@ def _bulk_line(box: Sandbox | _LazySandbox) -> str:
def sample_writers(box: Sandbox) -> dict[str, Callable[[str], str]]:
"""The run samples, each from the run that prints it."""
install_run = lambda: box.run(WALKTHROUGH, "--yes", "--jobs", "1", landed=True) # noqa: E731
check_run = lambda: box.run(WALKTHROUGH, "--check", held=True) # noqa: E731
dry_run = lambda: box.run("retroarch", "--dry-run") # noqa: E731
"""The run samples, each from the run that prints it: the walk through a
run reads its --verbose lines, the default sample the lines it hides them
from."""
default_run = lambda: box.run(WALKTHROUGH, "--yes", "--jobs", "1", landed=True) # noqa: E731
install_run = lambda: box.run(WALKTHROUGH, "--yes", "--jobs", "1", "--verbose", # noqa: E731
landed=True)
check_run = lambda: box.run(WALKTHROUGH, "--check", "--verbose", held=True) # noqa: E731
dry_run = lambda: box.run("retroarch", "--dry-run", "--verbose") # noqa: E731
return {
"run-default": lambda _body: _fenced(
_lines_from(default_run(), "Installing ", "Next: ")),
"dry-run": lambda _body: (
f"A RetroArch dry run into an empty folder, with the list of "
f"{list_date('retroarch')}, printed this:\n\n"
@@ -598,7 +604,7 @@ def _refused_lines(text: str) -> list[str]:
"""The files a check flags, each with its reason and where its cores read it."""
lines = text.splitlines()
start = next((i for i, line in enumerate(lines)
if re.fullmatch(r" \d+ files? [a-z ]+:", line)), None)
if re.fullmatch(r" \d+ files? [a-z ]+\.", line)), None)
if start is None:
raise SampleFailed("the check printed no files not in place")
end = max(i for i, line in enumerate(lines) if line.lstrip().startswith("read by "))
+104 -22
View File
@@ -331,13 +331,11 @@
^[[0;1m ^[[0;1;35m4^[[0;1m files to download, ^[[0;1;35m1.0 MB^[[0m
BIOS folder: ^[[0;1m~/.config/retroarch/system^[[0m
^[[0;32m1 in place^[[0m, ^[[0;33m3 missing^[[0m, ^[[0;33m1 wrong copy^[[0m
The ^[[0;33m1 wrong copy^[[0m is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
^[[0;7;35m > Install^[[0m
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -363,6 +361,8 @@
@@ -372,11 +372,9 @@
^[[0;1m ^[[0;1;32mEvery file on the list is in place^[[0m
BIOS folder: ^[[0;1m~/.config/retroarch/system^[[0m
^[[0;32m5 in place^[[0m, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
^[[0;7;35m > Close^[[0m
Details
Options
@@ -404,6 +402,8 @@
@@ -657,11 +657,9 @@
=== confirm
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Confirm^[[0m
Counted again for these choices. Nothing is written before Install.
Counted again for these choices.
^[[0;1m ^[[0;1;35m1^[[0;1m file to download, ^[[0;1;35m16.0 KB^[[0m
BIOS folder: ^[[0;1m~/.config/retroarch/system^[[0m
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
^[[0;7;35m > Install^[[0m
Back
@@ -691,6 +689,8 @@
@@ -705,7 +705,7 @@
B, Ctrl-C or Select+Start stops. What is done stays done.
^[[0;1;31mFAILED^[[0m scph5500.bin (HTTP 404)
^[[0;32m ok^[[0m gba_bios.bin
@@ -744,11 +744,7 @@
^[[0;1;31mfailed:^[[0m scph5500.bin (HTTP 404)
Run it again to retry only those.
The files it replaced are kept in /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
To put them back: python3 install.py \
--restore /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Same choice without questions:
python3 install.py --platform retroarch --dest /home/user/.config/retroarch/system
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
@@ -776,7 +772,11 @@
^[[0;1;35mA/Enter^[[0m Exit
^[[0;1;35mA/Enter^[[0m Exit ^[[0;2mY/o^[[0m Details
=== error
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: ^[[0;1;31mSomething went wrong^[[0m
@@ -865,6 +865,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
?, F1 This help
@@ -896,7 +897,6 @@
Any key closes this help
@@ -906,11 +906,6 @@
^[[0;1m ^[[0;1;35m4^[[0;1m files to download, ^[[0;1;35m1.0 MB^[[0m
BIOS folder: ^[[0;1m~/.config/retroarch/system^[[0m
For the 412 games on this card.
One region where the games prove it: nintendo-gba Europe.
2 games get every BIOS of their system: Show the games.
^[[0;32m1 in place^[[0m, ^[[0;33m3 missing^[[0m, ^[[0;33m1 wrong copy^[[0m
The ^[[0;33m1 wrong copy^[[0m is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
^[[0;7;35m > Install^[[0m
For the games on this card: yes 412 games
@@ -918,6 +913,7 @@
Show the games
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -936,6 +932,10 @@
@@ -968,6 +968,88 @@
^[[0;1;35mA/Enter^[[0m Back
=== plan-details
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Details^[[0m
^[[0;32m1 in place^[[0m, ^[[0;33m3 missing^[[0m, ^[[0;33m1 wrong copy^[[0m
The ^[[0;33m1 wrong copy^[[0m is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
^[[0;1;35mA/Enter^[[0m Back
=== result-details
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Details^[[0m
To put them back: python3 install.py \
--restore /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Same choice without questions:
python3 install.py --platform retroarch --dest /home/user/.config/retroarch/system
+40 -14
View File
@@ -108,10 +108,10 @@
^[[0;1m ^[[0;1;35m4^[[0;1m files to download, ^[[0;1;35m1.0 MB^[[0m
BIOS folder:
^[[0;1m~/.config/retroarch/system^[[0m
^[[0;33m1 wrong copy^[[0m: backed up, replaced.
^[[0;7;35m > Install^[[0m
Choose systems
Copy from an old card or a folder
Details
Options
Quit
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
@@ -121,12 +121,12 @@
^[[0;1m ^[[0;1;32mEvery file on the list is in place^[[0m
BIOS folder:
^[[0;1m~/.config/retroarch/system^[[0m
^[[0;32m5 in place^[[0m, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
^[[0;7;35m > Close^[[0m
Details
Options
^[[0;1;35mA^[[0m Close ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
=== systems
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Choose systems^[[0m
@@ -210,14 +210,14 @@
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Confirm^[[0m
Counted again for these choices.
Nothing is written before Install.
^[[0;1m ^[[0;1;35m1^[[0;1m file to download, ^[[0;1;35m16.0 KB^[[0m
BIOS folder: ^[[0;1m~/.co.../retroarch/system^[[0m
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
^[[0;7;35m > Install^[[0m
Back
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2m?^[[0m Help
=== installing
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Installing^[[0m
@@ -230,7 +230,7 @@
is done stays done.
^[[0;1;31mFAILED^[[0m scph5500.bin (HTTP 404)
^[[0;32m ok^[[0m gba_bios.bin
^[[0;1;35mB^[[0m Stop ^[[0;2m?^[[0m Help
=== result
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: ^[[0;1;31mFinished with errors^[[0m
@@ -243,8 +243,8 @@
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
^[[0;2m 1-8 of 17^[[0m
^[[0;1;35mA^[[0m Exit ^[[0;2m^v^[[0m Scroll
Log: /tmp/retrobios-x/install.log
^[[0;1;35mA^[[0m Exit ^[[0;2mY^[[0m Details
=== error
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: ^[[0;1;31mSomething went wrong^[[0m
@@ -277,12 +277,12 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
?, F1 This help
BIOS folder:
^[[0;1m/home/user/.config/retroarch/system^[[0m
Any key closes this help
=== plan-games
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: RetroArch^[[0m
@@ -295,7 +295,7 @@
For the games on this card: yes 412
Only what starts: no
Show the games
^[[0;2m 1-4 of 8^[[0m
^[[0;2m 1-4 of 9^[[0m
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
=== games
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Games on this card^[[0m
@@ -310,3 +310,29 @@
^[[0;2m 1-5 of 9^[[0m
^[[0;1;35mA^[[0m Back ^[[0;2m^v^[[0m Scroll
=== plan-details
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Details^[[0m
^[[0;32m1 in place^[[0m, ^[[0;33m3 missing^[[0m, ^[[0;33m1 wrong copy^[[0m
The ^[[0;33m1 wrong copy^[[0m is replaced, the old
one kept in a backup folder.
Free space: 50.0 GB
^[[0;1;35mA^[[0m Back
=== result-details
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Details^[[0m
To put them back: python3 install.py \
--restore .../2026-10-23_1200
^[[0;2m Shortened to fit this window.^[[0m
Same choice without questions:
python3 install.py \
--platform retroarch \
--dest .../retroarch/system
^[[0;2m Shortened to fit this window.^[[0m
^[[0;1;35mA^[[0m Back
+50 -18
View File
@@ -132,15 +132,15 @@
^[[0;1m ^[[0;1;35m4^[[0;1m files to download, ^[[0;1;35m1.0 MB^[[0m
BIOS folder:
^[[0;1m~/.config/retroarch/system^[[0m
^[[0;33m1 wrong copy^[[0m: backed up, replaced.
^[[0;32m1 in place^[[0m, ^[[0;33m3 missing^[[0m, ^[[0;33m1 wrong copy^[[0m
Free space: 50.0 GB
^[[0;7;35m > Install^[[0m
Choose systems
Copy from an old card or a folder
Details
Options
Quit
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
=== plan-in-place
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: RetroArch^[[0m
@@ -148,15 +148,15 @@
^[[0;1m ^[[0;1;32mEvery file on the list is in place^[[0m
BIOS folder:
^[[0;1m~/.config/retroarch/system^[[0m
^[[0;32m5 in place^[[0m, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
^[[0;7;35m > Close^[[0m
Details
Options
^[[0;1;35mA^[[0m Close ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
=== systems
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Choose systems^[[0m
@@ -258,17 +258,17 @@
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Confirm^[[0m
Counted again for these choices.
Nothing is written before Install.
^[[0;1m ^[[0;1;35m1^[[0;1m file to download, ^[[0;1;35m16.0 KB^[[0m
BIOS folder: ^[[0;1m~/.co.../retroarch/system^[[0m
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
^[[0;7;35m > Install^[[0m
Back
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2m?^[[0m Help
=== installing
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Installing^[[0m
@@ -281,7 +281,7 @@
is done stays done.
^[[0;1;31mFAILED^[[0m scph5500.bin (HTTP 404)
^[[0;32m ok^[[0m gba_bios.bin
@@ -297,11 +297,11 @@
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
To put them back: python3 install.py \
--restore .../2026-10-23_1200
^[[0;2m Shortened to fit this window.^[[0m
^[[0;2m 1-11 of 17^[[0m
^[[0;1;35mA^[[0m Exit ^[[0;2m^v^[[0m Scroll
Log: /tmp/retrobios-x/install.log
^[[0;1;35mA^[[0m Exit ^[[0;2mY^[[0m Details
=== error
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: ^[[0;1;31mSomething went wrong^[[0m
@@ -340,6 +340,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
?, F1 This help
@@ -348,7 +349,6 @@
^[[0;1m/home/user/.config/retroarch/system^[[0m
Any key closes this help
=== plan-games
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: RetroArch^[[0m
@@ -356,13 +356,13 @@
^[[0;1m ^[[0;1;35m4^[[0;1m files to download, ^[[0;1;35m1.0 MB^[[0m
BIOS folder:
^[[0;1m~/.config/retroarch/system^[[0m
For the 412 games on this card.
^[[0;7;35m > Install^[[0m
For the games on this card: yes 412
Only what starts: no
Show the games
Choose systems
Copy from an old card or a folder
Details
Options
Quit
^[[0;1;35mA^[[0m Install ^[[0;2mB^[[0m Back ^[[0;2mY^[[0m Options ^[[0;2m?^[[0m Help
@@ -381,4 +381,36 @@
that they start:
GBA/demo.gba: not recognised
GBA/hack.gba: not recognised
^[[0;1;35mA^[[0m Back
=== plan-details
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Details^[[0m
^[[0;32m1 in place^[[0m, ^[[0;33m3 missing^[[0m, ^[[0;33m1 wrong copy^[[0m
The ^[[0;33m1 wrong copy^[[0m is replaced, the old
one kept in a backup folder.
Free space: 50.0 GB
^[[0;1;35mA^[[0m Back
=== result-details
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Details^[[0m
To put them back: python3 install.py \
--restore .../2026-10-23_1200
^[[0;2m Shortened to fit this window.^[[0m
Same choice without questions:
python3 install.py \
--platform retroarch \
--dest .../retroarch/system
^[[0;2m Shortened to fit this window.^[[0m
^[[0;1;35mA^[[0m Back
+73 -23
View File
@@ -203,13 +203,11 @@
^[[0;1m ^[[0;1;35m4^[[0;1m files to download, ^[[0;1;35m1.0 MB^[[0m
BIOS folder: ^[[0;1m~/.config/retroarch/system^[[0m
^[[0;32m1 in place^[[0m, ^[[0;33m3 missing^[[0m, ^[[0;33m1 wrong copy^[[0m
The ^[[0;33m1 wrong copy^[[0m is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
^[[0;7;35m > Install^[[0m
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -222,17 +220,17 @@
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Options ^[[0;2m?^[[0m Help
=== plan-in-place
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: RetroArch^[[0m
^[[0;1m ^[[0;1;32mEvery file on the list is in place^[[0m
BIOS folder: ^[[0;1m~/.config/retroarch/system^[[0m
^[[0;32m5 in place^[[0m, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
^[[0;7;35m > Close^[[0m
Details
Options
@@ -247,6 +245,8 @@
^[[0;1;35mA/Enter^[[0m Close ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Options ^[[0;2m?^[[0m Help
=== systems
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Choose systems^[[0m
@@ -401,11 +401,9 @@
=== confirm
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Confirm^[[0m
Counted again for these choices. Nothing is written before Install.
Counted again for these choices.
^[[0;1m ^[[0;1;35m1^[[0;1m file to download, ^[[0;1;35m16.0 KB^[[0m
BIOS folder: ^[[0;1m~/.config/retroarch/system^[[0m
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
^[[0;7;35m > Install^[[0m
Back
@@ -422,6 +420,8 @@
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2m?^[[0m Help
=== installing
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Installing^[[0m
@@ -433,7 +433,7 @@
B, Ctrl-C or Select+Start stops. What is done stays done.
^[[0;1;31mFAILED^[[0m scph5500.bin (HTTP 404)
^[[0;32m ok^[[0m gba_bios.bin
@@ -457,12 +457,7 @@
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
To put them back: python3 install.py \
--restore /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Same choice without questions:
python3 install.py --platform retroarch \
--dest /home/user/.config/retroarch/system
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
@@ -472,7 +467,12 @@
^[[0;1;35mA/Enter^[[0m Exit
^[[0;1;35mA/Enter^[[0m Exit ^[[0;2mY/o^[[0m Details
=== error
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: ^[[0;1;31mSomething went wrong^[[0m
@@ -529,6 +529,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
?, F1 This help
@@ -546,7 +547,6 @@
Any key closes this help
=== plan-games
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: RetroArch^[[0m
@@ -554,11 +554,6 @@
^[[0;1m ^[[0;1;35m4^[[0;1m files to download, ^[[0;1;35m1.0 MB^[[0m
BIOS folder: ^[[0;1m~/.config/retroarch/system^[[0m
For the 412 games on this card.
One region where the games prove it: nintendo-gba Europe.
2 games get every BIOS of their system: Show the games.
^[[0;32m1 in place^[[0m, ^[[0;33m3 missing^[[0m, ^[[0;33m1 wrong copy^[[0m
The ^[[0;33m1 wrong copy^[[0m is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
^[[0;7;35m > Install^[[0m
For the games on this card: yes 412 games
@@ -566,12 +561,17 @@
Show the games
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
^[[0;1;35mA/Enter^[[0m Install ^[[0;2mB/Esc^[[0m Back ^[[0;2mY/o^[[0m Options ^[[0;2m?^[[0m Help
=== games
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Games on this card^[[0m
@@ -597,4 +597,54 @@
^[[0;1;35mA/Enter^[[0m Back
=== plan-details
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Details^[[0m
^[[0;32m1 in place^[[0m, ^[[0;33m3 missing^[[0m, ^[[0;33m1 wrong copy^[[0m
The ^[[0;33m1 wrong copy^[[0m is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
^[[0;1;35mA/Enter^[[0m Back
=== result-details
^[[0;1m ^[[0;1;35mRetroBIOS^[[0;1m: Details^[[0m
To put them back: python3 install.py \
--restore /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Same choice without questions:
python3 install.py --platform retroarch \
--dest /home/user/.config/retroarch/system
^[[0;1;35mA/Enter^[[0m Back
+3 -3
View File
@@ -1,10 +1,8 @@
RetroBIOS: Confirm
Counted again for these choices. Nothing is written before Install.
Counted again for these choices.
1 file to download, 16.0 KB
BIOS folder: ~/.config/retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
@@ -34,6 +32,8 @@
+3 -3
View File
@@ -1,12 +1,12 @@
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
A Install B Back ? Help
+3 -3
View File
@@ -1,15 +1,15 @@
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
A Install B Back ? Help
+3 -3
View File
@@ -1,10 +1,8 @@
RetroBIOS: Confirm
Counted again for these choices. Nothing is written before Install.
Counted again for these choices.
1 file to download, 16.0 KB
BIOS folder: ~/.config/retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
@@ -21,4 +19,6 @@
A/Enter Install B/Esc Back ? Help
+1 -1
View File
@@ -3,6 +3,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
?, F1 This help
@@ -34,7 +35,6 @@
Any key closes this help
+2 -2
View File
@@ -3,10 +3,10 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
?, F1 This help
BIOS folder:
/home/user/.config/retroarch/system
Any key closes this help
+1 -1
View File
@@ -3,6 +3,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
?, F1 This help
@@ -11,5 +12,4 @@
/home/user/.config/retroarch/system
Any key closes this help
+1 -1
View File
@@ -3,6 +3,7 @@
Up/Down, k/j Move
A/Enter, Start Install
Y, o, Tab Options
d Details
B/Esc, Backspace Back
q, Ctrl-C Quit
?, F1 This help
@@ -20,5 +21,4 @@
Any key closes this help
+1 -1
View File
@@ -7,7 +7,7 @@
B, Ctrl-C or Select+Start stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
+1 -1
View File
@@ -8,5 +8,5 @@
is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
B Stop ? Help
+1 -1
View File
@@ -8,7 +8,7 @@
is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
+1 -1
View File
@@ -7,7 +7,7 @@
B, Ctrl-C or Select+Start stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
+3 -3
View File
@@ -2,13 +2,11 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -34,6 +32,8 @@
+1 -1
View File
@@ -3,10 +3,10 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options ? Help
+3 -3
View File
@@ -3,13 +3,13 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options ? Help
+3 -3
View File
@@ -2,13 +2,11 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -21,4 +19,6 @@
A/Enter Install B/Esc Back Y/o Options ? Help
+40
View File
@@ -0,0 +1,40 @@
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
A/Enter Back
+12
View File
@@ -0,0 +1,12 @@
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old
one kept in a backup folder.
Free space: 50.0 GB
A Back
+15
View File
@@ -0,0 +1,15 @@
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old
one kept in a backup folder.
Free space: 50.0 GB
A Back
+24
View File
@@ -0,0 +1,24 @@
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
A/Enter Back
+5 -5
View File
@@ -3,11 +3,6 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
For the 412 games on this card.
One region where the games prove it: nintendo-gba Europe.
2 games get every BIOS of their system: Show the games.
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
For the games on this card: yes 412 games
@@ -15,6 +10,7 @@
Show the games
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -33,6 +29,10 @@
+1 -1
View File
@@ -8,5 +8,5 @@
For the games on this card: yes 412
Only what starts: no
Show the games
1-4 of 8
1-4 of 9
A Install B Back Y Options ? Help
+1 -1
View File
@@ -3,13 +3,13 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
For the 412 games on this card.
> Install
For the games on this card: yes 412
Only what starts: no
Show the games
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options ? Help
+5 -5
View File
@@ -3,11 +3,6 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
For the 412 games on this card.
One region where the games prove it: nintendo-gba Europe.
2 games get every BIOS of their system: Show the games.
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
For the games on this card: yes 412 games
@@ -15,10 +10,15 @@
Show the games
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
A/Enter Install B/Esc Back Y/o Options ? Help
+3 -3
View File
@@ -2,11 +2,9 @@
Every file on the list is in place
BIOS folder: ~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
@@ -34,6 +32,8 @@
+4 -4
View File
@@ -3,10 +3,10 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
A Close B Back Y Options ? Help
+4 -4
View File
@@ -3,13 +3,13 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
A Close B Back Y Options ? Help
+3 -3
View File
@@ -2,11 +2,9 @@
Every file on the list is in place
BIOS folder: ~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
@@ -21,4 +19,6 @@
A/Enter Close B/Esc Back Y/o Options ? Help
+6 -6
View File
@@ -5,11 +5,7 @@
failed: scph5500.bin (HTTP 404)
Run it again to retry only those.
The files it replaced are kept in /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
To put them back: python3 install.py \
--restore /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Same choice without questions:
python3 install.py --platform retroarch --dest /home/user/.config/retroarch/system
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
@@ -37,4 +33,8 @@
A/Enter Exit
A/Enter Exit Y/o Details
+2 -2
View File
@@ -8,5 +8,5 @@
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
1-8 of 17
A Exit ^v Scroll
Log: /tmp/retrobios-x/install.log
A Exit Y Details
+5 -5
View File
@@ -8,8 +8,8 @@
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
To put them back: python3 install.py \
--restore .../2026-10-23_1200
Shortened to fit this window.
1-11 of 17
A Exit ^v Scroll
Log: /tmp/retrobios-x/install.log
A Exit Y Details
+7 -7
View File
@@ -6,12 +6,7 @@
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
To put them back: python3 install.py \
--restore /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Same choice without questions:
python3 install.py --platform retroarch \
--dest /home/user/.config/retroarch/system
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
@@ -21,4 +16,9 @@
A/Enter Exit
A/Enter Exit Y/o Details
+40
View File
@@ -0,0 +1,40 @@
RetroBIOS: Details
To put them back: python3 install.py \
--restore /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Same choice without questions:
python3 install.py --platform retroarch --dest /home/user/.config/retroarch/system
A/Enter Back
+12
View File
@@ -0,0 +1,12 @@
RetroBIOS: Details
To put them back: python3 install.py \
--restore .../2026-10-23_1200
Shortened to fit this window.
Same choice without questions:
python3 install.py \
--platform retroarch \
--dest .../retroarch/system
Shortened to fit this window.
A Back
+15
View File
@@ -0,0 +1,15 @@
RetroBIOS: Details
To put them back: python3 install.py \
--restore .../2026-10-23_1200
Shortened to fit this window.
Same choice without questions:
python3 install.py \
--platform retroarch \
--dest .../retroarch/system
Shortened to fit this window.
A Back
+24
View File
@@ -0,0 +1,24 @@
RetroBIOS: Details
To put them back: python3 install.py \
--restore /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Same choice without questions:
python3 install.py --platform retroarch \
--dest /home/user/.config/retroarch/system
A/Enter Back
+121 -71
View File
@@ -407,10 +407,10 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
@@ -420,28 +420,26 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
=== plan 80x24
RetroBIOS: RetroArch
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -454,6 +452,8 @@
A Install B Back Y Options
=== plan-in-place 40x12
RetroBIOS: RetroArch
@@ -461,12 +461,12 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
A Close B Back Y Options
=== plan-in-place 40x15
RetroBIOS: RetroArch
@@ -474,26 +474,24 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
A Close B Back Y Options
=== plan-in-place 80x24
RetroBIOS: RetroArch
Every file on the list is in place
BIOS folder: ~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
@@ -508,6 +506,8 @@
A Close B Back Y Options
=== systems 40x12
RetroBIOS: Choose systems
@@ -779,39 +779,37 @@
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
A Install B Back
=== confirm 40x15
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
A Install B Back
=== confirm 80x24
RetroBIOS: Confirm
Counted again for these choices. Nothing is written before Install.
Counted again for these choices.
1 file to download, 16.0 KB
BIOS folder: ~/.config/retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
@@ -828,6 +826,8 @@
A Install B Back
=== installing 40x12
RetroBIOS: Installing
@@ -839,7 +839,7 @@
B stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
B Stop
=== installing 40x15
@@ -852,7 +852,7 @@
B stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -868,7 +868,7 @@
B stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -883,35 +883,6 @@
B Stop
=== result 40x12
RetroBIOS: Finished with errors
3 installed, 1 failed.
BIOS folder:
/home/user/.config/retroarch/system
failed: scph5500.bin (HTTP 404)
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
A Exit
=== result 40x15
RetroBIOS: Finished with errors
3 installed, 1 failed.
BIOS folder:
/home/user/.config/retroarch/system
failed: scph5500.bin (HTTP 404)
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
A Exit
=== result 80x24
RetroBIOS: Finished with errors
@@ -921,7 +892,7 @@
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
@@ -936,7 +907,7 @@
A Exit
A Exit Y Details
=== error 40x12
RetroBIOS: Something went wrong
@@ -1006,12 +977,12 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
d Details
B, Select, Esc Back
q, Ctrl-C Quit
BIOS folder:
/home/user/.config/retroarch/system
Any key closes this help
=== help 40x15
RetroBIOS: Keys
@@ -1019,6 +990,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
d Details
B, Select, Esc Back
q, Ctrl-C Quit
@@ -1027,7 +999,6 @@
Any key closes this help
=== help 80x24
RetroBIOS: Keys
@@ -1035,6 +1006,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
d Details
B, Select, Esc Back
q, Ctrl-C Quit
@@ -1052,7 +1024,6 @@
Any key closes this help
=== plan-games 40x12
RetroBIOS: RetroArch
@@ -1065,7 +1036,7 @@
For the games on this card: yes 412
Only what starts: no
Show the games
1-4 of 8
1-4 of 9
A Install B Back Y Options
=== plan-games 40x15
RetroBIOS: RetroArch
@@ -1073,13 +1044,13 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
For the 412 games on this card.
> Install
For the games on this card: yes 412
Only what starts: no
Show the games
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
@@ -1089,11 +1060,6 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
For the 412 games on this card.
One region where the games prove it: nintendo-gba Europe.
2 games get every BIOS of their system: Show the games.
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
For the games on this card: yes 412 games
@@ -1101,12 +1067,17 @@
Show the games
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
=== games 80x24
RetroBIOS: Games on this card
@@ -1132,4 +1103,83 @@
A Back
=== plan-details 80x24
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
A Back
=== result-details 40x12
RetroBIOS: Details
Nothing more to say here.
A Back
=== result-details 40x15
RetroBIOS: Details
Nothing more to say here.
A Back
=== result-details 80x24
RetroBIOS: Details
Nothing more to say here.
A Back
+122 -72
View File
@@ -407,10 +407,10 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
@@ -420,28 +420,26 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
=== plan 80x24
RetroBIOS: RetroArch
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -454,6 +452,8 @@
A Install B Back Y Options
=== plan-in-place 40x12
RetroBIOS: RetroArch
@@ -461,12 +461,12 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
A Close B Back Y Options
=== plan-in-place 40x15
RetroBIOS: RetroArch
@@ -474,26 +474,24 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
A Close B Back Y Options
=== plan-in-place 80x24
RetroBIOS: RetroArch
Every file on the list is in place
BIOS folder: ~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
@@ -508,6 +506,8 @@
A Close B Back Y Options
=== systems 40x12
RetroBIOS: Choose systems
@@ -779,39 +779,37 @@
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
A Install B Back
=== confirm 40x15
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
A Install B Back
=== confirm 80x24
RetroBIOS: Confirm
Counted again for these choices. Nothing is written before Install.
Counted again for these choices.
1 file to download, 16.0 KB
BIOS folder: ~/.config/retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
@@ -828,6 +826,8 @@
A Install B Back
=== installing 40x12
RetroBIOS: Installing
@@ -840,7 +840,7 @@
stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
B Stop
=== installing 40x15
RetroBIOS: Installing
@@ -853,7 +853,7 @@
stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -868,7 +868,7 @@
B or Select+Start stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -883,35 +883,6 @@
B Stop
=== result 40x12
RetroBIOS: Finished with errors
3 installed, 1 failed.
BIOS folder:
/home/user/.config/retroarch/system
failed: scph5500.bin (HTTP 404)
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
A Exit
=== result 40x15
RetroBIOS: Finished with errors
3 installed, 1 failed.
BIOS folder:
/home/user/.config/retroarch/system
failed: scph5500.bin (HTTP 404)
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
A Exit
=== result 80x24
RetroBIOS: Finished with errors
@@ -921,7 +892,7 @@
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
@@ -936,7 +907,7 @@
A Exit
A Exit Y Details
=== error 40x12
RetroBIOS: Something went wrong
@@ -1006,12 +977,12 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
d Details
B, Select, Esc Back
q, Ctrl-C Quit
Select, then Start Quit at once
BIOS folder:
/home/user/.config/retroarch/system
Any key closes this help
=== help 40x15
RetroBIOS: Keys
@@ -1019,6 +990,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
d Details
B, Select, Esc Back
q, Ctrl-C Quit
Select, then Start Quit at once
@@ -1027,7 +999,6 @@
/home/user/.config/retroarch/system
Any key closes this help
=== help 80x24
RetroBIOS: Keys
@@ -1035,6 +1006,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
d Details
B, Select, Esc Back
q, Ctrl-C Quit
Select, then Start Quit at once
@@ -1052,7 +1024,6 @@
Any key closes this help
=== plan-games 40x12
RetroBIOS: RetroArch
@@ -1065,7 +1036,7 @@
For the games on this card: yes 412
Only what starts: no
Show the games
1-4 of 8
1-4 of 9
A Install B Back Y Options
=== plan-games 40x15
RetroBIOS: RetroArch
@@ -1073,13 +1044,13 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
For the 412 games on this card.
> Install
For the games on this card: yes 412
Only what starts: no
Show the games
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
@@ -1089,11 +1060,6 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
For the 412 games on this card.
One region where the games prove it: nintendo-gba Europe.
2 games get every BIOS of their system: Show the games.
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
For the games on this card: yes 412 games
@@ -1101,12 +1067,17 @@
Show the games
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
=== games 80x24
RetroBIOS: Games on this card
@@ -1132,4 +1103,83 @@
A Back
=== plan-details 80x24
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
A Back
=== result-details 40x12
RetroBIOS: Details
Nothing more to say here.
A Back
=== result-details 40x15
RetroBIOS: Details
Nothing more to say here.
A Back
=== result-details 80x24
RetroBIOS: Details
Nothing more to say here.
A Back
+122 -72
View File
@@ -407,10 +407,10 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
@@ -420,28 +420,26 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
=== plan 80x24
RetroBIOS: RetroArch
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -454,6 +452,8 @@
A Install B Back Y Options
=== plan-in-place 40x12
RetroBIOS: RetroArch
@@ -461,12 +461,12 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
A Close B Back Y Options
=== plan-in-place 40x15
RetroBIOS: RetroArch
@@ -474,26 +474,24 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
A Close B Back Y Options
=== plan-in-place 80x24
RetroBIOS: RetroArch
Every file on the list is in place
BIOS folder: ~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
@@ -508,6 +506,8 @@
A Close B Back Y Options
=== systems 40x12
RetroBIOS: Choose systems
@@ -779,39 +779,37 @@
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
A Install B Back
=== confirm 40x15
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
A Install B Back
=== confirm 80x24
RetroBIOS: Confirm
Counted again for these choices. Nothing is written before Install.
Counted again for these choices.
1 file to download, 16.0 KB
BIOS folder: ~/.config/retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
@@ -828,6 +826,8 @@
A Install B Back
=== installing 40x12
RetroBIOS: Installing
@@ -840,7 +840,7 @@
stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
B Stop
=== installing 40x15
RetroBIOS: Installing
@@ -853,7 +853,7 @@
stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -868,7 +868,7 @@
B or Select+Start stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -883,35 +883,6 @@
B Stop
=== result 40x12
RetroBIOS: Finished with errors
3 installed, 1 failed.
BIOS folder:
/home/user/.config/retroarch/system
failed: scph5500.bin (HTTP 404)
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
A Exit
=== result 40x15
RetroBIOS: Finished with errors
3 installed, 1 failed.
BIOS folder:
/home/user/.config/retroarch/system
failed: scph5500.bin (HTTP 404)
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
A Exit
=== result 80x24
RetroBIOS: Finished with errors
@@ -921,7 +892,7 @@
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
@@ -936,7 +907,7 @@
A Exit
A Exit Y Details
=== error 40x12
RetroBIOS: Something went wrong
@@ -1006,12 +977,12 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
d Details
B, Select, Esc Back
q, Ctrl-C Quit
Select, then Start Quit at once
BIOS folder:
/home/user/.config/retroarch/system
Any key closes this help
=== help 40x15
RetroBIOS: Keys
@@ -1019,6 +990,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
d Details
B, Select, Esc Back
q, Ctrl-C Quit
Select, then Start Quit at once
@@ -1027,7 +999,6 @@
/home/user/.config/retroarch/system
Any key closes this help
=== help 80x24
RetroBIOS: Keys
@@ -1035,6 +1006,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
Y, Tab, o Options
d Details
B, Select, Esc Back
q, Ctrl-C Quit
Select, then Start Quit at once
@@ -1052,7 +1024,6 @@
Any key closes this help
=== plan-games 40x12
RetroBIOS: RetroArch
@@ -1065,7 +1036,7 @@
For the games on this card: yes 412
Only what starts: no
Show the games
1-4 of 8
1-4 of 9
A Install B Back Y Options
=== plan-games 40x15
RetroBIOS: RetroArch
@@ -1073,13 +1044,13 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
For the 412 games on this card.
> Install
For the games on this card: yes 412
Only what starts: no
Show the games
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
@@ -1089,11 +1060,6 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
For the 412 games on this card.
One region where the games prove it: nintendo-gba Europe.
2 games get every BIOS of their system: Show the games.
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
For the games on this card: yes 412 games
@@ -1101,12 +1067,17 @@
Show the games
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
A Install B Back Y Options
=== games 80x24
RetroBIOS: Games on this card
@@ -1132,4 +1103,83 @@
A Back
=== plan-details 80x24
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
A Back
=== result-details 40x12
RetroBIOS: Details
Nothing more to say here.
A Back
=== result-details 40x15
RetroBIOS: Details
Nothing more to say here.
A Back
=== result-details 80x24
RetroBIOS: Details
Nothing more to say here.
A Back
+163 -55
View File
@@ -382,10 +382,10 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
Enter Install Esc Back o Options
@@ -395,28 +395,26 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
Enter Install Esc Back o Options
=== plan 80x24
RetroBIOS: RetroArch
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -429,6 +427,8 @@
Enter Install Esc Back o Options ? Help
=== plan-in-place 40x12
RetroBIOS: RetroArch
@@ -436,12 +436,12 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
Enter Close Esc Back o Options
=== plan-in-place 40x15
RetroBIOS: RetroArch
@@ -449,26 +449,24 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
Enter Close Esc Back o Options
=== plan-in-place 80x24
RetroBIOS: RetroArch
Every file on the list is in place
BIOS folder: ~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
@@ -483,6 +481,8 @@
Enter Close Esc Back o Options ? Help
=== systems 40x12
RetroBIOS: Choose systems
@@ -812,39 +812,37 @@
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
Enter Install Esc Back ? Help
=== confirm 40x15
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
Enter Install Esc Back ? Help
=== confirm 80x24
RetroBIOS: Confirm
Counted again for these choices. Nothing is written before Install.
Counted again for these choices.
1 file to download, 16.0 KB
BIOS folder: ~/.config/retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
@@ -861,6 +859,8 @@
Enter Install Esc Back ? Help
=== installing 40x12
RetroBIOS: Installing
@@ -873,7 +873,7 @@
stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
Esc Stop ? Help
=== installing 40x15
RetroBIOS: Installing
@@ -886,7 +886,7 @@
stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -901,7 +901,7 @@
Esc or Ctrl-C stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -927,8 +927,8 @@
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
1-8 of 17
Enter Exit ^v Scroll
Log: /tmp/retrobios-x/install.log
Enter Exit o Details
=== result 40x15
RetroBIOS: Finished with errors
@@ -940,11 +940,11 @@
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
To put them back: python3 install.py \
--restore .../2026-10-23_1200
Shortened to fit this window.
1-11 of 17
Enter Exit ^v Scroll
Log: /tmp/retrobios-x/install.log
Enter Exit o Details
=== result 80x24
RetroBIOS: Finished with errors
@@ -954,12 +954,7 @@
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
To put them back: python3 install.py \
--restore /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Same choice without questions:
python3 install.py --platform retroarch \
--dest /home/user/.config/retroarch/system
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
@@ -969,7 +964,12 @@
Enter Exit
Enter Exit o Details
=== error 40x12
RetroBIOS: Something went wrong
@@ -1084,12 +1084,12 @@
Up/Down, k/j Move
Enter Install
o, Tab Options
d Details
Esc, Backspace Back
q, Ctrl-C Quit
?, F1 This help
BIOS folder:
/home/user/.config/retroarch/system
Any key closes this help
=== help 40x15
RetroBIOS: Keys
@@ -1097,6 +1097,7 @@
Up/Down, k/j Move
Enter Install
o, Tab Options
d Details
Esc, Backspace Back
q, Ctrl-C Quit
?, F1 This help
@@ -1105,7 +1106,6 @@
/home/user/.config/retroarch/system
Any key closes this help
=== help 80x24
RetroBIOS: Keys
@@ -1113,6 +1113,7 @@
Up/Down, k/j Move
Enter Install
o, Tab Options
d Details
Esc, Backspace Back
q, Ctrl-C Quit
?, F1 This help
@@ -1130,7 +1131,6 @@
Any key closes this help
=== plan-games 40x12
RetroBIOS: RetroArch
@@ -1143,7 +1143,7 @@
For the games on this card: yes 412
Only what starts: no
Show the games
1-4 of 8
1-4 of 9
Enter Install Esc Back o Options
=== plan-games 40x15
RetroBIOS: RetroArch
@@ -1151,13 +1151,13 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
For the 412 games on this card.
> Install
For the games on this card: yes 412
Only what starts: no
Show the games
Choose systems
Copy from an old card or a folder
Details
Options
Quit
Enter Install Esc Back o Options
@@ -1167,11 +1167,6 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
For the 412 games on this card.
One region where the games prove it: nintendo-gba Europe.
2 games get every BIOS of their system: Show the games.
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
For the games on this card: yes 412 games
@@ -1179,12 +1174,17 @@
Show the games
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
Enter Install Esc Back o Options ? Help
=== games 40x12
RetroBIOS: Games on this card
@@ -1239,4 +1239,112 @@
Enter Back
=== plan-details 40x12
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old
one kept in a backup folder.
Free space: 50.0 GB
Enter Back
=== plan-details 40x15
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old
one kept in a backup folder.
Free space: 50.0 GB
Enter Back
=== plan-details 80x24
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
Enter Back
=== result-details 40x12
RetroBIOS: Details
To put them back: python3 install.py \
--restore .../2026-10-23_1200
Shortened to fit this window.
Same choice without questions:
python3 install.py \
--platform retroarch \
--dest .../retroarch/system
Shortened to fit this window.
Enter Back
=== result-details 40x15
RetroBIOS: Details
To put them back: python3 install.py \
--restore .../2026-10-23_1200
Shortened to fit this window.
Same choice without questions:
python3 install.py \
--platform retroarch \
--dest .../retroarch/system
Shortened to fit this window.
Enter Back
=== result-details 80x24
RetroBIOS: Details
To put them back: python3 install.py \
--restore /home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Same choice without questions:
python3 install.py --platform retroarch \
--dest /home/user/.config/retroarch/system
Enter Back
+126 -47
View File
@@ -436,10 +436,10 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install R2 Back L2 Options
@@ -449,28 +449,26 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install R2 Back L2 Options
=== plan 80x24
RetroBIOS: RetroArch
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -483,6 +481,8 @@
A Install R2 Back L2 Options
=== plan-in-place 40x12
RetroBIOS: RetroArch
@@ -490,12 +490,12 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
A Close R2 Back L2 Options
=== plan-in-place 40x15
RetroBIOS: RetroArch
@@ -503,26 +503,24 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
A Close R2 Back L2 Options
=== plan-in-place 80x24
RetroBIOS: RetroArch
Every file on the list is in place
BIOS folder: ~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
@@ -537,6 +535,8 @@
A Close R2 Back L2 Options
=== systems 40x12
RetroBIOS: Choose systems
@@ -808,39 +808,37 @@
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
A Install R2 Back
=== confirm 40x15
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
A Install R2 Back
=== confirm 80x24
RetroBIOS: Confirm
Counted again for these choices. Nothing is written before Install.
Counted again for these choices.
1 file to download, 16.0 KB
BIOS folder: ~/.config/retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
@@ -857,6 +855,8 @@
A Install R2 Back
=== installing 40x12
RetroBIOS: Installing
@@ -868,7 +868,7 @@
R2 stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
R2 Stop
=== installing 40x15
@@ -881,7 +881,7 @@
R2 stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -897,7 +897,7 @@
R2 stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -923,8 +923,8 @@
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
A Exit
Log: /tmp/retrobios-x/install.log
A Exit L2 Details
=== result 40x15
RetroBIOS: Finished with errors
@@ -936,11 +936,11 @@
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
A Exit
A Exit L2 Details
=== result 80x24
RetroBIOS: Finished with errors
@@ -950,7 +950,7 @@
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
@@ -965,7 +965,7 @@
A Exit
A Exit L2 Details
=== error 40x12
RetroBIOS: Something went wrong
@@ -1080,12 +1080,12 @@
D-pad, Up/Down Move
A, Start, Enter Install
L2, Tab, o Options
d Details
R2, Esc, Backspace Back
q, Ctrl-C Quit
B Sends no key here
BIOS folder:
/home/user/.config/retroarch/system
Any key closes this help
=== help 40x15
RetroBIOS: Keys
@@ -1093,6 +1093,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
L2, Tab, o Options
d Details
R2, Esc, Backspace Back
q, Ctrl-C Quit
B Sends no key here
@@ -1101,7 +1102,6 @@
/home/user/.config/retroarch/system
Any key closes this help
=== help 80x24
RetroBIOS: Keys
@@ -1109,6 +1109,7 @@
D-pad, Up/Down Move
A, Start, Enter Install
L2, Tab, o Options
d Details
R2, Esc, Backspace Back
q, Ctrl-C Quit
B Sends no key here
@@ -1126,7 +1127,6 @@
Any key closes this help
=== plan-games 40x12
RetroBIOS: RetroArch
@@ -1139,7 +1139,7 @@
For the games on this card: yes 412
Only what starts: no
Show the games
1-4 of 8
1-4 of 9
A Install R2 Back L2 Options
=== plan-games 40x15
RetroBIOS: RetroArch
@@ -1147,13 +1147,13 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
For the 412 games on this card.
> Install
For the games on this card: yes 412
Only what starts: no
Show the games
Choose systems
Copy from an old card or a folder
Details
Options
Quit
A Install R2 Back L2 Options
@@ -1163,11 +1163,6 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
For the 412 games on this card.
One region where the games prove it: nintendo-gba Europe.
2 games get every BIOS of their system: Show the games.
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
For the games on this card: yes 412 games
@@ -1175,12 +1170,17 @@
Show the games
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
A Install R2 Back L2 Options
=== games 80x24
RetroBIOS: Games on this card
@@ -1206,4 +1206,83 @@
A Back
=== plan-details 80x24
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
A Back
=== result-details 40x12
RetroBIOS: Details
Nothing more to say here.
A Back
=== result-details 40x15
RetroBIOS: Details
Nothing more to say here.
A Back
=== result-details 80x24
RetroBIOS: Details
Nothing more to say here.
A Back
+151 -43
View File
@@ -436,10 +436,10 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
Start Install B Back
@@ -449,28 +449,26 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
1 wrong copy: backed up, replaced.
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
> Install
Choose systems
Copy from an old card or a folder
Details
Options
Quit
Start Install B Back
=== plan 80x24
RetroBIOS: RetroArch
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
@@ -483,6 +481,8 @@
Start Install B Back
=== plan-in-place 40x12
RetroBIOS: RetroArch
@@ -490,12 +490,12 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
Start Close B Back
=== plan-in-place 40x15
RetroBIOS: RetroArch
@@ -503,26 +503,24 @@
Every file on the list is in place
BIOS folder:
~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced,
the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
Start Close B Back
=== plan-in-place 80x24
RetroBIOS: RetroArch
Every file on the list is in place
BIOS folder: ~/.config/retroarch/system
5 in place, 0 missing, 0 wrong copies
1 of the 0 wrong copies is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Close
Details
Options
@@ -537,6 +535,8 @@
Start Close B Back
=== systems 40x12
RetroBIOS: Choose systems
@@ -866,39 +866,37 @@
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
Start Install B Back
=== confirm 40x15
RetroBIOS: Confirm
Counted again for these choices.
Nothing is written before Install.
1 file to download, 16.0 KB
BIOS folder: ~/.co.../retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
Start Install B Back
=== confirm 80x24
RetroBIOS: Confirm
Counted again for these choices. Nothing is written before Install.
Counted again for these choices.
1 file to download, 16.0 KB
BIOS folder: ~/.config/retroarch/system
0 in place, 1 missing, 0 wrong copies
Free space: 50.0 GB
> Install
Back
@@ -915,6 +913,8 @@
Start Install B Back
=== installing 40x12
RetroBIOS: Installing
@@ -926,7 +926,7 @@
B stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
B Stop
=== installing 40x15
@@ -939,7 +939,7 @@
B stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -955,7 +955,7 @@
B stops. What is done stays done.
FAILED scph5500.bin (HTTP 404)
ok gba_bios.bin
@@ -981,7 +981,7 @@
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
Start Exit
=== result 40x15
RetroBIOS: Finished with errors
@@ -994,7 +994,7 @@
The files it replaced are kept in
/home/user/.config/retroarch/
retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
@@ -1008,7 +1008,7 @@
Run it again to retry only those.
The files it replaced are kept in
/home/user/.config/retroarch/retrobios-backup/2026-10-23_1200
Details: /tmp/retrobios-x/install.log
Log: /tmp/retrobios-x/install.log
@@ -1138,12 +1138,12 @@
D-pad, Up/Down Move
Start, Enter Install
o, Tab Options
d Details
B, Esc, Backspace Back
q, Ctrl-C Quit
Select Keyboard on or off
Menu Close the terminal
Any key closes this help
=== help 40x15
RetroBIOS: Keys
@@ -1151,6 +1151,7 @@
D-pad, Up/Down Move
Start, Enter Install
o, Tab Options
d Details
B, Esc, Backspace Back
q, Ctrl-C Quit
Select Keyboard on or off
@@ -1159,7 +1160,6 @@
BIOS folder:
/home/user/.config/retroarch/system
Any key closes this help
=== help 80x24
RetroBIOS: Keys
@@ -1167,6 +1167,7 @@
D-pad, Up/Down Move
Start, Enter Install
o, Tab Options
d Details
B, Esc, Backspace Back
q, Ctrl-C Quit
Select Keyboard on or off
@@ -1184,7 +1185,6 @@
Any key closes this help
=== plan-games 40x12
RetroBIOS: RetroArch
@@ -1197,7 +1197,7 @@
For the games on this card: yes 412
Only what starts: no
Show the games
1-4 of 8
1-4 of 9
Start Install B Back
=== plan-games 40x15
RetroBIOS: RetroArch
@@ -1205,13 +1205,13 @@
4 files to download, 1.0 MB
BIOS folder:
~/.config/retroarch/system
For the 412 games on this card.
> Install
For the games on this card: yes 412
Only what starts: no
Show the games
Choose systems
Copy from an old card or a folder
Details
Options
Quit
Start Install B Back
@@ -1221,11 +1221,6 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
For the 412 games on this card.
One region where the games prove it: nintendo-gba Europe.
2 games get every BIOS of their system: Show the games.
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
For the games on this card: yes 412 games
@@ -1233,12 +1228,17 @@
Show the games
Choose systems
Copy the BIOS files from an old card or a folder
Details
Options
Quit
Start Install B Back
=== games 40x12
RetroBIOS: Games on this card
@@ -1293,4 +1293,112 @@
Start Back
=== plan-details 40x12
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old
one kept in a backup folder.
Free space: 50.0 GB
Start Back
=== plan-details 40x15
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old
one kept in a backup folder.
Free space: 50.0 GB
Start Back
=== plan-details 80x24
RetroBIOS: Details
1 in place, 3 missing, 1 wrong copy
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
Start Back
=== result-details 40x12
RetroBIOS: Details
Nothing more to say here.
Start Back
=== result-details 40x15
RetroBIOS: Details
Nothing more to say here.
Start Back
=== result-details 80x24
RetroBIOS: Details
Nothing more to say here.
Start Back
+2 -2
View File
@@ -187,7 +187,7 @@ class InstallerSamplesSayWhatTheCodePrints(unittest.TestCase):
page = _page("installer.md")
refused = _generated(page, "check-refused")
self.assertRegex(refused, r"\n \d+ files? [a-z ]+:\n")
self.assertRegex(refused, r"\n \d+ files? [a-z ]+\.\n")
self.assertIn("read by beetle_psx, pcsx_rearmed at", refused)
self.assertNotRegex(page, r"opened at /\S+ by ")
self.assertIn('"read by ', inspect.getsource(install.opened_line))
@@ -201,7 +201,7 @@ class InstallerSamplesSayWhatTheCodePrints(unittest.TestCase):
at = page.index("<!-- generated: run-download -->")
before = page[page.rindex("\n\n", 0, at - 2):at]
self.assertIn("At a terminal one progress line", before)
self.assertIn("A pipe or a log gets one line per file", before)
self.assertIn("a pipe or a log gets one\nline per file", before)
def test_the_generator_pages_name_the_run_samples_and_step_7(self):
tools = _page("tools.md")
+30 -17
View File
@@ -331,11 +331,16 @@ class TestRunOutcome(unittest.TestCase):
return buf.getvalue(), code
def test_success_names_the_location_and_the_way_to_check(self):
install.OUTPUT.verbose = install.OUTPUT.where_said = False
out, code = self._report(downloaded=12, up_to_date=3)
self.assertEqual(code, 0)
self.assertIn("12 files installed", out)
self.assertIn("/userdata/bios", out)
self.assertIn("--check", out)
self.assertIn("Next: ", out)
self.assertNotIn("--check", out)
self.addCleanup(setattr, install.OUTPUT, "verbose", False)
install.OUTPUT.verbose = True
self.assertIn("--check", self._report(downloaded=12, up_to_date=3)[0])
def test_total_failure_does_not_say_done_and_exits_two(self):
out, code = self._report(errors=7)
@@ -523,7 +528,12 @@ class AMissingDestIsSaid(unittest.TestCase):
def test_one_missing_level_or_no_terminal_is_said_not_asked(self):
root = Path(_tempdir(self))
# A "no" that was asked would stop the run with 2.
# A "no" that was asked would stop the run with 2. Said with
# --verbose: the folder is made, and the run names it.
install.OUTPUT.verbose = False
self.assertEqual(self._call(root / "bios", True, "n"), ("", None))
self.addCleanup(setattr, install.OUTPUT, "verbose", False)
install.OUTPUT.verbose = True
out, code = self._call(root / "bios", True, "n")
self.assertIsNone(code)
self.assertIn("does not exist", out)
@@ -1512,7 +1522,7 @@ class InstallerStaysOnPython38AndTheStdlib(unittest.TestCase):
check=False,
)
self.assertEqual(proc.returncode, 3, proc.stdout + proc.stderr)
self.assertIn("to download:", proc.stdout)
self.assertIn("to download.", proc.stdout)
def test_no_syntax_a_312_parser_lets_through(self):
"""Parenthesized context managers (3.9) and f-strings reusing their
@@ -2157,7 +2167,7 @@ class TestLaunchboxDetectionPowershell(unittest.TestCase):
proc = subprocess.run(
[
"pwsh", "-NoProfile", "-File",
str(REPO_ROOT / "install.ps1"), "--standalone-copies",
str(REPO_ROOT / "install.ps1"), "--standalone-copies", "--verbose",
],
env=env,
capture_output=True,
@@ -4163,7 +4173,7 @@ class DataDirectoriesAreInstalled(unittest.TestCase):
"files": {"Sys/big.bin": {"sha1": hashlib.sha1(member).hexdigest(),
"size": len(member)}}}]
(repo.root / "install" / "retroarch.json").write_text(json.dumps(listed))
proc = repo.run("--no-input")
proc = repo.run("--no-input", "--verbose")
bios = sum(len(payload) for payload, *_ in FakeRepository.PAYLOADS.values())
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertIn(f"{install.format_size(bios + archive)} downloaded in", proc.stdout)
@@ -4447,7 +4457,7 @@ class ACheckNamesWhatTheCollectionLacks(unittest.TestCase):
}
proc = subprocess.run(
[sys.executable, "-I", "install.py", "--platform", "recalbox",
"--no-input", "--dest", str(home / "bios"), "--check"],
"--no-input", "--dest", str(home / "bios"), "--check", "--verbose"],
cwd=REPO_ROOT, env=env, capture_output=True, text=True, timeout=300,
check=False,
)
@@ -4969,6 +4979,8 @@ class TheInstallerSaysWhatItFound(unittest.TestCase):
(dest / "lynxboot.img").write_bytes(b"corrupt")
proc = self._run("--dest", str(dest), "--system", "atari-lynx", "--check")
self.assertEqual(proc.returncode, 3)
# A wrong copy is named in every mode: it is what the user acts on.
self.assertIn("1 file to download.", proc.stdout)
self.assertIn("wrong copy: lynxboot.img", proc.stdout)
self.assertIn("without --check", proc.stdout)
@@ -4988,7 +5000,7 @@ class TheInstallerSaysWhatItFound(unittest.TestCase):
def test_a_piped_log_keeps_its_order(self):
proc = subprocess.run(
[sys.executable, "install.py", "--platform", "retroarch", "--no-input",
"--target", "no-such-target", "--dest", str(self.home / "s")],
"--verbose", "--target", "no-such-target", "--dest", str(self.home / "s")],
cwd=REPO_ROOT, env=self.env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, timeout=300, check=False,
)
@@ -5653,9 +5665,9 @@ class RunsAgainstTheRealLists(unittest.TestCase, _LoopbackRun):
dest.mkdir()
(dest / "scph5501.bin").write_bytes(b"junk")
proc = _install_run(self.env, "--platform", "retroarch", "--dest", str(dest),
"--check", "--no-input")
"--check", "--no-input", "--verbose")
self.assertEqual(proc.returncode, 3, proc.stderr)
listing = proc.stdout.split("to download:\n", 1)[1].splitlines()
listing = proc.stdout.split("to download.\n", 1)[1].splitlines()
self.assertEqual(listing[0].strip(), "wrong copy: scph5501.bin")
def test_a_dry_run_counts_the_data_trees_and_ends_on_its_last_line(self):
@@ -5685,7 +5697,7 @@ class RunsAgainstTheRealLists(unittest.TestCase, _LoopbackRun):
"bios/Other/ppsspp/PPSSPP/ppge_atlas.meta"):
shutil.copy(REPO_ROOT / repo_path, dest / "PPSSPP")
proc = _install_run(self.env, "--platform", "retroarch", "--dest", str(dest),
"--system", "sony-psp", "--check", "--no-input")
"--system", "sony-psp", "--check", "--no-input", "--verbose")
self.assertEqual(proc.returncode, 3, proc.stdout + proc.stderr)
self.assertNotIn("Every file on the list is in place", proc.stdout)
found = re.search(r"(\d+) in place, (\d+) missing", proc.stdout)
@@ -5699,10 +5711,11 @@ class RunsAgainstTheRealLists(unittest.TestCase, _LoopbackRun):
dest.mkdir()
proc = _install_run({**self.env, "RETROBIOS_INSTALL_OFFLINE": "1"}, "--platform",
"retroarch", "--dest", str(dest), "--check", "--no-input",
"--verbose",
"--manifest", str(REPO_ROOT / "install" / "retroarch.json"))
self.assertEqual(proc.returncode, 3, proc.stdout + proc.stderr)
missing = re.search(r"0 in place, (\d+) missing", proc.stdout).group(1)
self.assertIn(f"\n {missing} files not in place:\n", proc.stdout)
self.assertIn(f"\n {missing} files not in place.\n", proc.stdout)
self.assertIn(f" {missing} files need the internet", proc.stdout)
def test_a_cores_folder_can_be_named(self):
@@ -5711,7 +5724,7 @@ class RunsAgainstTheRealLists(unittest.TestCase, _LoopbackRun):
(cores / "handy_libretro.so").write_bytes(b"")
proc = _install_run(self.env, "--platform", "retroarch", "--dest",
str(self.home / "s"), "--cores-dir", str(cores),
"--dry-run", "--no-input")
"--dry-run", "--no-input", "--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertIn("to the installed cores", proc.stdout)
@@ -5912,7 +5925,7 @@ class StandaloneFoldersAreNamedWhenThereAreAny(unittest.TestCase, _LoopbackRun):
def _run(self) -> str:
proc = _install_run(self.env, "--platform", "retroarch", "--dest",
str(self.home / "system"), "--system", "atari-lynx",
"--no-input")
"--no-input", "--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
return proc.stdout
@@ -6661,7 +6674,7 @@ class RomMFirmwareInstallsUnderItsTemplate(unittest.TestCase, _LoopbackRun):
" firmware: \"{platform}/bios\"\n")
proc = _install_run(self.env, "--platform", "romm", "--dest",
str(base / "library" / "bios"), "--system", "atari-lynx",
"--no-input")
"--no-input", "--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertTrue((base / "library" / "lynx" / "bios" / "lynxboot.img").is_file())
self.assertFalse((base / "library" / "bios").exists())
@@ -6810,7 +6823,7 @@ class EventsDescribeTheRun(unittest.TestCase, _SyntheticTree):
SKIPPED = [{"reason": "not_found", "label": "not found", "files": 1, "required": 1}]
def test_a_check_run(self):
proc, events = self._run("--check")
proc, events = self._run("--check", "--verbose")
self.assertEqual(proc.returncode, 3)
installer = events[0]["installer"]
self.assertEqual(events, self._head("check", installer) + [
@@ -7448,7 +7461,7 @@ class KeptFilesEndToEnd(unittest.TestCase, _SyntheticTree):
def test_install_then_restore(self):
proc = _install_run(self.env, "--platform", "retroarch", "--dest", str(self.bios),
"--no-input", "--jobs", "1")
"--no-input", "--jobs", "1", "--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertEqual((self.bios / "b.bin").read_bytes(), self.FILES["bios/A/b.bin"])
runs = list((self.home / install.BACKUP_DIR).iterdir())
@@ -7614,7 +7627,7 @@ class TheListVersionIsSaidBeforeAndAfter(unittest.TestCase):
"may show some files as not matching it.")
def test_line_mode_says_it_before_the_install_and_with_the_result(self):
proc = self.run_installer()
proc = self.run_installer("--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertEqual(proc.stdout.count(self.SAID), 2, proc.stdout)
self.assertLess(proc.stdout.index(self.SAID), proc.stdout.index("Checking existing"))
+2 -2
View File
@@ -483,7 +483,7 @@ class TheOneLinerReadsInstalledCores(unittest.TestCase):
def test_only_what_the_installed_cores_read(self):
with _CoreRepository() as repo:
repo.install_core("pcsx_rearmed_neon")
proc = repo.run("--installed-cores")
proc = repo.run("--installed-cores", "--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertIn("1 core installed", proc.stdout)
self.assertEqual(
@@ -562,7 +562,7 @@ class TheInterfaceFindsTheCoresLineModeFinds(unittest.TestCase):
found = []
for given in ((), ("--dest", str(repo.dest))):
command = [sys.executable, str(REPO_ROOT / "install.py"), "--platform",
"retroarch", *given, "--installed-cores", "--check"]
"retroarch", *given, "--installed-cores", "--check", "--verbose"]
proc = subprocess.run(command, env=env, capture_output=True, text=True,
timeout=60, check=False, stdin=subprocess.DEVNULL)
self.assertNotIn("no installed core found", proc.stderr)
+14 -3
View File
@@ -308,6 +308,15 @@ class ProgressOutput(unittest.TestCase):
return stream, term
def test_a_log_gets_one_line_per_file_and_failures_say_why(self):
# A file that went well is a line of --verbose, a failure of every run.
install.OUTPUT.verbose = False
stream, term = self._term()
progress = install.Progress(2, 10, term)
progress.done("a.bin", True)
progress.done("b.bin", False, "HTTP 404")
self.assertEqual(stream.getvalue(), " FAILED b.bin (HTTP 404)\n")
self.addCleanup(setattr, install.OUTPUT, "verbose", False)
install.OUTPUT.verbose = True
stream, term = self._term()
progress = install.Progress(2, 10, term)
progress.done("a.bin", True)
@@ -329,6 +338,8 @@ class ProgressOutput(unittest.TestCase):
["0:00", "0:42", "3:05", "1:02:09"])
def test_the_summary_names_each_failure(self):
self.addCleanup(setattr, install.OUTPUT, "verbose", False)
install.OUTPUT.verbose = True
buf = io.StringIO()
with contextlib.redirect_stdout(buf), self.assertRaises(SystemExit):
install._report_outcome(
@@ -544,7 +555,7 @@ class AFolderThisUserCannotEnter(unittest.TestCase):
with FakeRepository() as repo:
locked = self._locked(repo)
try:
proc = repo.run("--check")
proc = repo.run("--check", "--verbose")
finally:
os.chmod(locked, 0o755)
self.assertNotIn("Traceback", proc.stderr)
@@ -671,7 +682,7 @@ class AHomeThisUserCannotOpen(unittest.TestCase):
("detected", [])):
runs[name] = subprocess.run(
[sys.executable, str(REPO_ROOT / "install.py"), "--check",
"--no-input", *extra],
"--no-input", "--verbose", *extra],
env=env, capture_output=True, text=True, timeout=60,
stdin=subprocess.DEVNULL)
finally:
@@ -864,7 +875,7 @@ class APartlyClosedHome(unittest.TestCase):
("events", ["--events"])):
runs[name] = subprocess.run(
[sys.executable, str(REPO_ROOT / "install.py"), "--check",
"--no-input", *extra],
"--no-input", "--verbose", *extra],
env=env, capture_output=True, text=True, timeout=60,
stdin=subprocess.DEVNULL)
finally:
+2 -2
View File
@@ -42,7 +42,7 @@ class AndroidMediaScanner(unittest.TestCase):
return self.repo, proc
def test_an_android_install_writes_one(self):
repo, proc = self._run("android")
repo, proc = self._run("android", "--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
marker = repo.dest / ".nomedia"
self.assertTrue(marker.is_file())
@@ -106,7 +106,7 @@ class NamesAFatCardCannotHold(unittest.TestCase):
"files": files}))
out = io.StringIO()
argv = ["install.py", "--platform", "retrodeck", "--dest", str(dest),
"--manifest", str(listed), "--from", str(old), "--no-input"]
"--manifest", str(listed), "--from", str(old), "--no-input", "--verbose"]
with mock.patch.object(install, "holds_fat_refused_names", return_value=holds), \
mock.patch.object(install, "detect_os", return_value="linux"), \
mock.patch("sys.argv", argv), contextlib.redirect_stdout(out), \
+4 -4
View File
@@ -219,8 +219,8 @@ class CopyFromAnOldCard(FromFolder):
def test_check_names_what_the_folder_would_give_and_writes_nothing(self):
self.keep("BIOS/SCPH5501.BIN", US)
self.keep("gba_bios.bin", GBA)
proc = self.run_installer("--check", "--manifest", str(self.list), "--from",
str(self.old))
proc = self.run_installer("--check", "--verbose", "--manifest", str(self.list),
"--from", str(self.old))
self.assertEqual(proc.returncode, 3, proc.stdout + proc.stderr)
self.assertIn(f"{self.old} holds 2 of these files: a run without --check "
"copies them.", proc.stdout)
@@ -376,7 +376,7 @@ class MisplacedInTheBiosFolder(Scratch):
def test_a_nested_bios_folder_is_fixed_and_left_in_place(self):
for name, data in PAYLOAD.items():
self.place(f"system/{name}", data)
proc = self.install()
proc = self.install("--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
for name, data in PAYLOAD.items():
self.assertEqual((self.dest / name).read_bytes(), data)
@@ -387,7 +387,7 @@ class MisplacedInTheBiosFolder(Scratch):
def test_a_pack_extracted_into_a_folder_of_its_own_is_fixed(self):
for name, data in PAYLOAD.items():
self.place(f"RetroArch_BIOS_Pack/{name}", data)
proc = self.install()
proc = self.install("--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertIn("one folder too deep: it is at RetroArch_BIOS_Pack/scph5501.bin",
proc.stdout)
+3 -2
View File
@@ -833,7 +833,7 @@ class CommandLine(unittest.TestCase):
return [json.loads(line) for line in proc.stdout.splitlines() if line.startswith("{")]
def test_check_narrows_to_what_the_games_need(self):
proc = run_cli(self.card, "--for-games", "--check", "--events")
proc = run_cli(self.card, "--for-games", "--check", "--events", "--verbose")
events = self.events(proc)
games = next(e for e in events if e["event"] == "games")
self.assertEqual(games["games"], 2)
@@ -973,7 +973,8 @@ class ARunThatAsksNothing(unittest.TestCase):
def test_it_installs_what_the_games_need(self):
unasked, said = self.install("--yes")
self.assertEqual(unasked, self.install("--for-games", "--yes")[0])
self.assertIn("--all-files installs the whole list", said)
self.assertIn("what the games need", said)
self.assertIn("--all-files installs the whole list", self.install("--yes", "--verbose")[1])
def test_all_files_installs_the_whole_list(self):
everything, said = self.install("--all-files", "--yes")
+178
View File
@@ -19,6 +19,7 @@ import hashlib
import http.server
import json
import os
import re
import shutil
import subprocess
import sys
@@ -51,6 +52,46 @@ JOURNEY_KEYS = {
"another_device": (19, 20),
}
# Words each journey's screens show by default, summed over the screens it
# stops on, and the most one screen of the default path may show (owner,
# 2026-11-09: a beginner lost the important line in text). A path is no
# word: its length is the machine's. The counts from before the Details
# screen took the plan's and the result's notes are in claudedocs/
# user_research/installer_ux/phase1_measure.md. A change that adds words
# raises a number here and says why.
JOURNEY_WORDS = {
"first_install": 63,
"card_with_games": 106,
"update": 79,
"one_system": 193,
"one_system_again": 68,
"what_is_missing": 223,
"another_device": 230,
}
# Which setup? names every setup, Preview every file: lists, not prose.
SCREEN_CEILING = 90
# The same journeys in line mode, run without questions: (lines, words) of
# what happens, where, the result and the next step.
LINE_TEXT = {
"first_install": (4, 18),
"card_with_games": (4, 29),
"update": (5, 36),
"one_system": (4, 18),
"what_is_missing": (3, 15),
"another_device": (4, 18),
}
def words(text: str) -> int:
"""Words a reader reads, a path left out."""
return sum(1 for token in text.split() if re.search(r"[A-Za-z0-9]", token)
and "/" not in token)
def lines(text: str) -> int:
return sum(1 for line in text.splitlines() if line.strip())
# Runs install.py with the mounted drives a reader would show, none or the
# card named: the drives of the machine running the tests (WSL's /mnt/d)
# changed the screens. Only the screen's own process looks for cards: the
@@ -139,6 +180,17 @@ class Journeys(unittest.TestCase):
term = Pty(list(args), {**repo.env, **(env or {})}, 80, 24, program=program)
self.addCleanup(lambda: term.status is None and os.kill(term.pid, 9))
self.keys = 0
self.screens: list[str] = []
waiting = term.wait
def wait(needle: str, timeout: float = 20.0) -> str:
# Each screen the journey stops on, read once drawn whole.
waiting(needle, timeout)
screen = term.settle(0.3)
self.screens.append(screen)
return screen
term.wait = wait # type: ignore[method-assign]
return term
def press(self, term: Pty, *keys: str) -> None:
@@ -156,6 +208,11 @@ class Journeys(unittest.TestCase):
self.assertEqual((started, total), JOURNEY_KEYS[journey],
f"{journey} takes {(started, total)} keys now: longer is a step "
"back, shorter lowers JOURNEY_KEYS")
said = [words(screen) for screen in self.screens]
self.assertLessEqual(sum(said), JOURNEY_WORDS[journey],
f"{journey} shows {said} words: more is a step back")
self.assertLessEqual(max(said), SCREEN_CEILING, said)
self.screens = []
def finish(self, term: Pty) -> int:
term.wait("A/Enter Exit", timeout=60)
@@ -302,5 +359,126 @@ class Journeys(unittest.TestCase):
sorted(e["dest"] for e in repo.listed["files"]))
@unittest.skipUnless(pty, "the journeys need a pty")
class LineModeJourneys(unittest.TestCase):
"""The journeys in line mode without questions: what happens, where, the
result and the next step by default, every hidden line given back by
--verbose, and nothing waiting for an answer."""
def run_line(self, repo: Repository, *args: str, card: Path | None = None
) -> subprocess.CompletedProcess:
program = [sys.executable, "-c",
_WITH_CARDS.format(script=str(REPO_ROOT / "install.py"))]
env = {**repo.env, **({"JOURNEY_CARD": str(card)} if card else {})}
return subprocess.run([*program, *args], env=env, capture_output=True, text=True,
timeout=120, check=False, stdin=subprocess.DEVNULL)
def budget(self, journey: str, proc: subprocess.CompletedProcess) -> None:
text = proc.stdout + proc.stderr
most_lines, most_words = LINE_TEXT[journey]
self.assertLessEqual(lines(text), most_lines, text)
self.assertLessEqual(words(text), most_words, text)
def test_each_journey_says_what_where_result_and_next(self):
repo = Repository(self)
dest = repo.retroarch_here()
# No --yes and no terminal: nothing is asked, nothing waits.
proc = self.run_line(repo)
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.budget("first_install", proc)
said = [line for line in proc.stdout.splitlines() if line.strip()]
self.assertEqual([line.split(" ", 1)[0] for line in said],
["Installing", "BIOS", "Done.", "Next:"])
changed = next(e for e in repo.listed["files"] if e["dest"] == "md_eu.bin")
changed["size"] += 1
repo.write_list()
proc = self.run_line(repo, "--yes")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.budget("update", proc)
shutil.rmtree(dest)
dest.mkdir()
proc = self.run_line(repo, "--system", "nintendo-gba", "--yes")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.budget("one_system", proc)
shutil.rmtree(dest)
dest.mkdir()
proc = self.run_line(repo, "--check")
self.assertEqual(proc.returncode, 3, proc.stdout + proc.stderr)
self.budget("what_is_missing", proc)
proc = self.run_line(repo, "--platform", "batocera", "--dest", str(repo.home / "bios"),
"--yes")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.budget("another_device", proc)
def test_a_card_with_games(self):
repo = Repository(self)
card = repo.root / "MUOS"
(card / "MUOS" / "bios").mkdir(parents=True)
(card / "ROMS" / "GBA").mkdir(parents=True)
(card / "ROMS" / "GBA" / "unknown.gba").write_bytes(b"not in any catalog")
proc = self.run_line(repo, "--yes", card=card)
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.budget("card_with_games", proc)
self.assertIn("what the games need", proc.stdout)
def test_verbose_gives_back_every_line(self):
"""Each line a run prints by default, --verbose prints too, with
more: the same runs into two folders, read as one."""
repo = Repository(self)
folders = {name: repo.root / name for name in ("default", "verbose")}
runs: dict[str, list] = {}
for name, folder in folders.items():
extra = ["--verbose"] if name == "verbose" else []
base = ["--platform", "retroarch", "--dest", str(folder)]
runs[name] = [self.run_line(repo, *base, "--yes", *extra)]
# A later run replaces a changed file and keeps the old one.
(folder / "md_eu.bin").write_bytes(b"changed")
runs[name].append(self.run_line(repo, *base, "--yes", *extra))
(folder / "md_eu.bin").write_bytes(b"changed")
runs[name].append(self.run_line(repo, *base, "--check", *extra))
def read(text: str, folder: Path) -> set:
# The backup folder is named for the second it was made in.
text = re.sub(r"retrobios-backup\S*", "retrobios-backup", text)
return {line.strip().replace(str(folder), "<folder>")
for line in text.splitlines() if line.strip()}
for index in range(3):
with self.subTest(run=index):
quiet, full = runs["default"][index], runs["verbose"][index]
self.assertEqual(quiet.returncode, full.returncode, quiet.stdout + full.stdout)
shown = read(quiet.stdout + quiet.stderr, folders["default"])
said = read(full.stdout + full.stderr, folders["verbose"])
self.assertLessEqual(shown, said, sorted(shown - said))
self.assertGreater(len(said), len(shown))
def test_what_to_act_on_is_said_by_default(self):
"""A file an emulator needs that the collection lacks, a wrong copy
and what becomes of it: said without --verbose (review of
2026-11-10). The plain missing files wait on --verbose."""
repo = Repository(self)
repo.listed["omitted_files"] = [{"dest": "lost.bin", "name": "lost.bin",
"system": "nintendo-gba", "systems": ["nintendo-gba"],
"required": True, "reason": "not_found"}]
repo.write_list()
folder = repo.root / "dest"
base = ["--platform", "retroarch", "--dest", str(folder)]
needs = "1 file an emulator needs is not available here (--verbose names it)."
proc = self.run_line(repo, *base, "--yes")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertIn(needs, proc.stdout)
(folder / "md_eu.bin").write_bytes(b"changed")
(folder / "gba_bios.bin").unlink()
proc = self.run_line(repo, *base, "--check")
self.assertEqual(proc.returncode, 3, proc.stdout + proc.stderr)
self.assertIn("wrong copy: md_eu.bin", proc.stdout)
self.assertIn("A wrong copy is replaced, and the one in place kept in "
"retrobios-backup", proc.stdout)
self.assertIn(needs, proc.stdout)
self.assertNotIn("missing: gba_bios.bin", proc.stdout)
self.assertIn("missing: gba_bios.bin",
self.run_line(repo, *base, "--check", "--verbose").stdout)
if __name__ == "__main__":
unittest.main()
+8 -4
View File
@@ -461,7 +461,7 @@ class ABoardWithoutTheNetwork(Scratch):
(self.root / "targets" / "retroarch.json").write_text(json.dumps(BOARDS))
self.complete()
proc = self.run_installer("--offline", "--check", "--manifest", str(self.list),
"--target", "psx-board", "--no-input")
"--target", "psx-board", "--no-input", "--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertIn("for target psx-board", proc.stdout)
@@ -500,7 +500,7 @@ class FromPackInstallsOffline(Scratch):
for p in self.dest.rglob("*") if p.is_file()}
def test_the_pack_installs_with_no_network(self):
proc = self.run_installer("--from-pack", str(self.pack))
proc = self.run_installer("--from-pack", str(self.pack), "--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertEqual(self.installed(), PAYLOAD)
self.assertIn("Copying 4 files", proc.stdout)
@@ -571,7 +571,8 @@ class FromPackInstallsOffline(Scratch):
for rel, data in tree.items()},
}]
self.list.write_text(json.dumps(listed))
proc = self.run_installer("--from-pack", str(self.pack), "--manifest", str(self.list))
proc = self.run_installer("--from-pack", str(self.pack), "--manifest", str(self.list),
"--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertIn("dolphin-sys: 2 installed", proc.stdout)
for rel, data in tree.items():
@@ -708,6 +709,8 @@ class TheSummaryNamesTheBulk(unittest.TestCase):
files, {"system_cores": {"3do": ["mame", "opera"]}}, True, True), [])
def test_the_end_summary_prints_the_line(self):
self.addCleanup(setattr, install.OUTPUT, "verbose", False)
install.OUTPUT.verbose = True
out = io.StringIO()
with contextlib.redirect_stdout(out):
install._report_outcome([Path("/x")], 3, 0, 0, 0, False,
@@ -950,7 +953,8 @@ class OfflineLeavesFilesMissing(Scratch):
}]
self.list.write_text(json.dumps(listed))
self.complete()
proc = self.run_installer("--offline", "--manifest", str(self.list), "--no-input")
proc = self.run_installer("--offline", "--manifest", str(self.list), "--no-input",
"--verbose")
# Its files are missing, as the BIOS files --offline leaves out are,
# with or without questions.
self.assertIn("bluemsx: 0 already in place, 1 needs the internet (--offline)",
+5 -5
View File
@@ -594,7 +594,7 @@ class FakeRepository:
class TheOneLinerNarrows(unittest.TestCase):
def test_options_install_only_the_choice(self):
with FakeRepository() as repo:
proc = repo.run("--system", "sony-playstation", "--region", "us")
proc = repo.run("--system", "sony-playstation", "--region", "us", "--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertIn("Narrowed 5 to 2 files", proc.stdout)
self.assertEqual(repo.installed(), ["pcsx/extra.bin", "scph5501.bin"])
@@ -1606,14 +1606,14 @@ class ThePlatformsOwnListAlone(unittest.TestCase):
def test_install_check_and_dry_run_apply_it(self):
with FakeRepository() as repo:
proc = repo.run("--declared-only")
proc = repo.run("--declared-only", "--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertIn("5 to 3 files", proc.stdout)
self.assertEqual(repo.installed(),
["gba_bios.bin", "scph5500.bin", "scph5501.bin"])
for mode in ("--check", "--dry-run"):
with self.subTest(mode=mode), FakeRepository() as repo:
proc = repo.run("--declared-only", mode)
proc = repo.run("--declared-only", mode, "--verbose")
self.assertIn("5 to 3 files", proc.stdout, proc.stderr)
self.assertIn("0 in place, 3 missing", proc.stdout)
self.assertEqual(repo.installed(), [])
@@ -1633,7 +1633,7 @@ class ThePlatformsOwnListAlone(unittest.TestCase):
def test_a_local_file_list_applies_it(self):
with FakeRepository() as repo:
proc = repo.run("--declared-only", "--dry-run", "--manifest",
proc = repo.run("--declared-only", "--dry-run", "--verbose", "--manifest",
str(repo.root / "install" / "retroarch.json"))
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertIn("5 to 3 files", proc.stdout)
@@ -1778,7 +1778,7 @@ class SomethingElseAtADestination(unittest.TestCase):
def test_each_says_why_in_the_check_and_the_install(self):
with FakeRepository() as repo:
self.prepare(repo)
proc = repo.run("--check")
proc = repo.run("--check", "--verbose")
self.assertEqual(proc.returncode, 3, proc.stderr)
self.assertNotIn("Traceback", proc.stderr)
self.assertIn(" wrong copy: scph5500.bin\n a folder sits where this "
+359 -35
View File
@@ -83,6 +83,11 @@ def keys(*names: str) -> list:
return [Key("char", name[5:]) if name.startswith("char:") else Key(name) for name in names]
# The screens as --verbose draws them, every Details line where it belongs:
# the findings marked with it read lines the default leaves to Details.
in_full = mock.patch.object(install, "tui_verbose", lambda _state: True)
def _args(**given) -> argparse.Namespace:
"""The namespace main() parses, every option at its default."""
values = dict(
@@ -1002,19 +1007,7 @@ def golden_states() -> dict:
"bytes_total": 1064964}
installing.finished = [("gba_bios.bin", True, ""), ("scph5500.bin", False, "HTTP 404")]
states["installing"] = installing
result = checked_state()
result.stack.append("result")
result.code = 1
result.log = "/tmp/retrobios-x/install.log"
result.download = installing.download
result.summary = {
"event": "summary", "mode": "install", "installed": 3, "up_to_date": 1, "pending": 1,
"failed": [{"dest": "scph5500.bin", "reason": "HTTP 404"}],
"skipped": [{"reason": "not_found", "label": "not found", "files": 2, "required": 1}],
"left_out": [], "kept": [{"folder": str(HOME / ".config/retroarch/retrobios-backup/"
"2026-10-23_1200"), "files": 1, "bytes": 4}],
"interrupted": False}
states["result"] = result
states["result"] = golden_states_result()
error = fresh_state()
error.stack, error.run = ["setup", "error"], "check"
error.error = [" Could not fetch the file list for retroarch after 3 attempts: "
@@ -1032,9 +1025,33 @@ def golden_states() -> dict:
listed = games_state()
listed.stack.append("games")
states["games"] = listed
# What the plan and the result leave out by default, one press away.
details = checked_state()
details.stack.append("details")
states["plan-details"] = details
told = golden_states_result()
told.stack.append("details")
states["result-details"] = told
return states
def golden_states_result() -> install.TuiState:
"""The result golden_states draws, a file failed and one kept."""
result = checked_state()
result.stack.append("result")
result.code = 1
result.log = "/tmp/retrobios-x/install.log"
result.download = {"event": "download", "files": 4, "bytes": 1064964}
result.summary = {
"event": "summary", "mode": "install", "installed": 3, "up_to_date": 1, "pending": 1,
"failed": [{"dest": "scph5500.bin", "reason": "HTTP 404"}],
"skipped": [{"reason": "not_found", "label": "not found", "files": 2, "required": 1}],
"left_out": [], "kept": [{"folder": str(HOME / ".config/retroarch/retrobios-backup/"
"2026-10-23_1200"), "files": 1, "bytes": 4}],
"interrupted": False}
return result
def result_states() -> dict:
"""The result of a run that installed everything, of one with a file
failed, of one --offline left files out of, and of one stopped."""
@@ -1159,8 +1176,11 @@ class Views(unittest.TestCase):
def test_each_role_paints_a_status_word_on_the_result_screens(self):
"""Done, a part done, files left offline, a stop: between them the
result screens use every role, each on a word that says it."""
# The failed files are listed on the result's Details.
told = result_states()["partial"]
told.stack.append("details")
drawn = "\n".join(install.tui_paint(str(line), line.style, True)
for state in result_states().values()
for state in [*result_states().values(), told]
for line in install.tui_view(state, 80, 24))
for role, word in (("accent", "RetroBIOS"), ("strong+success", "4 files installed"),
("strong+success", "Done"), ("failure", "1 failed"),
@@ -1573,6 +1593,7 @@ class SmallFixes(unittest.TestCase):
self.assertTrue(state.installed_only)
self.assertNotIn("--installed-cores", [f for f, _v in install.tui_flags(state)])
@in_full
def test_the_end_of_a_wrapped_pager_is_reachable(self):
state = checked_state()
state.stack.append("result")
@@ -2081,7 +2102,7 @@ class WorkFolders(unittest.TestCase):
def test_a_failure_keeps_the_log_it_names(self):
work, out = self.run_to(1)
self.assertTrue(work.exists())
self.assertIn(f"Details: {work / 'install.log'}", out)
self.assertIn(f"Log: {work / 'install.log'}", out)
def test_a_stop_leaves_nothing(self):
work, out = self.run_to(130)
@@ -2238,6 +2259,7 @@ class CopyFromAnOldCard(unittest.TestCase):
self.assertEqual(state.from_folders, [Path("/media/old")])
self.assertIn(("--from", "/media/old"), install.tui_flags(state))
@in_full
def test_files_in_the_wrong_place_are_said_and_listed(self):
state = checked_state()
state.platform, state.dest = "retroarch", SYSTEM_DIR
@@ -2778,6 +2800,7 @@ class CardsSpaceAndConfirm(unittest.TestCase):
if row.action == "game_data")
self.assertEqual(label, "Game data: left out")
@in_full
def test_the_plan_says_which_config_named_the_folder(self):
config = HOME / ".config/retroarch/retroarch.cfg"
with mock.patch.dict(install._RETROARCH_CONFIGS, {SYSTEM_DIR: (config, config.parent)}):
@@ -2803,6 +2826,7 @@ class CardsSpaceAndConfirm(unittest.TestCase):
# 1 MB from the network in 2 s, whatever came from the old card.
self.assertIn("512.0 KB/s, 0:02 left", text)
@in_full
def test_the_plan_says_what_the_scan_left_out(self):
state = checked_state()
state.from_folders = [Path("E:\\")]
@@ -3009,21 +3033,26 @@ class SaveDirectoryLeftOut(unittest.TestCase):
install.tui_receive(state, ("ended", state.run_id, 1, []))
return state, summary
@in_full
def test_a_list_that_holds_none_of_them_offers_no_folder(self):
"""RetroPie's list leaves its save files out with no hash or address:
--save-dir installs none of them, and was advised again each run."""
state, summary = self._ended(MANIFEST)
line = ("2 files read from RetroArch's save directory left out: RetroArch keeps "
"it in no fixed place, and its file list holds none of them.")
# The count on the screen, why on Details.
lines = ["2 files read from RetroArch's save directory left out.",
"RetroArch keeps it in no fixed place, and its file list holds none of them."]
plan = " ".join(" ".join(install.tui_view(state, 120, 40)).split())
self.assertIn(line, plan)
self.assertIn(" ".join(lines), plan)
self.assertNotIn("--save-dir", plan)
state.stack.append("result")
state.code = 0
state.summary = {**summary, "mode": "install", "installed": 5}
self.assertEqual([text for text in install.tui_outcome(state)[1] if "save" in text],
[line])
outcome = install.tui_outcome(state)[1]
self.assertEqual([text for text in outcome if "save" in text or "place" in text],
lines)
self.assertTrue(install._is_detail(outcome[outcome.index(lines[1])]))
@in_full
def test_files_left_out_of_the_save_directory_are_said(self):
state = fresh_state()
state.stack = ["setup"]
@@ -3039,12 +3068,12 @@ class SaveDirectoryLeftOut(unittest.TestCase):
with mock.patch.object(install, "_tui_folder_facts", return_value={
"free": 50 * 1024 ** 3, "writable": True, "cores": []}):
install.tui_receive(state, ("ended", state.run_id, 1, []))
line = ("2 files read from RetroArch's save directory left out: its place is "
"not known. ")
line = "2 files read from RetroArch's save directory left out. "
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).", plan)
self.assertIn(line + "Choose it in Options, Save folder (--save-dir). Its place is "
"not known.", plan)
state.stack.append("result")
state.code = 0
state.summary = {**summary, "mode": "install", "installed": 5}
@@ -3673,6 +3702,7 @@ class DataFilesHaveASize(unittest.TestCase):
""""8179 files to download, 6.6 GB, and 2636 data
files", the data files with no size."""
@in_full
def test_the_plan_gives_the_data_files_size(self):
state = fresh_state()
state.stack = ["setup"]
@@ -3720,6 +3750,7 @@ class ThePagerCountsLines(unittest.TestCase):
self.assertTrue(any(re.fullmatch(rf" \d+-{len(wrapped)} of {len(wrapped)}",
line) for line in body), body)
@in_full
def test_the_counter_moves_one_line_per_press(self):
state = self.result_state(40, 12)
first = [line for line in install.tui_view(state, 40, 12) if " of " in line][-1]
@@ -3862,6 +3893,7 @@ class BackTicksAndSaveFolder(unittest.TestCase):
state = install.TuiState()
self.assertLess(abs(state.now - time.monotonic()), 5)
@in_full
def test_a_pack_installs_from_the_pack(self):
state = checked_state()
state.found = [{"event": "found", "platform": "retroarch", "dest": FILES[0]["dest"],
@@ -4152,6 +4184,7 @@ class TheSaveFolderLineFollowsTheChoice(unittest.TestCase):
self.assertEqual(state.screen, "result")
self.assertNotIn("save directory", self.said(install.tui_outcome(state)[1]))
@in_full
def test_kept_the_confirm_screen_and_the_result_name_it(self):
state = self.run_to_result(confirm_saves=True, install_saves=True)
self.assertIn(self.LINE, self.confirm)
@@ -4882,6 +4915,11 @@ class EndToEnd(unittest.TestCase):
screen = term.wait("RetroBIOS is on the KNULLI card")
self.assertIn("start RetroBIOS from Ports", screen)
self.assertNotIn(install.APP_OFFER, screen)
# The command, a Details line, is said after Exit once they were opened.
term.send("d")
term.wait("RetroBIOS: Details")
term.send("\x1b")
term.wait("RetroBIOS is on the KNULLI card")
term.send("\r")
self.assertEqual(term.close(), 0)
self.assertAppOnCard(root, "KNULLI")
@@ -4904,7 +4942,9 @@ class EndToEnd(unittest.TestCase):
after = term.after()
self.assertIn("RetroBIOS: Done", after)
self.assertIn("5 files installed.", after)
self.assertIn("--platform retroarch --dest", after)
# The command is a Details line: said after Exit once they were opened.
self.assertIn("Next: start RetroArch and play.", after)
self.assertNotIn("--platform retroarch --dest", after)
# The cursor and the main screen come back before the result is printed.
self.assertIn(b"\x1b[?25h\x1b[?1049l", term.raw)
@@ -4943,15 +4983,24 @@ class EndToEnd(unittest.TestCase):
term.send("\r")
screen = term.wait("files to download")
self.assertIn("6 files to download", screen)
# The second folder the run writes to, named before
# anything is written, and again on the result.
# The second folder the run writes to, named on the
# plan's Details before anything is written, and on the
# result's.
term.send("d")
screen = term.wait("RetroBIOS: Details")
self.assertIn("1 file read from RetroArch's save directory goes to a "
"folder per core under:\n ~/MySaves", screen)
term.send("\x1b")
term.wait("files to download")
term.send("\r")
screen = term.wait("6 files installed.")
term.wait("6 files installed.")
term.send("d")
screen = term.wait("RetroBIOS: Details")
# Spelled in full on the result, as its other folders.
joined = "".join(line.strip() for line in screen.splitlines())
self.assertIn("folder per core under:" + str(saves), joined)
term.send("\x1b")
term.wait("6 files installed.")
term.send("\r")
self.assertEqual(term.close(), 0)
results.append(written(repo, saves))
@@ -4978,6 +5027,10 @@ class EndToEnd(unittest.TestCase):
self.assertIn("3 files to download", screen)
term.send("\r")
term.wait("3 files installed.")
term.send("d")
term.wait("RetroBIOS: Details")
term.send("\x1b")
term.wait("3 files installed.")
term.send("\r")
self.assertEqual(term.close(), 0)
self.assertEqual(repo.installed(), ["pcsx/extra.bin", "scph5500.bin", "scph5501.bin"])
@@ -4999,9 +5052,13 @@ class EndToEnd(unittest.TestCase):
screen = term.wait("4 files to download")
self.assertIn("> Install", screen)
term.send("\r")
screen = term.wait("installed.")
term.wait("installed.")
term.send("o")
screen = term.wait("RetroBIOS: Details")
self.assertIn("1 game data file left out.", screen)
term.send("\r")
term.wait("installed.")
term.send("\r")
self.assertEqual(term.close(), 0)
self.assertEqual(repo.installed(), ["gba_bios.bin", "mgba/extra.bin",
"pcsx/extra.bin", "scph5501.bin"])
@@ -5090,6 +5147,10 @@ class EndToEnd(unittest.TestCase):
self.assertIn("5 files installed.", screen)
self.assertIn("3 files copied from", screen)
self.assertIn("2 downloaded.", screen)
term.send("o")
term.wait("RetroBIOS: Details")
term.send("\r")
term.wait("files installed.")
term.send("\r")
self.assertEqual(term.close(), 0)
self.assertEqual(len(repo.installed()), 5)
@@ -5808,6 +5869,7 @@ class ThePlanNumbersAddUp(unittest.TestCase):
""""47 files to download" sat above "49 missing, 1 wrong copy": the 3
found on this machine were the difference, left to the reader."""
@in_full
def test_the_copied_files_are_the_difference(self):
state = checked_state()
state.found = [found("scph5500.bin", str(SYSTEM_DIR), "one folder too deep")]
@@ -6441,6 +6503,7 @@ class ConfirmRefusalsAndCardRows(unittest.TestCase):
if row.action == "platform"}
self.assertEqual(labels["knulli"], "KNULLI")
@in_full
def test_offline_plan_counts_agree(self):
""""3 downloaded" under "3 need the internet";
the data files read as if on hand."""
@@ -6871,6 +6934,7 @@ class WhatEachScreenOffers(unittest.TestCase):
rows = install.tui_rows(state)
self.assertEqual(rows[state.focus["browse"]].value, stick)
@in_full
def test_offline_plan_names_the_folder_already_copied_from(self):
state = checked_state()
state.from_folders = [self.root / "from1"]
@@ -6979,6 +7043,7 @@ class WhatEachScreenOffers(unittest.TestCase):
"not known. Name it with --save-dir next time.", text)
self.assertNotIn("the collection does not hold", text)
@in_full
def test_plural_game_data_and_the_backup_line_at_40x12(self):
state = checked_state()
games = [{"dest": f"g{index}.dat", "system": "", "category": install.GAME_DATA}
@@ -7017,6 +7082,8 @@ def line_decision(args: argparse.Namespace, answer) -> tuple[list, str]:
def stop(_args, platforms):
raise _Decided(platforms)
# --verbose's text, which names what detection found.
args.verbose = True
install.LOCAL.reset()
with mock.patch.object(install, "app_refusal", side_effect=stop), \
mock.patch.object(install, "detect_os", return_value="linux"), \
@@ -7757,6 +7824,7 @@ class LineModeAndScreenDecideAlike(_LineModeFindings, _OutsideTextFindings,
self.assertIn("1 game data file left out (--no-game-data)", after)
self.assertNotIn("doom1.wad would", after)
@in_full
def test_the_plan_counts_game_data_apart_and_one_press_leaves_it_out(self):
state = checked_state()
manifest = json.loads(json.dumps(state.manifest))
@@ -7798,7 +7866,7 @@ class LineModeAndScreenDecideAlike(_LineModeFindings, _OutsideTextFindings,
(backup / "gba_bios.bin").write_bytes(b"gba")
(backup / "doom" / "doom1.wad").write_bytes(b"wad" * 10)
args = _args(manifest=listing, dest=self.here, offline=True, declared_only=False,
no_input=True)
no_input=True, verbose=True)
setattr(args, "from", [backup])
real = io.StringIO()
install.LOCAL.reset()
@@ -7888,6 +7956,7 @@ class LineModeAndScreenDecideAlike(_LineModeFindings, _OutsideTextFindings,
if split:
self.assertEqual(int(split[1]) + int(split[2]), int(head[1]))
@in_full
def test_the_screen_divides_the_count_it_shows(self):
state = checked_state()
manifest = json.loads(json.dumps(state.manifest))
@@ -7965,7 +8034,7 @@ class LineModeAndScreenDecideAlike(_LineModeFindings, _OutsideTextFindings,
old = self.root / "OLD"
old.mkdir()
(old / "gba_bios.bin").write_bytes(payload)
args = _args(manifest=listing, declared_only=False, offline=True)
args = _args(manifest=listing, declared_only=False, offline=True, verbose=True)
setattr(args, "from", [old])
real = io.StringIO()
install.LOCAL.reset()
@@ -8047,7 +8116,10 @@ class LineModeAndScreenDecideAlike(_LineModeFindings, _OutsideTextFindings,
def line_run(self, args: argparse.Namespace, answers: list, mounts: list = (),
found: list = ()) -> tuple[str, list]:
"""(text, prompts) of line mode at a terminal for args, the answers
given in turn: the run stops at the first question left unanswered."""
given in turn: the run stops at the first question left unanswered.
Its text is --verbose's: these findings read the lines the default
leaves out."""
args.verbose = True
given, asked = iter(answers), []
def answer(prompt: str) -> str:
@@ -9012,6 +9084,7 @@ class PacksWordingAndDocs(unittest.TestCase):
install.LOCAL.pack = mock.Mock(name="pack")
self.assertIn("to install from the pack", install._pager_items(state)[0])
@in_full
def test_scroll_is_named_only_where_the_text_scrolls(self):
""""Up/Down Scroll" over nine lines that fit."""
state = checked_state()
@@ -10077,6 +10150,7 @@ class CardNamesAndHelp(_CardFixtures, unittest.TestCase):
self.assertNotIn("A/Enter", bar)
self.assertNotIn("B/Esc", bar)
@in_full
def test_the_folder_its_config_named_is_said_under_it(self):
state = checked_state()
long_dest = Path("/home/user/" + "a-long-folder-name/" * 5 + "system")
@@ -10222,6 +10296,7 @@ class TheAndroidResultReadsWhole(unittest.TestCase):
state.cols, state.rows = cols, 40
return install.tui_view(state, cols, 40)
@in_full
def test_a_path_or_a_command_is_never_split_inside_a_word(self):
full = "/sdcard/RetroArch/retrobios-backup/20261027-223524"
for cols in (40, 50, 80):
@@ -10943,6 +11018,7 @@ class TapsGameDataAndExitCodes(_CardFixtures, unittest.TestCase):
plain.help = True
self.assertNotIn("Tap", " ".join(install.tui_view(plain, 80, 24)))
@in_full
def test_the_result_is_left_by_a_tap(self):
"""The result gave the mouse back, and on Termux
leaving it took the keyboard. A tap on its Exit closes it, and its
@@ -10959,6 +11035,7 @@ class TapsGameDataAndExitCodes(_CardFixtures, unittest.TestCase):
# Root cause 2: one narrowed list drives every count.
@in_full
def test_the_game_data_row_and_the_save_line_follow_the_choice(self):
"""With only the GBA ticked, the plan said 1 game
data file and a save file of the PlayStation."""
@@ -10996,6 +11073,7 @@ class TapsGameDataAndExitCodes(_CardFixtures, unittest.TestCase):
word = "left out" if left else "kept"
self.assertRegex(line, rf"^ Game data: {word} +51(, | files, )2\.4 GB$")
@in_full
def test_data_files_are_named_apart(self):
"""Data files beside BIOS files and game data, the
parts adding up to the count above, each kind said once."""
@@ -11008,6 +11086,7 @@ class TapsGameDataAndExitCodes(_CardFixtures, unittest.TestCase):
"other cores. Data files: the files an emulator reads from a data "
"folder of its own, such as Dolphin's Sys and PPSSPP's assets.", text)
@in_full
def test_confirm_counts_the_kinds_apart(self):
"""Extra criteria: the plan split BIOS files and game
data, Confirm did not."""
@@ -11287,6 +11366,7 @@ class TapsGameDataAndExitCodes(_CardFixtures, unittest.TestCase):
rows = install.tui_rows(state)
self.assertIn(sides[0][1], [row.value for row in rows if row.action == "drive"])
@in_full
def test_named_by_sits_under_its_folder(self):
"""The offline remedy and the narrowing came
between the folder and what named it."""
@@ -11416,7 +11496,7 @@ class TapsOnAPty(unittest.TestCase):
bar = term.screen.display[-1]
self.click(term, bar.index("Exit"), len(term.screen.display) - 1)
self.assertEqual(term.close(), 0)
self.assertIn("Same choice without questions:", term.after())
self.assertIn("Next: start RetroArch and play.", term.after())
self.assertGreater(term.raw.rfind(b"\x1b[?1000l"), mark)
@@ -11737,6 +11817,7 @@ class OneChoicePerRun(unittest.TestCase):
state.exit_code, state.quit_asked = install.QUIT_CODE, True
self.assertIn(note, install.tui_farewell(state))
@in_full
def test_the_help_defines_game_data_at_40_columns(self):
"""The row was a choice nothing explained on the
Miyoo's screen."""
@@ -11802,6 +11883,7 @@ class OneChoicePerRun(unittest.TestCase):
whole="command")
self.assertIn(f" --from-pack {path}", lines)
@in_full
def test_the_definition_is_said_once_and_never_spliced(self):
state = checked_state()
state.systems = []
@@ -11814,6 +11896,7 @@ class OneChoicePerRun(unittest.TestCase):
self.assertIn("Not available here: jazz2/Source/Anims.j2a (Jazz Jackrabbit 2), game "
"data its game reads.", text)
@in_full
def test_the_headline_counts_every_file_once(self):
""""8358 files and 2636 data files", and the
definition dropped at 80 columns."""
@@ -11825,6 +11908,7 @@ class OneChoicePerRun(unittest.TestCase):
self.assertNotIn("and 7 data files", lines[2])
self.assertIn("Data files: the files an emulator reads", text)
@in_full
def test_the_save_line_never_ends_on_its_colon(self):
""""...goes to a folder per core under:" with no
folder at 60x20."""
@@ -11922,6 +12006,7 @@ class OneChoicePerRun(unittest.TestCase):
said = " ".join(install._needed_lines(entries, "Needed"))
self.assertIn("... and 1 more system (--check --verbose lists them all)", said)
@in_full
def test_the_farewell_names_the_option_not_the_screen(self):
state = _installed_state(SYSTEM_DIR)
state.manifest = {**state.manifest, "save_directory": {"folder": "saves",
@@ -12619,7 +12704,7 @@ class ChoiceStateInBothModes(unittest.TestCase):
self.assertNotIn("to fetch them", text)
self.assertIn("to copy 1 of them from RetroArch_BIOS_Pack.zip", text)
options = ("--platform", "retroarch", "--manifest", str(listing), "--dest", str(dest),
"--yes")
"--yes", "--verbose")
proc = install_run(*options, "--from-pack", str(pack))
text = proc.stdout + proc.stderr
self.assertIn("Copying 1 file (1 B) from RetroArch_BIOS_Pack.zip...", text)
@@ -12669,6 +12754,7 @@ class ChoiceStateInBothModes(unittest.TestCase):
_title, lines = install.tui_outcome(state)
self.assertIn("7 data files not installed: not in RetroArch_BIOS_Pack.zip.", lines)
@in_full
def test_a_command_at_40_columns_breaks_between_shell_words(self):
"""The terminal folded commands inside a word,
"--platform" ended a line with its value on the next."""
@@ -12702,6 +12788,7 @@ class ChoiceStateInBothModes(unittest.TestCase):
farewell = "\n".join(install.tui_farewell(state))
self.assertIn("/home/moi/retroarch_system/tmp/tui_audit14/sb/a/b/bios", farewell)
@in_full
def test_a_folder_on_its_own_line_has_one_indent(self):
"""The save folder at the label's indent, the
BIOS folder under it, a path's last piece without its indent."""
@@ -12774,7 +12861,7 @@ class ChoiceStateInBothModes(unittest.TestCase):
files = [self.entry("gba_bios.bin", b"gba", ["nintendo-gba"])]
listing = self.listing("retroarch", files)
proc = install_run("--platform", "retroarch", "--manifest", str(listing),
"--dest", str(self.here), "--check", "--offline")
"--dest", str(self.here), "--check", "--offline", "--verbose")
self.assertIn("0 in place, 1 missing, 0 wrong copies", proc.stdout)
self.assertNotIn("present (", proc.stdout)
source = (REPO_ROOT / "install.py").read_text(encoding="utf-8")
@@ -13232,6 +13319,8 @@ class OneVocabularyOneCount(unittest.TestCase):
return listing, dest, old
def run_line(self, listing: Path, dest: Path, answers: list, **given) -> str:
# --verbose's text: the counts compared are lines the default leaves out.
given.setdefault("verbose", True)
from_ = given.pop("from_", None)
args = _args(manifest=listing, dest=dest, declared_only=False, **given)
if from_:
@@ -13334,6 +13423,7 @@ class OneVocabularyOneCount(unittest.TestCase):
self.assertIn("1 in place, 3 missing, 0 wrong copies", text)
self.assertIn("Nothing installed: 3 files need the internet", text)
@in_full
def test_a_pack_holding_none_offers_no_install(self):
"""A data tree whose folder is the BIOS folder
itself (blueMSX) made any pack hold a data file: Install focused,
@@ -13526,6 +13616,7 @@ class TheFromPlanSaysItsKinds(unittest.TestCase):
"""Every file found in the folder, the plan named
the Game data row and said neither the kinds nor what game data is."""
@in_full
def test_the_kinds_and_the_definition_stay(self):
audit = OneChoicePerRun()
state = audit.game_plan()
@@ -13626,7 +13717,9 @@ class ProvenCardsAndCounts(unittest.TestCase):
def line_from(self, args: argparse.Namespace, answers: list,
folders: list[Path] = ()) -> tuple[str, list]:
"""(text, prompts) of line mode at a terminal, the result included,
the folders --from names read first, as main() reads them."""
the folders --from names read first, as main() reads them: --verbose's
text."""
args.verbose = True
given, asked = iter(answers), []
def answer(prompt: str) -> str:
@@ -14533,6 +14626,7 @@ class CoresListAndFolderFailures(unittest.TestCase):
# Root cause 6: every other item.
@in_full
def test_offline_the_plan_promises_no_replacement_it_cannot_make(self):
"""Offline, the plan said a wrong copy no folder
holds "is replaced"."""
@@ -14659,6 +14753,7 @@ class GamesOnTheCard(unittest.TestCase):
"""For the games on this card: the first choice where the card holds
games, the whole list one press away (--for-games)."""
@in_full
def test_a_card_with_games_opens_on_them(self):
state = games_state()
self.assertTrue(state.for_games)
@@ -14836,5 +14931,234 @@ class GamesEndToEnd(unittest.TestCase):
written = sorted(p.name for p in card.bios.rglob("*") if p.is_file())
self.assertEqual(written, ["bios_CD_E.bin", "cd_extra.bin"])
# The most words each screen may show by default, one count per size of
# SIZES (owner, 2026-11-09: a beginner lost the important line in text). A
# path is no word. The plan, Confirm and the result say what happens, where
# and what next; their counts, notes and commands wait on Details. A list
# shows more rows on a taller window. A change that adds words to a screen
# raises its numbers here and says why: the result names its failed files
# (review of 2026-11-10), what the user acts on.
SCREEN_WORDS = {
"setup": (29, 36, 34, 34), "platforms": (23, 27, 89, 225), "folder": (27, 27, 25, 25),
"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),
"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),
"games": (36, 45, 44, 44), "plan-details": (30, 30, 29, 29),
"result-details": (30, 30, 19, 19),
}
def screen_words(lines: list) -> int:
"""Words of a drawn screen, its title and bar included, a path left out."""
return sum(1 for line in lines for token in str(line).split()
if re.search(r"[A-Za-z0-9]", token) and "/" not in token)
class ScreenWordBudgets(unittest.TestCase):
def setUp(self):
patcher = mock.patch.object(install, "_mounted_cards", return_value=[])
patcher.start()
self.addCleanup(patcher.stop)
def test_every_screen_keeps_to_its_budget(self):
states = golden_states()
self.assertEqual(sorted(states), sorted(SCREEN_WORDS),
"a screen without a word budget, or a budget without its screen")
for name, state in states.items():
for (cols, rows), budget in zip(SIZES, SCREEN_WORDS[name]):
with self.subTest(screen=name, size=f"{cols}x{rows}"):
state.cols, state.rows = cols, rows
words = screen_words(install.tui_view(state, cols, rows))
self.assertLessEqual(words, budget)
def test_the_default_path_says_what_where_and_next_first(self):
"""The plan opens on what it installs and where, the result on what
happened, where and the next step: nothing between them."""
plan = [str(line).strip() for line in install.tui_view(checked_state(), 80, 24)]
self.assertEqual(plan[2:4], ["4 files to download, 1.0 MB",
"BIOS folder: ~/.config/retroarch/system"])
self.assertEqual(plan[4], "")
done = result_states()["done"]
lines = [str(line).strip() for line in install.tui_view(done, 80, 24)]
self.assertEqual(lines[2:5], ["4 files installed. 1 file already in place.",
f"BIOS folder: {SYSTEM_DIR}",
"Next: start RetroArch and play."])
class DetailsScreen(unittest.TestCase):
"""What the plan, Confirm and the result leave out by default, one key
or one row away, and every line of --verbose in one place or the other."""
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 test_d_opens_the_plans_details_and_b_comes_back(self):
state = checked_state()
self.assertNotIn("Free space", self.text(state))
press(state, "d")
self.assertEqual(state.screen, "details")
shown = self.text(state)
for line in ("1 in place, 3 missing, 1 wrong copy", "Free space: 50.0 GB",
"The 1 wrong copy is replaced"):
self.assertIn(line, shown)
press(state, "esc")
self.assertEqual(state.screen, "plan")
def test_a_controller_reaches_them_by_the_row(self):
state = checked_state()
rows = install.tui_rows(state)
state.focus["plan"] = [row.action for row in rows].index("details")
self.assertIn("A/Enter Show", self.text(state))
press(state, "enter")
self.assertEqual(state.screen, "details")
press(state, "enter")
self.assertEqual(state.screen, "plan")
def test_the_results_details_are_said_after_exit_once_opened(self):
state = golden_states_result()
self.assertIn("Y/o Details", self.text(state))
self.assertNotIn("Same choice without questions", "\n".join(install.tui_farewell(state)))
press(state, "o")
self.assertEqual(state.screen, "details")
self.assertIn("Same choice without questions:", self.text(state))
press(state, "esc")
self.assertEqual(state.screen, "result")
press(state, "enter")
farewell = "\n".join(install.tui_farewell(state))
self.assertIn("Same choice without questions:", farewell)
self.assertIn("failed: scph5500.bin (HTTP 404)", farewell)
def test_confirm_has_its_details_too(self):
state = golden_states()["confirm"]
self.assertNotIn("Free space", self.text(state))
press(state, "d")
self.assertEqual(state.screen, "details")
self.assertIn("Nothing is written before Install.", self.text(state))
self.assertIn("Free space: 50.0 GB", self.text(state))
def test_every_line_is_on_the_screen_or_its_details(self):
"""The screen and its Details together say what --verbose says on
the screen alone: no line is lost."""
for name in ("plan", "plan-games", "plan-in-place", "confirm", "result"):
with self.subTest(screen=name):
state = golden_states()[name]
whole = (install._plan_text(state) if state.screen == "plan"
else install._confirm_text(state) if state.screen == "confirm"
else install.tui_outcome(state)[1])
shown = install._shown(state, whole)
state.stack.append("details")
told = install.details_lines(state)
self.assertEqual(sorted(map(str, whole)),
sorted(map(str, [*shown, *[line for line in told
if install._is_detail(line)]])))
def test_verbose_draws_them_in_place_and_offers_no_row(self):
state = checked_state()
state.passthrough = [("--verbose", None)]
text = self.text(state)
self.assertIn("Free space: 50.0 GB", text)
self.assertNotIn("details", [row.action for row in install.tui_rows(state)])
def test_installing_lists_the_files_that_went_well_with_verbose(self):
state = golden_states()["installing"]
self.assertNotIn("gba_bios.bin", self.text(state))
self.assertIn("FAILED scph5500.bin (HTTP 404)", self.text(state))
state.passthrough = [("--verbose", None)]
self.assertIn("ok gba_bios.bin", self.text(state))
class WhatToActOnStaysOnTheScreen(unittest.TestCase):
"""A failure, a file an emulator needs that the collection lacks, a
remedy to choose: on the screen by default, never only on Details
(review of 2026-11-10)."""
def setUp(self):
patcher = mock.patch.object(install, "_mounted_cards", return_value=[])
patcher.start()
self.addCleanup(patcher.stop)
def text(self, state: install.TuiState) -> str:
return " ".join(" ".join(install.tui_view(state, 80, 24)).split())
def test_a_failed_file_is_named_on_the_result_and_after_exit(self):
state = golden_states_result()
self.assertIn("failed: scph5500.bin (HTTP 404)", self.text(state))
press(state, "enter")
self.assertIn("failed: scph5500.bin (HTTP 404)", "\n".join(install.tui_farewell(state)))
def needs(self, state: install.TuiState) -> install.TuiState:
state.manifest = {**MANIFEST, "omitted_files": [
{"dest": "lost.bin", "system": "sony-playstation", "required": True},
{"dest": "spare.bin", "system": "sony-playstation", "required": False}]}
return state
def test_a_file_an_emulator_needs_is_counted_on_the_plan(self):
state = self.needs(checked_state())
said = "1 file an emulator needs is not available here (Details names it)."
self.assertIn(said, self.text(state))
press(state, "d")
self.assertIn("lost.bin", self.text(state))
# With --verbose the names are on the plan, and the count not again.
state = self.needs(checked_state())
state.passthrough = [("--verbose", None)]
self.assertNotIn(said, self.text(state))
self.assertIn("lost.bin", self.text(state))
def test_a_file_an_emulator_needs_is_counted_on_the_result(self):
state = self.needs(result_states()["done"])
self.assertIn("1 file an emulator needs is not available here (Details names it).",
self.text(state))
def test_the_save_folder_remedy_is_on_the_result(self):
state = result_states()["done"]
state.manifest = {**MANIFEST, "save_directory": {"folder": "saves", "by_core": True}}
state.summary["skipped"] = [{"reason": "no_save_directory", "files": 2}]
text = self.text(state)
self.assertIn("2 files read from RetroArch's save directory left out. "
f"{install.TUI_SAVE_REMEDY}", text)
self.assertNotIn("Its place is not known.", text)
press(state, "o")
self.assertIn("Its place is not known.", self.text(state))
def test_the_save_folder_remedy_is_on_the_plan(self):
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))
def test_files_that_could_not_be_read_are_counted_where_no_row_says_so(self):
unread = [{"event": "unreadable", "platform": "retroarch", "folder": "/media/OLD",
"path": "/media/OLD/scph5501.bin", "member": "",
"reason": "Permission denied"}]
state = checked_state()
state.unreadable = list(unread)
# The plan's row names them: the count waits on Details.
self.assertIn("Show the files that could not be read", self.text(state))
# Everything in place: no row, the count on the plan.
done = checked_state()
done.check = {**CHECK, "present": 5, "missing": 0, "wrong_hash": 0,
"bytes_needed": 0, "not_installable": 0}
done.unreadable = list(unread)
self.assertNotIn("unreadable", [row.action for row in install.tui_rows(done)])
self.assertIn("1 file in OLD could not be read.", self.text(done))
def test_another_drive_to_add_is_said_on_the_plan(self):
state = checked_state()
state.scanned = [{"event": "scanned", "folder": "E:\\", "files": 200000,
"truncated": True, "other_drives": ["E:\\D"]}]
text = self.text(state)
self.assertIn("E:\\D is another drive: add it to copy from it too.", text)
self.assertNotIn("Read the first", text)
if __name__ == "__main__":
unittest.main()
+3 -3
View File
@@ -287,7 +287,7 @@ class InstallerLeavesGameDataOut(unittest.TestCase):
def test_install_leaves_it_out_and_names_it(self):
self.serve()
proc = self.run_installer("--no-game-data")
proc = self.run_installer("--no-game-data", "--verbose")
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
self.assertTrue((self.bios / "a.bin").exists())
self.assertFalse((self.bios / "game.wad").exists())
@@ -297,10 +297,10 @@ class InstallerLeavesGameDataOut(unittest.TestCase):
def test_check_and_dry_run_leave_it_out(self):
self.serve()
for mode, listed in (("--check", "1 file to download:"),
for mode, listed in (("--check", "1 file to download."),
("--dry-run", "Would download 1 file")):
with self.subTest(mode=mode):
proc = self.run_installer("--no-game-data", mode)
proc = self.run_installer("--no-game-data", mode, "--verbose")
self.assertIn(
"2 game data files left out (--no-game-data), 1 of them not installable: "
"game.wad, level.dat",
+39 -16
View File
@@ -171,8 +171,9 @@ Enter checks the BIOS folder when nothing can be copied.
Run at a terminal, the installer opens a full screen that a keyboard or a
controller drives. Three presses of A or Enter install everything the setup
reads: the setup found here, the plan, then the result. The plan screen says
how many files are missing, how much they weigh, how many are in place and how
much space is free, before anything is written.
what it installs, its size and where, before anything is written. **Details**
(`d`) adds the rest: how many files are in place, the free space, the files
left out and why.
The other choices sit one press away. **Choose systems** ticks the consoles to
keep, with the total to download under the list. It opens on its first system,
@@ -207,6 +208,7 @@ install starts.
| 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 |
| 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) | |
| Stop an install | Esc, `q`, Ctrl+C | B, Select+Start (B alone with muxterm's own keys) |
@@ -229,11 +231,12 @@ regions screen, Left and Right move the region under the cursor up and down the
order of preference. During the download, a stop waits for the files in
progress, and a second one stops at once. Select+Start on a handheld ends the
installer with a signal (SIGTERM), which stops it the same way. Either way the
result says how many files were installed, and it stays printed in the terminal
once the screen closes, with the command that repeats the run. A run that failed
keeps its log and names it, and every other run leaves nothing behind. A path
on the result is never split inside a word: one longer than the window takes
a line of its own, which the terminal folds. A command breaks only between its
result says how many files were installed and the next step, and it stays
printed in the terminal once the screen closes. Its **Details** hold the
command that repeats the run, printed in the terminal too once they were
opened. A run that failed keeps its log and names it, and every other run
leaves nothing behind. A path on the result is never split inside a word: one
longer than the window takes a line of its own, which the terminal folds. A command breaks only between its
arguments, an option kept with its value, each line but the last ending with
the continuation its shell reads (a backslash in sh, a backtick in PowerShell), so
the lines copied run as one command. Where an option and its path do not fit
@@ -315,8 +318,9 @@ them back on every way out, a stop with Ctrl+Z included. On the command line
screen, any screen showing a command and the screen where a path is typed,
the terminal keeps the mouse, so text is selected, copied and pasted as usual.
The result takes taps, so a touch screen can leave it: it is printed again in
the terminal once the screen closes, its commands with it, and its last line
says so. Elsewhere, and on the result, hold Shift while selecting. `--plain`
the terminal once the screen closes, with its commands when its Details were
opened, and the last line of the Details says so. Elsewhere, and on the
result, hold Shift while selecting. `--plain`
never asks for clicks. Clicks and taps were checked on 2026-10-27 in xterm,
tmux, GNU screen 4.09, Windows Terminal (WSL) and Termux
on Android 17. macOS Terminal and iTerm2 are not checked yet, nor the key
@@ -449,6 +453,25 @@ Batocera install into an empty folder, from the list of 2026-11-06.
The figures change with each release.
<!-- /generated: run-date -->
By default a run says five things: what it installs, where, a progress line
at a terminal, the result and the next step. A failure, a wrong copy or a file
an emulator needs that is not available is said too.
<!-- generated: run-default -->
```
Installing 18175 files (9.2 GB) for Batocera.
BIOS folder: /userdata/bios
Done. 18175 files installed.
80 files an emulator needs are not available here (--verbose names them).
Next: start Batocera and play.
```
<!-- /generated: run-default -->
With `--verbose` (`-v`) it says each step, the lists and the reasons too.
The lines below are those of the same run with `--verbose`.
### Before the run
The size depends on the platform and changes with each release. The run prints
@@ -473,8 +496,8 @@ Looking for setups here...
<!-- /generated: run-first-lines -->
A line above them, `RetroBIOS` and a code, identifies the installer: the
code is the start of the SHA-256 the command checked before running it. The
path is the folder it will write to.
code is the start of the SHA-256 the command checked before running it.
`--version` prints it too. The path is the folder it will write to.
### The file index and the download
@@ -501,8 +524,8 @@ is already there, and only the rest is downloaded. A download of 1 GB or more
is confirmed first, the size and the folder in the question.
The second phase downloads what is missing. At a terminal one progress line
moves with the count and the size. A pipe or a log gets one line per file,
as the log of the run below:
moves with the count and the size. With `--verbose` a pipe or a log gets one
line per file, as the log of the run below:
<!-- generated: run-download -->
@@ -537,8 +560,8 @@ To check this later, run the same command with --check.
<!-- /generated: run-end -->
When most of a download of 100 MB or more comes from one system, or from
one core of a single system, the end of the run says so and what narrowing
would save, once that saving is over half.
one core of a single system, the end of a `--verbose` run says so and what
narrowing would save, once that saving is over half.
<!-- generated: bulk-hint -->
@@ -601,7 +624,7 @@ against a list of four files:
<!-- generated: check-refused -->
```
3 files to download:
3 files to download.
2 of these files are in the BIOS folder in the wrong place: a run without --check copies them where the cores open them, and leaves the copies found as they are.
wrong copy: scph5501.bin
another region: the contents of scph5500.bin (japan)