Every launcher exports RETROBIOS_PAD: gptokeyb, muxterm, evmapy or
miyoo, so the interface can name the buttons that reach it. The wiki
lists the values, and the tests check each launcher's.
Apps such as AetherSX2 named their user data folder only in prose, so
the site could neither print it nor show only the OS tabs that apply.
data_root keys android, ios, windows, macos and linux, each with the
path the user finds and the source_ref that builds it.
The speed is bytes_network over time, the interface's own guess at local
bytes is gone. The plan says when a --from folder was read only in part
or holds another drive, and how many files on the old card could not be
read, with a list of each reason. The --from folders are only checked
before the screen opens, never indexed there.
Release packs now exist for six platforms only, and install.py refuses a
pack named for another. build_port.py writes packs.txt from the
registry's release_pack groups. Offline, the launcher takes only a pack
spelling the detected system, names that pack, or says no release pack
serves it and gives Wi-Fi or the computer route.
The one-liner opens the full screen, and the page walked through the
line questions. It now shows the first screen, the keys and the
controller, Install, the copy from an old card and the result titles.
Troubleshooting no longer asks to move a nested bios folder by hand.
- A handheld card mounted on a computer is found by the files its
firmware writes there: muOS, Onion, spruceOS, CrossMix, Allium and
Koriki from their device rules read below the card root, KNULLI and
ROCKNIX from the share their first boot seeds. It is a setup of its
own ("muOS SD card (E:\)") with the folder that system reads, and
--dest <card root> writes there too. --platform muos with a folder
that is no muOS card is refused with the folder to name.
- Space (X on PortMaster, Y on muxterm) opens the confirm screen from
Install and never installs, closes or quits by itself.
- Short of space, Choose another folder comes first, and the result
says the card is full in words, with no log lines and no command
naming the interface's own copy of the list.
- The plan keeps the BIOS folder second, cut in its middle, a --from
folder by its name, and the config that named the folder.
- The result says how many files came from the old card, how many
were downloaded and how many are still missing, as the line mode.
- The "other" row is named and explained, files shared by several
systems are counted apart, and a file that cannot be supplied is
named with its system, only where it matters.
- A work folder left by SIGKILL is removed by the next run, a failed
run's log after a week. A full card keeps no log.
- Smaller: the sudo advice names the folder, removing a --from folder
says so, the preview counts its files only, a proxy is named, a list
that is no JSON and the clock error read as plain sentences.
The engine counts the bytes a copy, the pack or a resumed partial copy
supplied, and those the network brought. A transfer event carries both
after each progress event and once when the downloads end, so a screen
shows a download speed without guessing.
Start + Select now kills a sentinel the launcher owns, and the launcher
asks the interface to stop, so a running install still writes its result.
A mapper that dies, a chvt the compositor refuses, a terminal app that
cannot open and a read-only card each end the run or hand over to the
next route instead of a console waiting for a button. muOS starts
gptokeyb2 with -X and keeps muxterm off the pad so Y opens the options.
The walk no longer enters another drive mounted below the folder (it is
named instead), /proc, /sys, /dev or a Windows junction, and the root of
the system is refused. from_folder_problem() checks a folder without
reading it. A scanned event reports each folder read, cut short or not.
A file, ZIP member or folder below a --from folder that could not be
read was taken as no match. When its size is that of a file the run
still needs, or unknown, --check, --dry-run and an install now name it
with the reason, and an unreadable event carries it.
A copy that could not be read when it was copied was reported as a
folder not writable. The source is now opened first and its errors are
its own, and a download failing after it names both reasons. A run
reading a pack falls back to the pack, never to the network.
A missing file walked every archive of the BIOS folder and every file of
its size: 3,000 ZIPs and 3,000 missing files took 186 s, now under 1 s.
--from copies and the owner of a refused file use the same lookups.
upstream_frozen says why upstream_commit stays: the port vendors that
upstream tree, synced to that release, or merged that commit. The view
is then read at its own pin, the way pin_frozen holds source_commit, and
a bump leaves it. Outside a GitHub fork network nothing else could tell
aps3e's vendored RPCS3 1f080485 from an upstream read on a date.
--check-version said pcsx2x6's v0.2.22 was older than its code because
the pin holds release v0.2.20, the latest the project published. A
release that is an ancestor of the declared tag is not a later one.
A folded run rewritten whole keeps its separators (boom3, mame), a
double-quoted source_ref is matched as the loader reads it and escaped on
write (etlegacy), two files sharing a name take their build mode in the
field path (supermodel Games.xml), and a project word ending the line
before a path marks the citation external (genesis_plus_gx_expanded).
The build workflow downloads it from SourMesen/librashader's nightly
release on every build, so no shipped copy has a fixed hash: the entry
joins the recorded unidentified bundled files.
get/<platform>/ lists the files of the current release pack, read from
release.json, or gives the command for a setup without one. The README,
setup pages, Get your files and the release notes link it instead of
asset URLs, which die with each deleted tag. Setups without a ZIP say so
with the command beside it, and validate_site checks every get/ page.
release_pack: true in the registry is the list a release builds: RetroArch,
EmuDeck, RetroBat, Batocera, RomM and MiSTer, 83% of the downloads of
v2026.10.10. generate_pack --all, --verify-packs, the pipeline consistency
step, release_record and check_release read it. Manifests are still written
for every platform.
The plan offers the folder browser, mounted cards first and ZIPs listed,
to add --from (Options adds a second one, Left takes it back), checks the
folder again and says how many files it holds and how many are left to
download. Misplaced files are counted with a Show list of their reasons,
and a local copy no longer counts in the download speed. The found event
test now holds that the interface reads it after a check and an install.
Without a network, muOS and ArkOS launchers ask to copy the BIOS files from the card the system does not read (muOS bind.sh, dArkOS roms2), then install with --from and the pack. CI checks that shellcheck is present, and the test fails under CI without it.
The interface accepted --from and its check and install runs never got it, so they downloaded what the folder holds. Pins in install.sh and install.ps1 follow install.py.
mame_hash_scraper --release rewrote the whole profile and dropped the
pin_frozen that mame0258 and mame0278 carry: libretro/mame HEAD is a
later release, so without it profile_sync compares their refs with code
the Recalbox build never had. A release profile now states it, from
built_by, and keeps the reason a reader wrote.
realign_prose stripped a path prefix, or read a path two repositories
share, in the repository whose pin moved, while the profile meant the
other. hatarib's hatari/src/tos.c became hatari/hatari's src/tos.c, and
freej2me_plus's fork citations were read in hex007/freej2me: 32 and 8
citations asked to be read again although their files were unchanged.
The path as written now decides first, and a shared path belongs to the
repository declared first, as in attribution.
test_a_frozen_build_files_its_content_with_the_build_that_follows paired
every pin_frozen profile with every live one of the same source. It now
skips platform builds (built_by), and requires the frozen systems to be
a subset: a later release can add a board (advancemame's coh1002v).
The PortMaster port now starts install.py --tui on the console (or in
foot or xterm under a compositor), with gptokeyb in kill mode on the
installer's path, retrobios.gptk, TERM=linux and a Terminus font, and
puts the console, font and mapper back on exit. pugwash screens stay as
the fallback where no terminal can show, and start in no-check mode so
PortMaster's A/B fix applies. Without a network, every launcher installs
from a pack found on the card or names the pack and the folder.
Adds launchers for Batocera and Knulli (Ports, evmapy .keys), muOS
(.muxapp in muxterm with gptokeyb2) and the Tools menu of ArkOS,
dArkOS, ROCKNIX, JELOS and AmberELEC, built by build_port.py with the
pinned installer and tested on a simulated device.
For a port that is a GitHub fork of its upstream, the upstream code it
carries is the merge base of the two, not upstream's HEAD. The bump moved
upstream_commit to upstream HEAD on snes9x, kronos, np2kai and others
whose pin was that base. The upstream view's head is now the merge base
GitHub's compare names, the way _follow_submodule already reads a
vendored submodule. Outside a fork network the HEAD stays.
A sentence written in two fields was located over the whole file, found
twice and left in both: the writer now searches the lines of its own
scalar. A structured ref was judged pending when its old text appeared
anywhere, so a chain of shifts (tr1x cut2 508 to 531, cut3 531 to 552)
held the pin on the values the previous entries took: it is now looked
up at its own field.
The recale skipped runs in YAML comments and runs wrapped over two
lines of a literal block, while the pin check counted them: the run
wrote nothing and said nothing. Both are now rewritten at their located
position. A citation that still cannot move is named on stderr and the
profile is left untouched.
A stop by SIGTERM (Start+Select under gptokeyb) reads the summary like
Ctrl-C and prints the count on disk. The systems filter keeps the ticks
and the focus. The bar names what A does on the focused row. Options
check the folder again, a missing network fails in 15 s with plain words,
an unwritable folder offers another one, a folder inside a card offers
its root, and only a failure keeps its log.
New event kind: checking, with files_done and files_total, while the
files in place are hashed. The exact event lists of the check and
install runs now carry two of them, for the two files in place.
--from FOLDER (repeatable, a ZIP accepted) reads an old SD card, a
backup or a Downloads folder: files indexed by size, hashed only when
a size matches an entry, ZIP members included. Each file is copied,
checked again once written, and the source is never modified. Mac ._
files, system folders and links leading out of the folder are skipped.
An install also puts in place the files it finds misplaced in the BIOS
folder (bios/bios/, a pack extracted one level too deep, a name in
capitals, a ZIP never extracted, one folder up), leaving the user's
copy where it is. --check and --dry-run say what a run would copy.
Events gain one kind, found, sent before the copy, which a downloaded
event then reports. The end of the run says where each file came from.
Each manifest carries system_titles, the name the site prints for every system its files list (Sony PlayStation, not sony-playstation or psx), so the installer can show it. Older installers ignore the field.
The prose guard read citations under the pins a profile held before the
write. A bump that moves upstream_commit alone could then leave prose the
report had judged in the fork under the new upstream pin: ten profiles
(dolphin-mmjr2, hatari, xenios among them) came out of a bump with prose
to read again. The written file is now read again and the original bytes
put back when a citation is left under an older pin.
A pin_frozen reason was printed as a tag name ("pinned to tag EmuELEC,
the only platform..."); it now reads "pin held (...)".
check_version read latest_tag as the first entry of GitHub's REST
listing, which sorts by name (nesoid: 2.5 before 2.5-4, gargoyle:
stable-20090825 before 2026.1.1), and found the declared tag only on that
first page of 100. The newest tag now comes from GraphQL by commit date,
and the declared one is asked for by name on GitHub, GitLab and Forgejo.
The clean-tree guard of the write modes looked at the whole emulators
directory: a loop of --emulator X --rebase-refs refused every profile
after the first it wrote. It now looks at the selected profiles only.