chore: merge one card reading and offline wording

This commit is contained in:
Abdessamad Derraz committed 2026-10-27 06:12:18 +01:00
commit c2d745fbfa
26 files changed
+1414 -431

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@
$ErrorActionPreference = "Stop"
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
$defaultInstallSha256 = "2127f3c7a1646cc408520e364d2a08c6aa32a8905990c1a2581ce977b39017ab"
$defaultInstallSha256 = "2e88f22d2f3cd933d9b198b841367d6e2d7084de228fa0b12dc2f11c71c0c1e9"
$maximumInstallerBytes = 2MB
$installerExit = 0
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
+611 -242
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -18,7 +18,7 @@ esac
TEMP_INSTALLER=""
TEMP_DIRECTORY=""
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
DEFAULT_INSTALL_SHA256="2127f3c7a1646cc408520e364d2a08c6aa32a8905990c1a2581ce977b39017ab"
DEFAULT_INSTALL_SHA256="2e88f22d2f3cd933d9b198b841367d6e2d7084de228fa0b12dc2f11c71c0c1e9"
MAX_INSTALLER_BYTES=2097152
cleanup() {
+8 -8
View File
@@ -27,8 +27,8 @@
{
"dest": "roms/ports/retrobios/install.py",
"repo_path": "install.py",
"size": 673359,
"sha256": "2127f3c7a1646cc408520e364d2a08c6aa32a8905990c1a2581ce977b39017ab",
"size": 692187,
"sha256": "2e88f22d2f3cd933d9b198b841367d6e2d7084de228fa0b12dc2f11c71c0c1e9",
"executable": false
},
{
@@ -71,8 +71,8 @@
"muos": {
"archive": {
"dest": "ARCHIVE/RetroBIOS.muxapp",
"size": 725908,
"sha256": "da7fa84e69575d43fbf529c053f43f8f665963c19e25ed44923183a072ed919d"
"size": 744736,
"sha256": "17daef144acc01204912de201bbb6bbf8996d1a866a37074e7cbaffe879e911b"
},
"members": [
{
@@ -92,8 +92,8 @@
{
"name": "RetroBIOS/install.py",
"repo_path": "install.py",
"size": 673359,
"sha256": "2127f3c7a1646cc408520e364d2a08c6aa32a8905990c1a2581ce977b39017ab",
"size": 692187,
"sha256": "2e88f22d2f3cd933d9b198b841367d6e2d7084de228fa0b12dc2f11c71c0c1e9",
"executable": false
},
{
@@ -166,8 +166,8 @@
{
"dest": ".config/modules/retrobios/install.py",
"repo_path": "install.py",
"size": 673359,
"sha256": "2127f3c7a1646cc408520e364d2a08c6aa32a8905990c1a2581ce977b39017ab",
"size": 692187,
"sha256": "2e88f22d2f3cd933d9b198b841367d6e2d7084de228fa0b12dc2f11c71c0c1e9",
"executable": false
},
{
+2 -2
View File
@@ -160,13 +160,13 @@ handheld_guide:
- name: Onion
folder: '`BIOS` at the root of the card'
folder_source: https://onionui.github.io/docs/emulators
devices: [Miyoo Mini, Miyoo Mini+]
devices: [Miyoo Mini, Miyoo Mini Plus]
devices_source: https://github.com/OnionUI/Onion
- name: spruceOS
folder: '`BIOS` at the root of the card'
folder_source: https://github.com/spruceUI/spruceOS/wiki/10.-Adding-BIOS
devices: [Miyoo A30, Miyoo Flip, Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip,
TrimUI Smart Pro, TrimUI Brick, Anbernic RG35XX Plus, RG40XX H, Powkiddy RGB30]
TrimUI Smart Pro, TrimUI Brick, RG35XX Plus, RG40XX H, Powkiddy RGB30]
devices_source: https://github.com/spruceUI/spruceOS/wiki/Supported-Devices
- name: Allium
folder: '`BIOS` at the root of the card'
+1 -1
View File
@@ -3,8 +3,8 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
+1 -1
View File
@@ -3,7 +3,7 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from your old card or a folder
+2 -2
View File
@@ -3,13 +3,13 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
1 wrong copy: backed up, replaced.
Free space: 50.0 GB
The 1 wrong copy is replaced, the old
one kept in a backup folder.
> Install
Choose systems
Copy from your old card or a folder
Options
Quit
A Install B Back Y Options ? Help
+1 -1
View File
@@ -3,8 +3,8 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
+22 -22
View File
@@ -2,7 +2,7 @@
Choose the setup that will use the BIOS files.
Filter (/ to type)
Filter: setup or device (/)
> RetroArch
Batocera
Recalbox
@@ -10,31 +10,31 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
RetroPie
AmberELEC PAN4ELEC
EmuELEC
KNULLI, SD card
MinUI, SD card
NextUI, SD card
MyMinUI, SD card
Allium, SD card RetroArch files
Allium, SD card Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip
AmberELEC, SD card Anbernic RG351, RG552
Anbernic stock firmware, SD card RetroArch files
ArchR, SD card RetroArch files
ArkOS, SD card RetroArch files
CrossMix-OS, SD card RetroArch files
dArkOS, SD card RetroArch files
ArchR, SD card R36S, R33S, R36S Soysauce, Odroid Go Advance, Odroid Go Super, RG351V, +6
ArkOS, SD card RG353M, RG353V, RG503, RGB30, RK2023, RG351MP, RG351V, R35S, RGB10
CrossMix-OS, SD card TrimUI Smart Pro
dArkOS, SD card Odroid Go Advance, RGB10, RG351MP, G350
EmuELEC, SD card Odroid Go
JELOS, SD card RetroArch files
KOLOS, SD card RetroArch files
Koriki, SD card RetroArch files
MiyooCFW, SD card RetroArch files
muOS, SD card RetroArch files
Onion, SD card RetroArch files
plumOS, SD card RetroArch files
RetroOZ, SD card RetroArch files
spruceOS, SD card RetroArch files
UnofficialOS, SD card RetroArch files
KNULLI, SD card Anbernic RG-Arc-S, RG28XX, RG34XX, RG35XX Original, RG35XX Plus, RG35XX H, +16
KOLOS, SD card RG353P, RG353M, RG353V, RG353VS, RG503, Powkiddy RK2023, RGB30, RGB10 Max 3
Koriki, SD card Miyoo Mini
MinUI, SD card Anbernic, Miyoo, TrimUI
MiyooCFW, SD card BittBoy, Pocket Go, PowKiddy Q90, PowKiddy V90, PowKiddy Q20 Mini, Sup M3, +1
muOS, SD card RG35XX H, RG35XX Plus, RG35XX SP, RG35XX 2024, RG34XX H, RG40XX H, RG40XX V, +4
MyMinUI, SD card Anbernic, Miyoo
NextUI, SD card TrimUI Brick, TrimUI Smart Pro
Onion, SD card Miyoo Mini, Miyoo Mini Plus
plumOS, SD card MagicX XU20 V32
RetroOZ, SD card OGS, OGA, RGB10Max, RGB10Max2
ROCKNIX, SD card Anbernic RG351, RG353, RG503, RG552, RG 55G1, RG ARC, RG35XX 2024, +38
spruceOS, SD card Miyoo A30, Miyoo Flip, Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip, +5
UnofficialOS, SD card R35S, R36S, RG351M, RG351P, RG351V, RG353M, RG353P, RG353V, RG503, +5
A/Enter Choose B/Esc Back / Filter ? Help
+1 -1
View File
@@ -1,6 +1,6 @@
RetroBIOS: Which setup?
Filter (/ to type)
Filter: setup or device (/)
> RetroArch
Batocera
Recalbox
+2 -2
View File
@@ -1,6 +1,6 @@
RetroBIOS: Which setup?
Filter (/ to type)
Filter: setup or device (/)
> RetroArch
Batocera
Recalbox
@@ -8,8 +8,8 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
1-10 of 34
A Choose B Back / Filter ? Help
+9 -9
View File
@@ -1,6 +1,6 @@
RetroBIOS: Which setup?
Filter (/ to type)
Filter: setup or device (/)
> RetroArch
Batocera
Recalbox
@@ -8,17 +8,17 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
RetroPie
AmberELEC PAN4ELEC
EmuELEC
KNULLI, SD card
MinUI, SD card
NextUI, SD card
MyMinUI, SD card
Allium, SD card RetroArch files
Allium, SD card Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip
AmberELEC, SD card Anbernic RG351, RG552
Anbernic stock firmware, SD card RetroArch files
ArchR, SD card R36S, R33S, R36S Soysauce, Odroid Go Advance, +8
ArkOS, SD card RG353M, RG353V, RG503, RGB30, RK2023, RG351MP, RG351V, +2
CrossMix-OS, SD card TrimUI Smart Pro
dArkOS, SD card Odroid Go Advance, RGB10, RG351MP, G350
EmuELEC, SD card Odroid Go
1-19 of 34
A/Enter Choose B/Esc Back / Filter ? Help
+13 -13
View File
@@ -76,9 +76,9 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
1-10 of 34
A Choose B Back
=== platforms 80x24
@@ -92,18 +92,18 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
RetroPie
AmberELEC PAN4ELEC
EmuELEC
KNULLI, SD card
MinUI, SD card
NextUI, SD card
MyMinUI, SD card
Allium, SD card RetroArch files
Allium, SD card Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip
AmberELEC, SD card Anbernic RG351, RG552
Anbernic stock firmware, SD card RetroArch files
ArchR, SD card R36S, R33S, R36S Soysauce, Odroid Go Advance, +8
ArkOS, SD card RG353M, RG353V, RG503, RGB30, RK2023, RG351MP, RG351V, +2
CrossMix-OS, SD card TrimUI Smart Pro
dArkOS, SD card Odroid Go Advance, RGB10, RG351MP, G350
EmuELEC, SD card Odroid Go
1-19 of 34
A Choose B Back
=== folder 40x12
@@ -436,7 +436,7 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from your old card or a folder
@@ -449,15 +449,15 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
1 wrong copy: backed up, replaced.
Free space: 50.0 GB
The 1 wrong copy is replaced, the old
one kept in a backup folder.
> Install
Choose systems
Copy from your old card or a folder
Options
Quit
A Install B Back Y Options
=== plan 80x24
RetroBIOS: RetroArch
@@ -465,8 +465,8 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
+13 -13
View File
@@ -76,9 +76,9 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
1-10 of 34
A Choose B Back
=== platforms 80x24
@@ -92,18 +92,18 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
RetroPie
AmberELEC PAN4ELEC
EmuELEC
KNULLI, SD card
MinUI, SD card
NextUI, SD card
MyMinUI, SD card
Allium, SD card RetroArch files
Allium, SD card Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip
AmberELEC, SD card Anbernic RG351, RG552
Anbernic stock firmware, SD card RetroArch files
ArchR, SD card R36S, R33S, R36S Soysauce, Odroid Go Advance, +8
ArkOS, SD card RG353M, RG353V, RG503, RGB30, RK2023, RG351MP, RG351V, +2
CrossMix-OS, SD card TrimUI Smart Pro
dArkOS, SD card Odroid Go Advance, RGB10, RG351MP, G350
EmuELEC, SD card Odroid Go
1-19 of 34
A Choose B Back
=== folder 40x12
@@ -436,7 +436,7 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from your old card or a folder
@@ -449,15 +449,15 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
1 wrong copy: backed up, replaced.
Free space: 50.0 GB
The 1 wrong copy is replaced, the old
one kept in a backup folder.
> Install
Choose systems
Copy from your old card or a folder
Options
Quit
A Install B Back Y Options
=== plan 80x24
RetroBIOS: RetroArch
@@ -465,8 +465,8 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
+13 -13
View File
@@ -76,9 +76,9 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
1-10 of 34
A Choose B Back
=== platforms 80x24
@@ -92,18 +92,18 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
RetroPie
AmberELEC PAN4ELEC
EmuELEC
KNULLI, SD card
MinUI, SD card
NextUI, SD card
MyMinUI, SD card
Allium, SD card RetroArch files
Allium, SD card Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip
AmberELEC, SD card Anbernic RG351, RG552
Anbernic stock firmware, SD card RetroArch files
ArchR, SD card R36S, R33S, R36S Soysauce, Odroid Go Advance, +8
ArkOS, SD card RG353M, RG353V, RG503, RGB30, RK2023, RG351MP, RG351V, +2
CrossMix-OS, SD card TrimUI Smart Pro
dArkOS, SD card Odroid Go Advance, RGB10, RG351MP, G350
EmuELEC, SD card Odroid Go
1-19 of 34
A Choose B Back
=== folder 40x12
@@ -436,7 +436,7 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from your old card or a folder
@@ -449,15 +449,15 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
1 wrong copy: backed up, replaced.
Free space: 50.0 GB
The 1 wrong copy is replaced, the old
one kept in a backup folder.
> Install
Choose systems
Copy from your old card or a folder
Options
Quit
A Install B Back Y Options
=== plan 80x24
RetroBIOS: RetroArch
@@ -465,8 +465,8 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
+16 -16
View File
@@ -55,7 +55,7 @@
=== platforms 40x12
RetroBIOS: Which setup?
Filter (/ to type)
Filter: setup or device (/)
> RetroArch
Batocera
Recalbox
@@ -68,7 +68,7 @@
=== platforms 40x15
RetroBIOS: Which setup?
Filter (/ to type)
Filter: setup or device (/)
> RetroArch
Batocera
Recalbox
@@ -76,15 +76,15 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
1-10 of 34
Enter Choose Esc Back / Filter
=== platforms 80x24
RetroBIOS: Which setup?
Filter (/ to type)
Filter: setup or device (/)
> RetroArch
Batocera
Recalbox
@@ -92,18 +92,18 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
RetroPie
AmberELEC PAN4ELEC
EmuELEC
KNULLI, SD card
MinUI, SD card
NextUI, SD card
MyMinUI, SD card
Allium, SD card RetroArch files
Allium, SD card Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip
AmberELEC, SD card Anbernic RG351, RG552
Anbernic stock firmware, SD card RetroArch files
ArchR, SD card R36S, R33S, R36S Soysauce, Odroid Go Advance, +8
ArkOS, SD card RG353M, RG353V, RG503, RGB30, RK2023, RG351MP, RG351V, +2
CrossMix-OS, SD card TrimUI Smart Pro
dArkOS, SD card Odroid Go Advance, RGB10, RG351MP, G350
EmuELEC, SD card Odroid Go
1-19 of 34
Enter Choose Esc Back / Filter ? Help
=== folder 40x12
@@ -382,7 +382,7 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from your old card or a folder
@@ -395,15 +395,15 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
1 wrong copy: backed up, replaced.
Free space: 50.0 GB
The 1 wrong copy is replaced, the old
one kept in a backup folder.
> Install
Choose systems
Copy from your old card or a folder
Options
Quit
Enter Install Esc Back o Options
=== plan 80x24
RetroBIOS: RetroArch
@@ -411,8 +411,8 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
+13 -13
View File
@@ -76,9 +76,9 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
1-10 of 34
A Choose R2 Back
=== platforms 80x24
@@ -92,18 +92,18 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
RetroPie
AmberELEC PAN4ELEC
EmuELEC
KNULLI, SD card
MinUI, SD card
NextUI, SD card
MyMinUI, SD card
Allium, SD card RetroArch files
Allium, SD card Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip
AmberELEC, SD card Anbernic RG351, RG552
Anbernic stock firmware, SD card RetroArch files
ArchR, SD card R36S, R33S, R36S Soysauce, Odroid Go Advance, +8
ArkOS, SD card RG353M, RG353V, RG503, RGB30, RK2023, RG351MP, RG351V, +2
CrossMix-OS, SD card TrimUI Smart Pro
dArkOS, SD card Odroid Go Advance, RGB10, RG351MP, G350
EmuELEC, SD card Odroid Go
1-19 of 34
A Choose R2 Back
=== folder 40x12
@@ -436,7 +436,7 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from your old card or a folder
@@ -449,15 +449,15 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
1 wrong copy: backed up, replaced.
Free space: 50.0 GB
The 1 wrong copy is replaced, the old
one kept in a backup folder.
> Install
Choose systems
Copy from your old card or a folder
Options
Quit
A Install R2 Back L2 Options
=== plan 80x24
RetroBIOS: RetroArch
@@ -465,8 +465,8 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
+13 -13
View File
@@ -76,9 +76,9 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
1-10 of 34
Start Choose B Back
=== platforms 80x24
@@ -92,18 +92,18 @@
EmuDeck
Lakka
RetroDECK
ROCKNIX, SD card
RomM
BizHawk
MiSTer FPGA
RetroPie
AmberELEC PAN4ELEC
EmuELEC
KNULLI, SD card
MinUI, SD card
NextUI, SD card
MyMinUI, SD card
Allium, SD card RetroArch files
Allium, SD card Miyoo Mini, Miyoo Mini Plus, Miyoo Mini Flip
AmberELEC, SD card Anbernic RG351, RG552
Anbernic stock firmware, SD card RetroArch files
ArchR, SD card R36S, R33S, R36S Soysauce, Odroid Go Advance, +8
ArkOS, SD card RG353M, RG353V, RG503, RGB30, RK2023, RG351MP, RG351V, +2
CrossMix-OS, SD card TrimUI Smart Pro
dArkOS, SD card Odroid Go Advance, RGB10, RG351MP, G350
EmuELEC, SD card Odroid Go
1-19 of 34
Start Choose B Back
=== folder 40x12
@@ -436,7 +436,7 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
1 wrong copy: backed up, replaced.
> Install
Choose systems
Copy from your old card or a folder
@@ -449,15 +449,15 @@
4 files to download, 1.0 MB
BIOS folder: ~/.co.../retroarch/system
1 in place, 3 missing, 1 wrong copy
1 wrong copy: backed up, replaced.
Free space: 50.0 GB
The 1 wrong copy is replaced, the old
one kept in a backup folder.
> Install
Choose systems
Copy from your old card or a folder
Options
Quit
Start Install B Back
=== plan 80x24
RetroBIOS: RetroArch
@@ -465,8 +465,8 @@
4 files to download, 1.0 MB
BIOS folder: ~/.config/retroarch/system
1 in place, 3 missing, 1 wrong copy
Free space: 50.0 GB
The 1 wrong copy is replaced, the old one kept in a backup folder.
Free space: 50.0 GB
> Install
Choose systems
+1 -1
View File
@@ -4249,7 +4249,7 @@ class DestAloneFollowsWhatIsFoundHere(unittest.TestCase):
plat, folder = install._platform_for_dest("linux", root, True)
self.assertEqual((plat, folder), ("retroarch", root))
self.assertIn(f"{root} is not an Onion SD card", out.getvalue())
self.assertIn("Onion, SD card (RetroArch files)", out.getvalue())
self.assertIn("Onion, SD card (Miyoo Mini, Miyoo Mini Plus)", out.getvalue())
def test_nothing_found_is_retroarch_said_aloud(self):
plat, out = self._pick([])
+3
View File
@@ -896,7 +896,10 @@ class AFolderAroundTheBiosFolder(FromFolder):
argv = ["install.py", "--platform", "retroarch", "--dest", str(bios),
"--manifest", str(self.list), "--from", str(card), "--no-input", "--yes"]
out = io.StringIO()
# The host's own system: on WSL the run turned drive letters on for
# every test after it.
with mock.patch.object(sys, "argv", argv), \
mock.patch.object(install, "detect_os", return_value="linux"), \
mock.patch.dict(os.environ, {"RETROBIOS_INSTALL_OFFLINE": "1"}), \
mock.patch.object(os, "replace", refusing(real_replace)), \
mock.patch.object(os, "rename", refusing(real_rename)), \
+94 -1
View File
@@ -663,7 +663,7 @@ class OfflineMakesNoRequest(Scratch):
self.assertEqual((self.dest / "scph5501.bin").read_bytes(), US)
self.assertFalse((self.dest / "gba_bios.bin").exists())
self.assertIn("no network request", proc.stdout + proc.stderr)
self.assertIn("run without --offline", proc.stdout + proc.stderr)
self.assertIn("without --offline", proc.stdout + proc.stderr)
self.assertNotIn("trying again", proc.stderr)
def test_modes_that_need_the_network_refuse_it(self):
@@ -843,5 +843,98 @@ class OfflineLeavesFilesMissing(Scratch):
self.assertNotIn("no longer in the source", proc.stdout)
class OneOfflineVocabulary(Scratch):
"""code_audit_14 P3-1 to P3-4, tui_audit_7 P3-4: --offline said one
thing at a terminal and another without questions, on the screen and
under --check."""
def old(self) -> Path:
old = self.root / "old"
old.mkdir()
(old / "scph5501.bin").write_bytes(US)
return old
def test_a_run_without_questions_says_missing_not_failed(self):
"""P3-2: "Finished with errors ... 3 files failed" and "Run the
same command again to retry only those"."""
proc = self.run_installer("--offline", "--manifest", str(self.list),
"--from", str(self.old()), "--no-input")
out = proc.stdout + proc.stderr
self.assertEqual(proc.returncode, 1, out)
self.assertIn("Finished with files missing. 1 file installed, 3 not fetched "
"(--offline).", out)
self.assertIn(f"They are not in {self.root / 'old'}", out)
for said in ("Finished with errors", "files failed", "retry",
"copy them from your old card"):
self.assertNotIn(said, out)
def test_nothing_installed_is_said_so(self):
"""P3-3: "Finished with files missing. 0 files installed"."""
failures = [("a.bin", install.NOT_WRITABLE), ("b.bin", install.NOT_WRITABLE)]
with contextlib.redirect_stdout(io.StringIO()) as out, \
self.assertRaises(SystemExit) as stop:
install._report_outcome([self.dest], 0, 0, 2, 0, False, failures=failures,
offline=20)
self.assertEqual(stop.exception.code, 2)
self.assertIn("Nothing installed: 20 files not fetched (--offline), 2 failed.",
out.getvalue())
self.assertNotIn("0 files installed", out.getvalue())
def test_the_command_suggested_keeps_the_offline_setting(self):
"""P3-3: sudo does not pass RETROBIOS_INSTALL_OFFLINE on."""
import test_install_tui as tui
args = tui._args(platform="retroarch", declared_only=False, offline=False)
hint = install.Invocation()
with unittest.mock.patch.dict(os.environ, {install.OFFLINE_VARIABLE: "1"}):
hint.record(args)
self.assertIn("--offline", hint.command("retroarch", self.dest, elevated=True))
def test_check_offline_says_what_cannot_come(self):
"""P3-4: "Run the same command without --check to fetch them"."""
proc = self.run_installer("--check", "--offline", "--manifest", str(self.list))
out = proc.stdout + proc.stderr
self.assertEqual(proc.returncode, 1, out)
self.assertNotIn("to fetch them", out)
self.assertIn("4 files need the internet, which --offline does not use.", out)
self.assertIn("Copy them from your old card or a folder (--from)", out)
def test_the_screen_says_missing_not_failed(self):
"""P3-2 on the full screen: "Finished with errors, 20 failed"."""
import test_install_tui as tui
reason = f"not fetched: {install.OFFLINE_WORDS}"
state = tui._installed_state(self.dest, 1, [{"dest": f"f{n}.bin", "reason": reason}
for n in range(20)])
state.summary["installed"] = 2
state.from_folders = [self.root / "from1"]
title, lines = install.tui_outcome(state)
self.assertEqual(title, "Finished with files missing")
text = " ".join(lines)
self.assertIn("2 installed, 20 not fetched (--offline).", text)
self.assertIn("They are not in", text)
self.assertNotIn("failed", text)
state.summary["installed"] = 0
self.assertEqual(install.tui_outcome(state)[0], "Nothing installed")
def test_the_line_menu_copies_from_a_folder(self):
"""tui_audit_7 P3-4: no way to a folder, and an install of nothing
offered."""
old = self.old()
install.LOCAL.reset()
self.addCleanup(install.LOCAL.reset)
answers = iter(["f", str(old), ""])
with unittest.mock.patch.dict(os.environ, {install.OFFLINE_VARIABLE: "1"}), \
unittest.mock.patch.object(install, "_ask", lambda _prompt: next(answers)), \
contextlib.redirect_stdout(io.StringIO()) as out:
self.assertIsNone(install._prompt_custom_selection("retroarch", [], []))
text = out.getvalue()
self.assertIn("Enter) check the BIOS folder, nothing can be downloaded", text)
self.assertIn("f) copy from a folder or an old card", text)
self.assertIn(f"Enter) install what is found in {old}", text)
self.assertEqual([index.label for index in install.LOCAL.folders], [str(old)])
self.assertNotIn("install everything", text)
if __name__ == "__main__":
unittest.main()
+511 -19
View File
@@ -594,7 +594,8 @@ class Flow(unittest.TestCase):
self.assertEqual(install.tui_rows(state)[state.focus["platforms"]].value, "retroarch")
press(state, "/", "b", "a", "t", "o")
self.assertEqual([row.value for row in install.tui_rows(state)][1:], ["batocera"])
press(state, "enter", "enter")
# tui_audit_7 P3-9.2: the one match is chosen with the first Enter.
press(state, "enter")
self.assertEqual(state.screen, "folder")
self.assertEqual(state.platform, "batocera")
state.focus["folder"] = 2
@@ -2194,9 +2195,13 @@ class AuditRoundThree(unittest.TestCase):
"MUOS at the root of its card.", proc.stderr)
self.assertIn("--dest <card>/MUOS/bios", proc.stderr)
self.assertEqual(list(root.iterdir()), [root / "roms"])
# Its BIOS folder named outright is taken as given.
self.assertEqual(install.card_dest("muos", "retroarch", root / "MUOS" / "bios"),
("retroarch", root / "MUOS" / "bios"))
# Its BIOS folder named outright is its card's, checked as the card
# is: on a folder with no MUOS it is refused, as typed it was.
with self.assertRaises(SystemExit), contextlib.redirect_stderr(io.StringIO()):
install.card_dest("muos", "retroarch", root / "MUOS" / "bios")
card = self.card({"MUOS/bios/": ""})
self.assertEqual(install.card_dest("muos", "retroarch", card / "MUOS" / "bios"),
("retroarch", card / "MUOS" / "bios"))
# On the device, the folder muOS binds it to.
bound = Path("/run/muos/storage/bios")
self.assertEqual(install.card_dest("muos", "retroarch", bound), ("retroarch", bound))
@@ -2212,9 +2217,10 @@ class AuditRoundThree(unittest.TestCase):
with self.assertRaises(SystemExit), \
contextlib.redirect_stderr(io.StringIO()) as err:
install.card_dest("muos", "retroarch", stray)
self.assertIn("is not a muOS SD card", err.getvalue())
self.assertEqual(install.card_dest("muos", "retroarch", root / "muos" / "BIOS"),
("retroarch", root / "muos" / "BIOS"))
self.assertIn("does not exist", err.getvalue())
card = self.card({"MUOS/bios/": ""})
self.assertEqual(install.card_dest("muos", "retroarch", card / "muos" / "BIOS"),
("retroarch", card / "muos" / "BIOS"))
def test_p1_1_a_card_two_systems_claim_says_so(self):
root = self.card({"MUOS/": "", "spruce/": ""})
@@ -3042,8 +3048,9 @@ class ACardSystemNamedOnAComputer(unittest.TestCase):
# without a word.
install._browse_to(state, muos)
press(state, "enter")
self.assertEqual(state.status, "That is a muOS card, not an Onion card. Choose "
"the card's top folder.")
# tui_audit_7 P3-1: it is a card's top folder, of another card.
self.assertEqual(state.status, "That is a muOS card. Insert the Onion card, or "
"go back and choose muOS.")
self.assertIsNone(state.dest)
def test_a_typed_card_root_is_checked_on_the_path_screen(self):
@@ -3614,7 +3621,9 @@ class LineModeOffline(unittest.TestCase):
out = term.raw.decode("utf-8", "replace")
self.assertIn("--offline: only the files found on this machine are installed, and no "
"--from names a folder to copy them from.", out)
self.assertIn("Enter) install what is found on this machine", out)
# tui_audit_7 P3-4: nothing to copy from, nothing to install.
self.assertIn("Enter) check the BIOS folder, nothing can be downloaded", out)
self.assertIn("f) copy from a folder or an old card", out)
self.assertIn("5 files need the internet, which --offline does not use.", out)
self.assertNotIn("Downloading", out)
self.assertNotIn("failed", out.lower())
@@ -3945,10 +3954,9 @@ class AGoesIntoASubfolder(unittest.TestCase):
with mock.patch.object(install, "_mounted_cards", return_value=[self.card]):
install._browse_to(self.state, self.home)
self.assertEqual(self.focused().action, "use")
self.assertIn("This is your home folder", self.state.status)
self.assertIn("No setup reads BIOS files in your home folder", self.state.status)
screen = " ".join(" ".join(install.tui_view(self.state, 80, 24)).split())
self.assertIn("This is your home folder: no setup reads its BIOS files here.",
screen)
self.assertIn("No setup reads BIOS files in your home folder.", screen)
def test_on_windows_the_other_drives_are_passed_over(self):
"""The card rows are drive roots there (E:\\): at D:\\ the first
@@ -5222,8 +5230,9 @@ class EveryCardSetupGoesToItsCard(unittest.TestCase):
for index, setup in enumerate(self.setups):
with self.subTest(system=setup.name):
card = _card_from_rules(self.root / f"card{index}", setup)
want = (_setup_folder(setup, card) if setup.known() or not setup.below
else card)
# code_audit_14 P2-1: a card no file names gets its folder
# below the root with --dest too, as typed.
want = _setup_folder(setup, card)
with contextlib.redirect_stdout(io.StringIO()):
by_dest = install.card_dest(setup.name, setup.plat, card)
self.assertEqual(by_dest, (setup.plat, want))
@@ -5244,7 +5253,7 @@ class EveryCardSetupGoesToItsCard(unittest.TestCase):
self.assertEqual(state.dest, want)
# Its card chosen as a card's root: its BIOS folder below it.
if setup.known():
choice = install.card_root_folder(setup.name, setup.plat, card)
choice = install.card_choice(setup.name, setup.plat, card)
self.assertEqual((choice.refused, choice.folder),
("", _setup_folder(setup, card)))
@@ -5261,7 +5270,7 @@ class EveryCardSetupGoesToItsCard(unittest.TestCase):
and setup.plat in install.CARD_PLATFORMS):
continue
with self.subTest(system=setup.name, card=name):
choice = install.card_root_folder(setup.name, setup.plat, card)
choice = install.card_choice(setup.name, setup.plat, card)
self.assertIn(f"is {install._a(name)} card, not "
f"{install._a(setup.name)} card", choice.refused)
self.assertTrue(install.card_choice(setup.name, setup.plat,
@@ -5276,7 +5285,7 @@ class EveryCardSetupGoesToItsCard(unittest.TestCase):
blank.mkdir()
for setup in self.setups:
with self.subTest(system=setup.name):
choice = install.card_root_folder(setup.name, setup.plat, blank)
choice = install.card_choice(setup.name, setup.plat, blank)
if setup.known() and not setup.fresh:
self.assertIn(f"is not {install._a(setup.name)} SD card", choice.refused)
else:
@@ -5462,7 +5471,7 @@ class AuditSix(unittest.TestCase):
rows = install.tui_rows(state)
self.assertEqual(state.screen, "browse")
self.assertEqual(rows[state.focus["browse"]].value, card)
self.assertEqual(state.status, "Choose the card below, or open the folder that holds it.")
self.assertEqual(state.status, "Choose the Onion card below.")
def test_a_number_outside_the_list_is_said(self):
"""tui_audit_6 P3-1: 9 was read as a folder."""
@@ -5551,5 +5560,488 @@ class AuditSix(unittest.TestCase):
self.assertIn(" +2 shared: counted once", install.tui_view(state, 40, 12))
class AuditSevenOneCardReading(unittest.TestCase):
"""code_audit_14 P2-1 to P3-12 and tui_audit_7 P2-5, P3-1: one reading
of a card's root, said the same on the screen and in line mode."""
def setUp(self):
self.root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
self.addCleanup(shutil.rmtree, self.root, True)
patcher = mock.patch.dict(install._DETECTED_OS, {})
patcher.start()
self.addCleanup(patcher.stop)
def blank(self, name: str = "USBSTICK") -> Path:
stick = self.root / name
(stick / "photos").mkdir(parents=True)
return stick
def start(self, **given) -> install.TuiState:
with mock.patch.object(install, "_mounted_cards", return_value=[]), \
mock.patch.object(install, "detect_platforms", return_value=[]), \
mock.patch.object(install, "detect_frontends", return_value=[]):
state, _effects = install.tui_start(_args(**given), "linux")
return state
def test_a_blank_card_gets_one_folder_and_one_note_everywhere(self):
"""code_audit_14 P2-1, tui_audit_7 P2-5: --dest kept the stick's
root, typed it got bios below it, and the screen said nothing."""
stick = self.blank()
for named, plat, below in (("knulli", "knulli", "bios"),
("rocknix", "rocknix", "roms/bios"),
("ArkOS", "retroarch", "bios")):
with self.subTest(system=named):
with contextlib.redirect_stdout(io.StringIO()) as out:
by_dest = install.card_dest(named, plat, stick)
self.assertEqual(by_dest, (plat, stick / below))
note = install.card_choice(named, plat, stick).note
self.assertTrue(note)
self.assertIn(note, out.getvalue())
state = self.start(platform=named, dest=str(stick))
self.assertEqual(state.dest, stick / below)
self.assertEqual(state.card_note, note)
# Kept on Create the folder? and on the plan.
self.assertEqual(state.screen, "create")
self.assertIn(note.split(":")[0],
" ".join(" ".join(install.tui_view(state, 120, 40)).split()))
install.tui_update(state, Key("enter"))
self.assertEqual(state.card_note, note)
def test_the_minui_family_refuses_a_blank_dest(self):
"""code_audit_14 P2-2: taken with --dest, refused typed."""
stick = self.blank()
for plat in ("minui", "nextui", "myminui"):
with self.subTest(platform=plat):
choice = install.card_choice(plat, plat, stick)
self.assertIn("SD card", choice.refused)
with self.assertRaises(SystemExit), \
contextlib.redirect_stderr(io.StringIO()):
install.card_dest(plat, plat, stick)
def test_a_card_mounted_as_its_bios_folder_is_the_card(self):
"""code_audit_14 P3-7: an Onion card mounted as BIOS was refused,
ROCKNIX's roms/bios was taken for KNULLI, a muOS card's folder
too."""
onion = self.root / "BIOS"
make_card(onion, "Onion")
choice = install.card_choice("Onion", "retroarch", onion)
self.assertEqual((choice.refused, choice.folder), ("", onion / "BIOS"))
knulli = self.root / "k" / "bios"
make_card(knulli, "KNULLI")
self.assertEqual(install.card_choice("knulli", "knulli", knulli).folder,
knulli / "bios")
rocknix = self.root / "ROCKNIX"
make_card(rocknix, "ROCKNIX")
refused = install.card_choice("knulli", "knulli", rocknix / "roms" / "bios")
self.assertIn("is a ROCKNIX card, not a KNULLI card", refused.refused)
muos = self.root / "MUOS"
make_card(muos, "muOS")
refused = install.card_choice("knulli", "knulli", muos / "MUOS" / "bios")
self.assertIn("is a muOS card", refused.refused)
def test_no_folder_above_a_mounted_drive_is_its_card(self):
"""A card mounted as MUOS beside a stick: /media/me, which holds
it, is no muOS card for the stick."""
stick = self.blank()
(self.root / "MUOS" / "bios").mkdir(parents=True)
with mock.patch.object(install, "_mounted_cards",
return_value=[stick, self.root / "MUOS"]):
choice = install.card_choice("knulli", "knulli", stick)
self.assertEqual((choice.refused, choice.folder), ("", stick / "bios"))
bios = self.root / "mnt" / "bios"
(bios / "x").mkdir(parents=True)
with mock.patch.object(install, "_mounted_cards", return_value=[bios]):
# A drive mounted at a folder named bios is the card itself.
self.assertEqual(install._card_and_below(
install.card_setup("knulli", "knulli"), bios), (bios, False))
def test_a_refused_dest_says_why_in_the_events(self):
"""code_audit_14 P3-8: code 1 and no reason in the stream."""
stick = self.blank()
proc = install_run("--events", "--platform", "onion", "--dest", str(stick),
"--manifest", str(REPO_ROOT / "install" / "retroarch.json"),
"--dry-run", env={install.OFFLINE_VARIABLE: "1"})
self.assertEqual(proc.returncode, 1, proc.stderr)
events = [json.loads(line) for line in proc.stdout.splitlines()]
error = next(event for event in events if event["event"] == "error")
self.assertEqual((error["stage"], error["reason"]), ("detect", "not_found"))
self.assertIn("is not an Onion SD card", error["message"])
self.assertEqual(events[-1], {"event": "end", "code": 1})
table = (REPO_ROOT / "wiki" / "installer.md").read_text(encoding="utf-8")
self.assertIn("not the card of the system named", table)
def test_a_card_no_file_names_is_titled_by_its_system(self):
"""code_audit_14 P3-9: an ArkOS card chosen read as RetroArch."""
stick = self.blank()
state = self.start(platform="ArkOS", dest=str(stick))
self.assertEqual(install.detected_os("retroarch", state.dest), "ArkOS")
def test_a_list_named_for_another_systems_card_says_so(self):
"""code_audit_14 P3-10: --platform batocera on an Onion card."""
onion = self.root / "ONION"
make_card(onion, "Onion")
choice = install.card_choice("batocera", "batocera", onion)
self.assertEqual((choice.refused, choice.folder), ("", onion))
self.assertIn("is an Onion card", choice.note)
self.assertIn("--platform onion", choice.note)
state = self.start(platform="batocera", dest=str(onion))
self.assertEqual(state.card_note, choice.note)
def test_amberelec_and_emuelec_are_asked_for_their_card(self):
"""code_audit_14 P3-11: "Use this folder" on /storage/roms of a PC."""
for plat, name in (("amberelec", "AmberELEC"), ("emuelec", "EmuELEC")):
with self.subTest(system=name):
setup = install.card_setup(plat, plat)
self.assertEqual((setup.name, setup.below), (name, ""))
state = self.start(platform=plat)
self.assertEqual(state.screen, "folder")
self.assertIsNone(state.folder_default)
self.assertIn("Where is the SD card?",
" ".join(install.tui_view(state, 80, 24)))
stick = self.blank(f"{name}STICK")
choice = install.card_choice(plat, plat, stick)
self.assertEqual(choice.folder, stick)
self.assertIn("cannot tell", choice.note)
def test_install_py_has_two_blank_lines_around_each_definition(self):
"""code_audit_14 P3-12: E305 after _card_mount, and the same slip
before _Interrupted, TuiRow and _setup_label (pycodestyle E302,
E303, E305 at top level)."""
import ast
lines = (REPO_ROOT / "install.py").read_text(encoding="utf-8").splitlines()
body = ast.parse("\n".join(lines)).body
def blank_before(node: ast.stmt) -> int:
# The longest run of blank lines between the code above and the
# statement, its decorators and comments in between, as
# pycodestyle counts them.
index = min([node.lineno] + [item.lineno for item in
getattr(node, "decorator_list", [])]) - 2
runs, run = [], 0
while index >= 0 and (not lines[index].strip() or lines[index].startswith("#")):
if lines[index].strip():
runs.append(run)
run = 0
else:
run += 1
index -= 1
return max(runs + [run])
definitions = (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)
for before, after in zip(body, body[1:]):
count = blank_before(after)
with self.subTest(line=after.lineno):
if isinstance(after, definitions):
self.assertEqual(count, 2)
elif isinstance(before, definitions):
self.assertEqual(count, 2)
def test_a_refusal_is_said_once_and_another_card_names_its_remedy(self):
"""tui_audit_7 P3-1: said twice, and "Choose the card's top folder"
for the top folder of another card."""
stick = self.blank()
state = self.start(platform="onion")
state.pad = "keys"
install._card_status(state, install.card_choice("Onion", "retroarch", stick))
after = install._status_after(state)
self.assertEqual(len(after), 1)
self.assertTrue(after[0].endswith("? says more."))
state.help = True
self.assertIn("Onion keeps .tmp_update",
" ".join(" ".join(install.tui_view(state, 80, 24)).split()))
state.help = False
muos = self.root / "MUOS"
make_card(muos, "muOS")
install._card_status(state, install.card_choice("Onion", "retroarch", muos))
self.assertEqual(state.status, "That is a muOS card. Insert the Onion card, or go "
"back and choose muOS.")
with mock.patch.object(install, "_ask", side_effect=[str(muos), str(stick),
KeyboardInterrupt]), \
mock.patch.object(install, "_mounted_cards", return_value=[]), \
contextlib.redirect_stdout(io.StringIO()) as out, \
self.assertRaises(KeyboardInterrupt):
install.card_install_dest("Onion", "retroarch", True)
self.assertIn("Insert the Onion card, or choose muOS for that one.", out.getvalue())
def test_line_mode_names_the_card_picked_and_its_folder(self):
"""tui_audit_7 P2-3, P3-5: no echo of the card picked, "Mount the
card" under two mounted, /mnt/d where the list says D:."""
cards = [self.root / "ONION", self.root / "ONION2"]
for card in cards:
make_card(card, "Onion")
with mock.patch.object(install, "_ask", return_value="2"), \
mock.patch.object(install, "_mounted_cards", return_value=cards), \
mock.patch.object(install, "_missing_dest", return_value=True), \
contextlib.redirect_stdout(io.StringIO()) as out, \
contextlib.redirect_stderr(io.StringIO()):
dest = install.card_install_dest("Onion", "retroarch", True)
self.assertEqual(dest, cards[1] / "BIOS")
text = out.getvalue()
self.assertIn("Type its number or its top folder:", text)
self.assertNotIn("Mount the card", text)
self.assertIn(f"ONION2: Onion files go to {cards[1] / 'BIOS'}", text)
self.assertEqual(install.detected_os("retroarch", dest), "Onion")
with mock.patch.object(install, "_ask", return_value=""), \
contextlib.redirect_stdout(io.StringIO()) as out:
install._prompt_custom_selection("retroarch", [], [], name="Onion",
bios_path=dest)
self.assertIn("Onion: 0 files", out.getvalue())
self.assertIn(f"BIOS folder: {dest}", out.getvalue())
proc = install_run("--platform", "onion", "--dest", str(cards[0]), "--dry-run",
"--manifest", str(REPO_ROOT / "install" / "retroarch.json"),
env={install.OFFLINE_VARIABLE: "1"})
self.assertNotIn("gets the RetroArch file list", proc.stdout)
def test_wsl_names_the_folder_by_its_drive_letter(self):
"""tui_audit_7 P3-6, code_audit_14 P3-5."""
with mock.patch.dict(install._WSL, {"active": True, "root": "/mnt"}):
self.assertEqual(install._short_path(Path("/mnt/d/BIOS"), HOME), "D:\\BIOS")
self.assertEqual(install.shown_path(Path("/mnt/d")), "D:\\")
args = _args(dest=Path("D:\\"), save_dir=Path("F:\\saves"))
setattr(args, "from", [Path("E:\\")])
args.from_pack = [Path("E:\\pack.zip")]
install._wsl_dest(args)
self.assertEqual((args.dest, getattr(args, "from"), args.from_pack, args.save_dir),
(Path("/mnt/d"), [Path("/mnt/e")], [Path("/mnt/e/pack.zip")],
Path("/mnt/f/saves")))
with mock.patch.dict(install._WSL, {"active": False}):
self.assertEqual(install._short_path(Path("/mnt/d/BIOS"), HOME), "/mnt/d/BIOS")
def test_a_multi_word_system_example_can_be_pasted(self):
"""code_audit_14 P3-6."""
with mock.patch.object(install, "_mounted_cards", return_value=[]), \
contextlib.redirect_stderr(io.StringIO()) as err, \
self.assertRaises(SystemExit):
install.card_install_dest("Anbernic stock firmware", "retroarch", False)
self.assertIn("--platform anbernicstockfirmware --dest", err.getvalue())
class AuditSevenScreens(unittest.TestCase):
"""tui_audit_7 P2-1 to P3-9: what each screen offers and says."""
def setUp(self):
self.root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
self.addCleanup(shutil.rmtree, self.root, True)
def setups(self, pad: str = "keys") -> install.TuiState:
state = install.TuiState(os_type="linux", home=HOME)
state.pad = pad
state.stack = ["platforms"]
state.focus["platforms"] = 1
return state
def test_a_handheld_is_found_by_its_device_in_few_presses(self):
"""P2-1: Onion was row 30 of 34, its row said "RetroArch files"."""
state = self.setups("gptokeyb")
rows = install.tui_rows(state)
labels = [row.label for row in rows]
self.assertEqual(labels[1], "RetroArch")
cards = [index for index, row in enumerate(rows) if row.label.endswith(", SD card")]
# One block, right after RetroArch.
self.assertEqual(cards, list(range(2, 2 + len(cards))))
onion = labels.index("Onion, SD card")
self.assertIn("Miyoo Mini", rows[onion].right)
# At 40x12 a page is 6 rows: L1/R1 then Down, then A.
page = 12 - 6
presses = (onion - 1) // page + (onion - 1) % page + 1
self.assertLessEqual(presses, 4)
text = " ".join(install.tui_view(state, 40, 12))
self.assertIn("handhelds come after RetroArch", " ".join(text.split()))
state.pad = "keys"
text = " ".join(" ".join(install.tui_view(state, 80, 24)).split())
self.assertIn("type your device's name (/)", text)
self.assertIn("Filter by setup or device name",
[what for _key, what in install._help_lines(state)])
# Something found here: the list keeps its order.
found = fresh_state()
found.stack = ["setup", "platforms"]
self.assertEqual([row.label for row in install.tui_rows(found)][2], "Batocera")
def test_line_mode_takes_a_device_name(self):
"""P2-1: "miyoo" refused at the line menu."""
with mock.patch.object(install, "_ask", side_effect=["bittboy"]), \
contextlib.redirect_stdout(io.StringIO()) as out:
self.assertEqual(install._choose_platform("Which?"), "MiyooCFW")
self.assertIn("your device's name", out.getvalue())
choices = install._line_setups()
onion = [name for name, _label in choices].index("Onion") + 1
with mock.patch.object(install, "_ask", side_effect=["miyoo mini+", str(onion)]), \
contextlib.redirect_stdout(io.StringIO()) as out:
self.assertEqual(install._choose_platform("Which?"), "Onion")
self.assertIn("setups run on 'miyoo mini+'", out.getvalue())
self.assertIn(f"{onion}) Onion, SD card (Miyoo Mini, Miyoo Mini Plus)", out.getvalue())
def test_one_spelling_per_device_and_one_press_for_one_match(self):
"""P3-9.2, P3-9.3, P3-9.4."""
for name, devices in install._SETUP_DEVICES.items():
with self.subTest(system=name):
self.assertFalse(any("+" in device for device in devices))
self.assertFalse(any(device.startswith("Anbernic RG35XX")
for device in install._SETUP_DEVICES["spruceOS"]))
state = self.setups()
state.filters["platforms"] = "mini+"
self.assertIn("Onion, SD card", [row.label for row in install.tui_rows(state)])
state.filters["platforms"] = "koriki"
state.focus["platforms"] = 0
self.assertEqual(install._primary(state), "A/Enter Choose")
with mock.patch.object(install, "_mounted_cards", return_value=[]):
press(state, "enter")
self.assertEqual(state.screen, "folder")
self.assertEqual(state.card_bound[0], "Koriki")
rows = {row.value: row for row in install.tui_rows(self.setups())}
self.assertNotIn("PAN4ELEC", rows["amberelec"].right)
def test_help_rows_keep_their_column_at_40(self):
"""P3-9.1: "/ Filter (Esc clears)" ran back to the left edge."""
for pad in ("gptokeyb", "gptokeyb_muxterm", "miyoo", "evmapy"):
with self.subTest(pad=pad):
state = self.setups(pad)
state.filters["platforms"] = "x"
state.help = True
lines = [line[1:] for line in install.tui_view(state, 40, 12)[2:-1]
if line.strip()]
column = lines[0].index("Move")
for line in lines:
self.assertTrue(line[:column].strip() == "" or line[column - 2:column]
== " ", line)
def test_copy_from_a_folder_with_none_says_so(self):
"""P2-2: the plan came back the same."""
state = checked_state()
old = self.root / "old"
old.mkdir()
state.from_folders = [old]
text = " ".join(" ".join(install.tui_view(state, 80, 24)).split())
self.assertIn("old holds none of these files.", text)
state.found = [found("gba_bios.bin", str(old))]
text = " ".join(" ".join(install.tui_view(state, 80, 24)).split())
self.assertNotIn("holds none", text)
stick = self.root / "STICK"
stick.mkdir()
state.home = self.root
with mock.patch.object(install, "_mounted_cards", return_value=[stick]), \
mock.patch.object(install, "_listed", return_value=[]):
install._open_from_browser(state)
rows = install.tui_rows(state)
self.assertEqual(rows[state.focus["browse"]].value, stick)
def test_offline_plan_names_the_folder_already_copied_from(self):
"""code_audit_14 P3-1."""
state = checked_state()
state.from_folders = [self.root / "from1"]
state.found = [found("gba_bios.bin", str(self.root / "from1"))]
with mock.patch.dict(os.environ, {install.OFFLINE_VARIABLE: "1"}):
text = " ".join(" ".join(install.tui_view(state, 100, 40)).split())
self.assertIn("They are not in from1", text)
self.assertNotIn("Copy them from your old card", text)
def test_the_card_browser_focuses_the_card_asked_for(self):
"""P3-2: the muOS card, listed first, was focused for Onion."""
muos, onion, second = self.root / "A_MUOS", self.root / "B_ONION", self.root / "C"
make_card(muos, "muOS")
make_card(onion, "Onion")
make_card(second, "Onion")
with mock.patch.object(install, "_mounted_cards", return_value=[]), \
mock.patch.object(install, "detect_platforms", return_value=[]), \
mock.patch.object(install, "detect_frontends", return_value=[]):
state, _effects = install.tui_start(_args(platform="onion"), "linux")
state.home = self.root
state.focus["folder"] = [row.action for row in install.tui_rows(state)].index(
"browse")
with mock.patch.object(install, "_mounted_cards",
return_value=[muos, onion, second]):
press(state, "enter")
self.assertEqual(install.tui_rows(state)[state.focus["browse"]].value, onion)
self.assertEqual(state.status, "Choose the Onion card below.")
for line in install.tui_view(state, 40, 12):
self.assertNotIn("Go back", line)
def test_confirm_offers_no_install_while_it_counts(self):
"""P3-3."""
state = checked_state()
state.stack.append("confirm")
state.running, state.dry = True, None
self.assertEqual(install.tui_view(state, 80, 24)[-1].split(), ["B/Esc", "Back", "?",
"Help"])
self.assertNotIn("Install", [what for _key, what in install._help_lines(state)])
def test_back_after_choosing_systems_lands_on_install(self):
"""P3-8."""
state = checked_state()
state.focus["plan"] = [row.action for row in install.tui_rows(state)].index("systems")
press(state, "enter")
self.assertEqual(state.screen, "systems")
state.focus["systems"] = next(index for index, row in
enumerate(install.tui_rows(state))
if row.action == "system")
press(state, " ", "esc")
self.assertEqual(state.screen, "plan")
rows = install.tui_rows(state)
self.assertEqual(rows[state.focus["plan"]].action, "install")
def test_a_lone_card_opens_the_first_screen_with_a_list(self):
"""P3-7: with --manifest, the card's folder was the first screen."""
onion = self.root / "ONIONB"
make_card(onion, "Onion")
shutil.rmtree(onion / "BIOS")
args = _args(platform="retroarch", manifest=str(REPO_ROOT / "install" /
"retroarch.json"))
args.platform_from_list = True
with mock.patch.object(install, "_mounted_cards", return_value=[onion]), \
mock.patch.object(install, "detect_platforms", return_value=[]), \
mock.patch.object(install, "detect_frontends", return_value=[]), \
mock.patch.dict(install._DETECTED_OS, {}):
state, effects = install.tui_start(args, "linux")
self.assertEqual((state.screen, effects), ("setup", []))
def test_a_file_left_for_its_save_directory_is_said_so(self):
"""P2-4: the menu counted 23, 22 were checked, and the end blamed
the collection for the file whose save directory is unknown."""
menus = []
def menu(_plat, files, *_rest, **_named):
menus.append([entry["dest"] for entry in files])
return None
with SaveDirectoryRepository() as repo:
argv = ["install.py", "--platform", "retroarch", "--dest", str(repo.dest),
"--manifest", str(repo.root / "install" / "retroarch.json")]
with mock.patch.dict(os.environ, repo.env()), \
mock.patch.object(sys, "argv", argv), \
mock.patch.object(sys.stdin, "isatty", return_value=True), \
mock.patch.object(install, "_is_terminal", return_value=True), \
mock.patch.object(install, "detect_os", return_value="linux"), \
mock.patch.object(install, "records_selection", return_value=True), \
mock.patch.object(install, "_prompt_custom_selection", menu), \
mock.patch.object(install, "_ask", return_value="y"), \
contextlib.redirect_stdout(io.StringIO()) as out, \
contextlib.redirect_stderr(io.StringIO()):
try:
install.main()
except SystemExit:
pass
finally:
install.LOCAL.reset()
self.assertEqual(len(menus), 1)
self.assertNotIn("Azahar/Azahar/sysdata/keys.txt", menus[0])
text = out.getvalue()
self.assertIn("1 file read from RetroArch's save directory left out: its place is "
"not known. Name it with --save-dir next time.", text)
self.assertNotIn("the collection does not hold", text)
def test_plural_game_data_and_the_backup_line_at_40x12(self):
"""P3-9.5, P3-9.6."""
state = checked_state()
games = [{"dest": f"g{index}.dat", "system": "", "category": install.GAME_DATA}
for index in range(2)]
with mock.patch.object(install, "_unavailable", return_value=(games, 0)):
self.assertIn("Copy them from", " ".join(install._unavailable_lines(state)))
with mock.patch.object(install, "_unavailable", return_value=(games[:1], 0)):
self.assertIn("Copy it from", " ".join(install._unavailable_lines(state)))
view = " ".join(" ".join(install.tui_view(state, 40, 12)).split())
self.assertIn("1 wrong copy: backed up, replaced.", view)
if __name__ == "__main__":
unittest.main()
+7 -3
View File
@@ -11,7 +11,9 @@ profiles, placed below that folder.
from __future__ import annotations
import contextlib
import hashlib
import io
import json
import os
import shutil
@@ -406,9 +408,11 @@ class InstallerReadsTheCard(unittest.TestCase):
(self.root / ".system" / "version.txt").write_text("MinUI-20251127-0\n")
(self.root / "Bios").mkdir()
with self.assertRaises(SystemExit):
install._check_card_root("minui", self.root / "Bios")
install._check_card_root("minui", self.root)
with self.assertRaises(SystemExit), contextlib.redirect_stderr(io.StringIO()):
install.card_dest("minui", "minui", self.root / "Bios")
self.assertEqual(install.card_dest("minui", "minui", self.root),
("minui", self.root))
class TheSitePageOfACardPlatform(unittest.TestCase):
"""A platform that names no file showed "0/0 Coverage (0%)" and an empty
+2 -2
View File
@@ -81,8 +81,7 @@ It opens a menu:
- **Right folder?** Press Enter.
- **Wrong folder?** Choose **Another setup or folder**, then browse to yours.
- **A handheld's SD card?** Under **Another setup or folder**, choose its
system: **Onion, SD card**.
- **A handheld's SD card?** Type its name (Miyoo Mini).
Arrows move, Enter chooses, Esc goes back, as A and B on a handheld.
@@ -103,6 +102,7 @@ Esc or Ctrl+C stops, B on a handheld. Run again to resume.
- **Done**: all files are in place.
- **Finished with errors**: run the command again. It retries only those.
- **Finished with files missing**: run it again online.
- **Nothing installed**: the screen says why. See [If it stops](#if-it-stops).
For questions line by line (screen readers), add `--plain`.
+53 -31
View File
@@ -120,10 +120,16 @@ they do online, and a file of the list the pack does not hold fails as
`--offline` forbids every request, for a device with no Wi-Fi: `--check`
still names the platform and its folder, then stops at the file list unless
`--manifest` or `--from-pack` gives it. At a terminal the run installs the
copies found on this machine and downloads nothing. The files left out are
missing, not declined: it ends `Finished with files missing` (code 1), or
`Nothing installed` (code 2) when none was found, as the full screen does.
`--manifest` or `--from-pack` gives it, and counts the files that need the
internet instead of offering to fetch them. The run installs the copies found
on this machine and downloads nothing. The files left out are missing, not
failed: it ends `Finished with files missing` (code 1), or `Nothing installed`
(code 2) when nothing was installed, at a terminal, without questions and on
the full screen alike. The advice that follows names a pack or a run without
`--offline`, and no `--from` folder already read. A command it suggests keeps
`--offline` when `RETROBIOS_INSTALL_OFFLINE` set it, sudo not passing that on.
The `--plain` menu then offers `f) copy from a folder or an old card`, and its
Enter checks the BIOS folder when nothing can be copied.
## The full-screen interface { #the-full-screen-interface }
@@ -144,9 +150,10 @@ downloads, the folder and, for a list with files read from the frontend's save
directory, that directory. Each row names the option it sets, and **Show the
command line** gives the same choice as one command for a script. **Preview the
file list** shows what would be downloaded and writes nothing. **Copy the BIOS
files from your old card or a folder** opens the folder browser, mounted cards
first, and adds [`--from`](#copy-from-an-old-card): the plan then says how many
files that folder holds and how many are left to download. After an install
files from your old card or a folder** opens the folder browser on the first
mounted card, and adds [`--from`](#copy-from-an-old-card): the plan then says how
many files that folder holds and how many are left to download, or that it
holds none of them. After an install
on a muOS, KNULLI or ROCKNIX card, the result offers **Put RetroBIOS on this
card (update from the console later)**, and **Options** has the same row
([On a handheld](#on-a-handheld-the-retrobios-port)). Files the BIOS
@@ -165,7 +172,7 @@ install starts.
| Tick, untick | Space, Right, Left | X, D-pad Right and Left |
| Back | Esc, Backspace | B, Select |
| Options | `o`, Tab | Y |
| Filter a long list | `/` then type | the Filter row |
| Filter a long list | `/` then type, Enter on a lone match chooses it | none: a pad cannot type (the setup list puts the SD cards after RetroArch) |
| Keys of the screen | `?`, F1 | |
| Stop an install | Esc, `q`, Ctrl+C | B, Start+Select |
| Quit | `q`, Ctrl+C | B on the first screen |
@@ -838,8 +845,11 @@ gives for the device, below the card's root instead of its mount point:
The full screen lists such a card on its first screen ("muOS SD card (E:\)").
Named by its system (`--platform muos`, `--platform knulli`), a `--dest` that
is another system's card is refused. One that holds none of the system's
files is refused where the card always carries them, and is taken as named
for KNULLI, ROCKNIX and the systems no file on the card names.
files is refused where the card always carries them. For KNULLI, ROCKNIX and
the systems no file on the card names, it gets the system's folder below it
(`bios`, `roms/bios`), with a note. Named with a file list instead
(`--platform batocera --dest` an Onion card), another system's card takes the
files as named, and a note names the system and its folder.
Desktop installs are read from their own configuration, and several can be
found on one machine.
@@ -910,8 +920,7 @@ When a platform named with `--platform` is not found on the machine, the
destination falls back to the directory its detection looks for:
`/userdata/bios` for Batocera, `/recalbox/share/bios` for Recalbox,
`/storage/system` for Lakka, `~/retrodeck` for RetroDECK, `~/Emulation/bios`
for EmuDeck (`C:\Emulation\bios` on Windows), `/storage/roms` for AmberELEC and
EmuELEC, `/media/fat/games` for MiSTer,
for EmuDeck (`C:\Emulation\bios` on Windows), `/media/fat/games` for MiSTer,
`~/RetroPie/BIOS` for RetroPie, `/romm/library/bios` for RomM,
`C:\RetroBat\bios` for RetroBat, and RetroArch's own default system
directory for RetroArch. BizHawk has no such folder: it reads `Firmware`
@@ -919,29 +928,42 @@ beside its executable wherever the archive was extracted, so a forced
`--platform bizhawk` that finds no install stops and asks for `--dest`.
The systems that run from an SD card keep their files there: MinUI, NextUI,
MyMinUI, KNULLI, ROCKNIX, and each system of the
MyMinUI, KNULLI, ROCKNIX, AmberELEC and EmuELEC (the card's games part, which
they mount as `/storage/roms`), and each system of the
[Linux handhelds](../platforms/linux-handhelds.md) that gets RetroArch's list
(Onion, muOS, spruceOS, ArkOS, UnofficialOS and the others). Run on the
device, the installer finds its folder. From a computer, each has a row of its
own in the setup list (**Onion, SD card**), without the filter, and in the
`--plain` menu. That row, `--platform onion` and the menu all ask for the
card: one card of that system mounted is taken, otherwise the screen opens
**Where is the SD card?**, which lists only that system's cards and offers
**Look again**, and `--plain` asks `Card root`, listing the same cards by
number. The RetroArch row is this computer's RetroArch.
`--plain` menu, beside the devices that run it (Miyoo Mini, Miyoo Mini Plus).
The filter takes a device name, and so does the `--plain` menu, which lists
the setups it matches by number. With nothing found here, the SD card rows
come right after RetroArch: Onion is four presses away with L1 and R1 at
40x12, where it was row 30 of 34. That row, `--platform onion` and the menu
all ask for the card: one card of that system mounted is taken, otherwise
the screen opens **Where is the SD card?**, which lists only that system's
cards and offers **Look again**, and `--plain` asks `Card root`, listing the
same cards by number and naming the one picked and its folder. The RetroArch
row is this computer's RetroArch.
The card's top folder is checked the same way wherever it is chosen, listed,
browsed, typed or named with `--dest`, and gives the same folder. A folder
that does not exist, or that is another system's card, is refused for every
system. One without the system's own files is refused for the MinUI family,
Onion, muOS, spruceOS, CrossMix-OS, Allium and Koriki. KNULLI and ROCKNIX
fill their card when they first start, and no file on the card names ArkOS,
dArkOS, JELOS, KOLOS, UnofficialOS, ArchR, RetroOZ, plumOS, MiyooCFW or the
Anbernic firmware: such a folder is taken, and a note says so. No to
creating its folders asks for the card again. Under WSL a drive letter
typed (`D:`, `D:\`) is that Windows drive. Without questions (`--no-input`)
the run stops with a `--dest` example. A `--dest` that is a folder inside a
MinUI card (its `Bios/`) is refused with the root to name instead. On Android a forced `--platform retroarch` falls back to
The card's top folder is read by one function wherever it is chosen, listed,
browsed, typed, numbered or named with `--dest`, and gives the same folder and
the same note on the screen and in `--plain`. A folder that does not exist,
or that is another system's card, is refused for every system, and so is a
folder inside another system's card (ROCKNIX's `roms/bios` named for KNULLI).
A card whose own files name it is that card whatever its name: an Onion card
mounted as `BIOS` gets `BIOS/BIOS`. One without the system's own files is
refused for the MinUI family, Onion, muOS, spruceOS, CrossMix-OS, Allium and
Koriki. KNULLI and ROCKNIX fill their card when they first start, and no file
on the card names ArkOS, dArkOS, JELOS, KOLOS, UnofficialOS, ArchR, RetroOZ,
plumOS, MiyooCFW, AmberELEC, EmuELEC or the Anbernic firmware: such a folder
gets the system's folder below it, and a note says so, kept on **Create the
folder?** and the plan. No to creating its folders asks for the card again.
Under WSL a drive letter (`D:`, `D:\`) is that Windows drive, typed or given
to `--dest`, `--from`, `--from-pack`, `--save-dir` and `--cores-dir`, and the
screens write the folder `D:\BIOS`. Without questions (`--no-input`) the
run stops with a `--dest` example. A `--dest` that is a folder inside a MinUI
card (its `Bios/`) is refused with the root to name instead. A refused
`--dest` ends with code 1 and an `error` event (`stage` `detect`). On Android a forced `--platform retroarch` falls back to
`<shared storage>/RetroArch/system` instead, a home directory there belonging
to Termux rather than to the frontend. Pass `--dest` to say where instead of
relying on that. When detection met a folder this user cannot open, under the
@@ -1311,7 +1333,7 @@ fields, the installer refuses to narrow rather than install everything.
| Code | Meaning |
|------|---------|
| `0` | everything asked for is installed or already up to date |
| `1` | a failure: the one-liner could not download or verify `install.py`, or found no Python 3.8 (nothing was run), an unusable base URL, an unknown platform or target, `--installed-cores` with no core found, a manifest that fails validation, a fetch that never succeeded, no platform to install for in a non-interactive run, some files failed or were left out by `--offline` while others installed, `--check` found files to fetch, a `--from-pack` pack that cannot be read, lacks a part or is for another platform, a `--from` folder that does not exist or cannot be read, `--restore` could not put a file back, or `--add-app` could not put every file of the app on the card |
| `1` | a failure: the one-liner could not download or verify `install.py`, or found no Python 3.8 (nothing was run), an unusable base URL, an unknown platform or target, `--installed-cores` with no core found, a manifest that fails validation, a fetch that never succeeded, no platform to install for in a non-interactive run, some files failed or were left out by `--offline` while others installed, `--check` found files to fetch, a `--from-pack` pack that cannot be read, lacks a part or is for another platform, a `--from` folder that does not exist or cannot be read, a `--dest` that is not the card of the system named (another system's card, a folder inside one, or a drive with none of its files where its card always has them), `--restore` could not put a file back, or `--add-app` could not put every file of the app on the card |
| `2` | nothing was installed: every file the run tried failed (a BIOS folder that cannot be written, for one), or `--offline` left every file out, the drive has too little free space for the download (checked before any folder is created), or the question about a folder that does not exist, named with `--dest` or typed at the folder question, was answered no. Also an option the installer refused before any download: an unknown option, `--jobs` outside 1 to 32, `--target` with `--every-board` or `--from-pack`, `--target` or `--list-targets` with `--offline` (or `RETROBIOS_INSTALL_OFFLINE` set), which read a core list from the network, `--add-app` with `--check`, `--dry-run`, a listing, `--from-pack` or `--offline`. The message then starts with `install.py: error:`. Also `--add-app` for a folder on no card that takes the app, said in an `Error:` line and an `app` event before anything is written. With `--restore`, a folder that is not a dated folder inside `retrobios-backup` |
| `130` | stopped with Ctrl+C, or with `q`, Ctrl+C or Ctrl+D at a question, or the full screen closed with no button pressed (`RETROBIOS_IDLE_EXIT`) before an install. Run the same command again to carry on |
| `141` | the output was piped to a reader that closed early (`--list-platforms \| head`) |