--for-games --required-only on a dArkOS card from a computer kept every
file of the list: --dest skipped the card's detection, so its rom
folders were never found. The games part beside the BIOS folder now
names them. The manifest marks a data tree every core runs without
(optional), and --required-only leaves it out: one GBA and one SNES game
took FBNeo's cheats and samples, 3,472 files no game opened.
A mistyped --system said '--list-systems lists all 579' and the listing
prints 532: the hint counted every spelling across profiles, and
system_index merges the ones that differ in case or separator before
printing. Both verify.py and generate_pack.py now pass that count to
suggest_names, whose listed parameter exists for this case.
Six helpers that write a pack member declared zipfile.ZipFile while every
call site also passes the PackRecord the pipeline builds emulator and
narrowed packs with. mypy reported the 19 call sites as arg-type errors.
With the union in the signature a helper that calls writestr or write
without its isinstance branch is a union-attr error in CI, instead of an
AttributeError in the pipeline's record pass.
RetroPie was the only platform with status: archived, which kept it out
of verify --all, slots, the pipeline and list_platforms unless
--include-archived was typed. release_pack already decides which packs a
release publishes and the installer serves every platform, so the status
set nothing apart. The field, its schema entry and the flag are gone, and
RetroPie is covered by every full run like any other platform.
generate_manifest called slot_choices, and pack_overrides called it again with the same five arguments. pack_overrides takes the choices when the caller holds them. Measured on generate_pack.py --all --manifest --offline: 48 calls and 7.0 s in slot_choices before, 24 calls and 4.3 s after (wall 45.7 s and 42.4 s, one run each on a loaded host). The manifests written are identical.
A pack folded for Windows and macOS keeps one spelling of each case twin. Extracted on a Steam Deck or Linux, the core asking for the other spelling finds nothing, and only the installer added it. Where the platform also runs on a filesystem that keeps case, the README now lists each spelling left out beside the one it holds: 12 for EmuDeck and 10 for RetroArch, the manifest's case_variants.
On a platform that writes bios/ in front of its destinations (RetroDECK, AmberELEC, EmuELEC), a core extra was grouped, dropped and held under its bare destination while the platform's declaration of the same file used bios/<name>. Beetle PSX's scph5501.bin was kept by the --region japan pack while the declaration was dropped and the installer dropped the path. extra_region_key keys every extra with the platform's declared root, in the builder, the manifest selection, verify's replay and the omissions. The same key gives the declared entries the systems of the core files at their path: every --system case of RetroDECK differed between pack and installer before, none does now.
A tarball tree was announced at its extracted size: yaps2 at 6.5 MB for a 21.2 MB download, Cemu's game profiles at 9.9 KB for 21.4 MB. refresh_data_dirs records the size of each archive it downloads (a tree current from before is measured, not refetched), the install lists carry it as archive_size, and the plan, the menu and the data event count it.
Each part was built alone: the Other_Cores part never saw the platform's destinations and each part had its own case fold. RetroDECK's parts wrote bios/SGB1.sfc/ members beside the file bios/SGB1.sfc, RetroBat's wrote 21 openmsx/ twins of openMSX/ files, and a path two systems declare got each system's file. The full pack is now built in memory first and a part writes a declared file or core extra only where the full pack writes the same bytes.
A path declared twice, once unpinned and once with a pin the copy misses: the builder wrote excluded over the file it shipped, in either order of the two systems. It now counts the path untested, as verify does, and lists no exclusion.
Batocera and KNULLI write the image name, not the board, to the card's board file (post-image-script.sh). The target scraper now records each board's images from Config.in, the target lists carry them, and the installer maps a card's image to its board. KNULLI's boot/knulli.board is read too.
The README, the setup pages and adding-a-platform tied archived to a
scraping schedule and a CI matrix that do not exist. Archived keeps a
platform out of the runs that cover every platform unless
--include-archived is passed.
Step 5 builds the --required-only, --no-game-data, --region, --target
and --system packs of every platform in memory and compares their paths
with verify run under the same narrowing. verify_platform gains
required_only, generate_pack a record mode. A run samples three regions,
targets and systems per platform (155 packs, about 3 minutes);
--full-narrowings compares all 1,150 (25 minutes). A test fails when
_narrowings() gains a dimension that is neither compared nor exempted.
A platform pack carried only the trees its own systems declared, so for_platforms reached RetroArch, Lakka and RetroPie alone and every other platform running blueMSX, FBNeo or Dolphin got no tree. The pack, the install list and the pipeline's tree members now add the trees of the libretro cores the platform runs, below the system directory and build folder the core reads, unless the platform installs or never opens them. A platform entry the manifest resolved into a carried tree becomes that tree's member, which the installer can fetch: AmberELEC and EmuELEC no longer hold 192 blueMSX machine configs back. stonesoup reads its tree beside the content and pcee2 compiles its resources in, so neither tree reaches a BIOS pack.
The pack suffix test of b20d65d12 missed the platform --system packs, which carry their system after it (_BIOS_Pack_Playstation.zip): their manifest was no longer injected and install.py refused them. A pack name holds _BIOS_Pack, then an optional tag, then .zip.
verify_and_finalize_packs took every ZIP of the directory, so the launcher archives build_port.py writes to the same dist/ were judged as packs, failed, and were rewritten with a manifest after build_port had printed their hashes. Every name the builder gives ends in _BIOS_Pack.zip, and only those are taken.
Raze lists three sized BLOOD.RFF versions and a fourth it takes without a check, the shareware RFF the collection holds. The Batocera pack printed two DISCREPANCY lines for it that verify never showed. A slot with an unchecked sibling now accepts any copy for that core, in the validation rules and in _matches_an_alternative. When every version refuses a copy, the message names all accepted values instead of the first.
Messages in check_freshness, generate_pack, common, profile_sync and fileless_audit named commands as export_native.py --platform x, which fails from the repository root. They now read python scripts/<name>.py like the rest of the report.
install.py took --list-platforms, generate_pack.py --list and verify.py
nothing. Both tools now take --list-platforms and print the same list
from common.py. generate_pack.py keeps --list (finding 80).
--all --system built every pack asked for and exited 1 on a platform
that has no such system. The scope is now decided before the lock:
skipped under --all, refused under --platform (finding 15).
A system the target leaves no core for was verified 0/0 with exit 0,
and generate_pack blamed the platform for lacking it. Both read
platform_scope_systems and system_scope_error now (findings 13, 14).
install/<platform>.json wrote the constant "1.0" as the version of the
list it was built from. The manifest now carries the version the platform
YAML records, and generate_pack refuses a YAML without one.
inherits: no longer passes a parent's version to a child: Lakka was
published as RetroArch v1.22.2 in its manifest, pack name and site page.
lakka.yml records v6.1, the Lakka-LibreELEC tag its target list is read
at. The MiSTer scraper refuses a BiosDB without a timestamp instead of
writing an empty version.
A pack's manifest.json records list_version, so an install from the pack
alone knows it too. Manifests regenerated twice and compared: only
version and generated changed.
The MinUI family's lists record cores and regions and no system: every
narrowing was refused with a remedy that fetched the same list. Each
option is now refused by the axis missing, with what works, and a pack
names its platform's list only when that list records the axis. Line
mode names what c chooses and skips a question with one answer, and
Options leaves out parallel downloads when nothing is downloaded.
Answers code_audit_17 and tui_audit_10: the folder found after o, SHARE
drive roots, one same-choice command, a counted confirm step in line
mode, counts from the narrowed list, read-only advice without sudo,
systems recorded in packs, Ctrl-Z stopping the whole job, and the
wording, layout and docs items.
_MISSING_DATA_DIRS is module state: a test that called main() after one
whose data directories were not cached exited 1 with the earlier run's
notice. main() now clears it first.
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.
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.
batocera-systems lists bios/saves/dolphin-emu/Triforce/segaboot.gcm, so the pack folder for the save directory is userdata-saves/. validate_schemas refuses a pack folder the BIOS tree already uses.
Batocera, KNULLI, Recalbox, RetroBat and RetroDECK hand a libretro core <saves>/<system> at each launch. Each copy lands below every system that runs the core. EmuDeck keeps one unsorted saves/retroarch/saves. The unsupported platforms record why.
load_from: save_dir entries of a libretro build were left out of every
pack. They now go below the platform's save folder (registry
save_directory: RetroArch saves/, Lakka savefiles/, by core), the
manifest marks them root: save, and verify replays the same placement.
Platforms keeping saves per system or per game list them as omitted.
The install manifest marks each game data file "category": "game_data":
a platform entry only game data profile entries read, or a core file its
profile declares game data. Firmware carries no category. Every manifest
says "categorized": true so the installer can refuse an older list.
generate_pack.py --no-game-data cuts those files from the platform list
and the core profiles before the build, names each one with the cores that
read it as game data, and tags the pack _NoGameData through _narrowings.
verify.py and the pack check take the same cut, so --verify-packs and the
report describe the pack the builder wrote. Modes without a platform
(--emulator, --system alone, --from-md5) and --all-variants refuse it.