An entry the installer places in the roms, emulators or configs folder is in the install list but not in the ZIP, which is unzipped into the BIOS folder. pack_files and pack_size counted them: release_record.py refused Batocera (19196 against 19125 in the archive) and RetroBat (9200 against 8882). Manifests regenerated twice, identical.
A release asset absent from the checkout is omitted from the install
list. That omission named only the first core reading it: harm1.wad,
action2.wad and delaweare.wad named gzdoom, so install.py --target
rpi1, where lzdoom runs, kept nothing the builder's list for rpi1
omits. The omission now names the cores of the claims folded onto the
entry too.
The first profile reading a requirement hosts the others' claims,
and two ways of dropping the host dropped them too. DraStic brings its
own usrcheat.dat, and DSPerate, which reads the same file, lost it
wherever DraStic runs. A missing file was recorded for its first
reader alone: NAPALM.GRP named Raze, Data.rsdk Sonic Mania. A list
built for a board without that reader carried what the full list
left out, and install.py --target disagreed with the builder on
ROCKNIX, KNULLI, Batocera and RetroPie boards.
A board's core list names Batocera's standalone amiberry and the
buildbot's amiberry core alike, so install.py --target kept the
amiberry 8.5.0 libretro files on bcm2711, whose RetroArch downloads
nothing. resolve_platform_cores decides that reach, and the install
list now carries its answer as downloaded_cores, read by the
installer's narrowing for a target.
The builder tested only the first installable name of a profile:
Batocera lists higan_sfc_balanced and nside_sfc_balanced, the x86_64
downloader offers the second, and the core left the x86_64 pack with
its Super GameBoy files.
RetroPie builds amiberry 5.7.2 and 6.3.3, one per board, both in
$md_inst of its amiberry package. Each build recorded roms/kick.rom
and whdboot/AmiQuit in the emulator's folder apart, and
install/retropie.json listed both destinations twice. A file read
outside the BIOS tree is now keyed by the folder the platform installs
the listed name into, and one omission names every core reading it.
platforms/romfolders/<platform>.yml records the roms root from the OS's
own source (roms_root path and from_bios), the same fact the registry's
roms_directory restated for ROCKNIX alone. The manifest's roms_directory
now carries from_bios, which the installer follows from a BIOS folder of
the platform's name, and the registry key is gone. A list that installs
above its BIOS folder (AmberELEC) holds its roms root in the install
tree: a file a launcher runs from there is a plain dest. emulators_directory
stays in the registry: no rom folders file names emulator folders.
RetroBat's Pico8 generator runs pico8.exe from AppConfig.GetFullPath:
the emulatorLauncher.cfg key, else the first existing of four folders of
the RetroBat install. The registry describes that search
(emulators_directory) and the manifest writes such a file as
root: emulators. The installer places it only when the BIOS folder sits
beside RetroBat's launcher config and the config does not move the
folder, and names it no_emulators_directory otherwise. Packs skip it as
they skip root: roms.
The PICO-8 0.2.7 Windows build (pico8.exe, SDL2.dll) is collected from
the archive.org copy of pico-8_0.2.7_windows.zip, its pico8.dat being
the 0.2.7 one already held.
Kronos reads kronos/stvbios.zip, else stvbios.zip at the root of the
system directory, and a ST-V game stops without either
(libretro.c:1608-1617, 1676-1681 at the pin). The archive under
archive_prefix and the root copy are one need, but only loose entries
could be members, so the required-only pack carried both.
An archive's own entry now joins a set like a loose file. verify
--emulator and the emulator pack settle archive units with the loose
entries, the required-only pick covers them, find_undeclared_files
and cross_reference record them, and the unit under archive_prefix
ranks before the same name at the root. Its members still settle the
archive by their contents. Kronos' stvbios.zip is grouped stv-bios.
No other profile reads one archive under two paths: odin and
tgpulse_next_m1 place their own entries at the prefix path, and
flycast's naomi.zip has one path per build.
A platform that binds part of a standalone layout leaves the entries no
binding covers in a folder outside the BIOS tree: they are no longer
packed at the BIOS root, and the manifest names them (root: emulator,
outside_bios_folder). A launcher that executes a profile's file binds
the program folder: in the BIOS tree, below the roms directory a registry
entry names (root: roms, installed into the folder holding the BIOS
folder), or nowhere a pack reaches. A moved entry resolves by its own
path as well as by its destination.
uae4all gains kick.rom and rom.key in its working directory and loses a
not_bundled_on that named Recalbox, which runs uae4all-rpi. lexaloffle
declares that its programs read beside their executable, lists the
Windows build RetroBat runs and the pico8_dyn ROCKNIX runs on x86_64.
Three changes to the search-list model, one commit because the readers
and the profiles must move together for the schema to hold.
Set requirement in a groups map. pom2 falls back on apple2.rom for the
][ and the ][+, and on apple2e.rom for the //e it boots and for the
unenhanced //e: one required on the file could not say both. A profile
now declares groups: {name: {required}}, a member writes no required
of its own (schema and validate_schemas refuse one) and load_emulator_
profiles derives it, true when one of its sets is. group takes a name,
a list, or a map from set to rank where the rank differs per set. pom2
is grouped from SystemProfile.cpp and MainWindow.cpp:246-296 at its pin.
One member per required set under --required-only. The pick is the
first the code reads among the held members the region filter leaves,
by rank then declaration order; a set the platform declares a member
of is carried by that declaration. The emulator pack, verify
--emulator, the platform pack, its manifest and verify --platform read
alternatives.required_only_spares or core_file_spares.
variant_group is now selection_slot: files a core chooses between by
a rule other than first found (disc territory for PicoDrive and
Pizza Boy SC, the o2em_bios option, a WebAssembly probe in SWF
Player). Where the code takes any one, the field became a group:
ARMSX1 (ArmSxBackend.java:149-157, name order), ePSXe (load_bios
0x433540-0x433621 of the 2.0.5 Linux x64 binary, one configured path,
HLE when BiosHLE is set), FPse and FPse-NG, DXX-Rebirth's HOGs
(inferno.cpp:472-512). odin and zesarux carried it with no effect.
slot.py compares ranks only between candidates that share a set.
A core that loads the first of several names (yabause: saturn_bios.bin,
sega_101.bin, mpr-17933.bin) needs one of them. Profiles wrote this as
required on the first name and optional on the others, so verify and
the gap report called saturn_bios.bin a missing required file while
sega_101.bin was held, and a set held only through a name marked
optional read as a need met by an optional file.
Model: an explicit group on the file entry, mirroring the platform
field. priority was not reused: it is a rank, read by slot.py, region.py
and truth.py inside a (system, region) slot, and that slot is not the
set. px68k-sdl ranks its IPL list and its font list each from 1 in one
system, pom2 ranks one list per machine model, DuckStation ranks 105
images across every region and takes any of them. A rank cannot say
where one list ends. priority stays the order inside a group.
Readers, through alternatives.settle_sets:
- verify --emulator and the emulator pack: a held member fills the
set, the others leave the report, a set nobody holds is one gap
under its first member naming the others.
- find_undeclared_files (verify platform, core complement of packs and
manifests): same rule, a platform declaration or launcher counts as
answering the set.
- cross_reference: excused members are set aside as other versions.
- slot.py: ranks compare members of one set only.
- archive members: a required set inside an archive is met by one.
- truth: a member gives no requirement of its own, one_of and
one_of_required carry the set, and the exporters and the diff leave
such a line's required as the platform wrote it.
- validate_schemas: members of a group share required.
Choosing GBA alone brought FBNeo's cheats and samples, 3,472 files,
because both trees listed every FBNeo system. The profile now names the
systems each serves, from the code: a driver's <name>.ini, cheat.dat,
cheatnes.dat or cheatsnes.dat (conc.cpp:815-828, 1059-1083), the md_,
pce_, tg_ and sgx_ drivers the tree holds, and samples read by arcade
drivers alone (samples.cpp:886-899).
The list now gives a tree the platform's ids its systems normalize to,
as a core extra's: the cheats said sega-megadrive where RetroArch says
sega-mega-drive, and a tree no platform system serves goes with the
files of none, as the pack puts it. The installer keeps a tree for the
systems chosen by name, not for every system a shared file names: the
arcade choice took blueMSX's machines through msx.zip.
--installed-cores treated a core with no profile as one that reads no
file, and dropped dc/dc_boot.bin for parallel_dc without a word (issue 87).
Such a core is now named, and the firmware its RetroArch .info declares is
kept. core_names lists every core a platform runs, so a profile with no
file is not reported unknown.
The RetroArch pack lands in RetroArch Android's system folder, where MediaProvider indexed 3 842 images, sounds and videos (552 MB) into the gallery and music apps: a folder holding .nomedia is hidden with every folder below it (FileUtils.java:1587-1629, ModernMediaScanner.java:845-846, a183e2b9). A pack whose platform files an Android target, unfiltered or built for one, carries an empty .nomedia. pack_files counts it and the integrity check takes it for a marker.
The manifest names the cores that read a tree no platform system
declares, every reader of a shared tree included. An install for the
games of a card keeps such a tree only when its plan runs one of them:
a SNES game on ArkOS (snes9x) no longer takes FBNeo's cheats and samples.
A system no setup names a core for keeps every core's tree.
The name step reused one variable for paths and for (path, md5, size)
tuples, the standalone copies reused targets inside its own loop, and
the WSL state read as object. Ten baseline entries no longer occur.
--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.
_pack_version took the text after the last underscore of the pack name.
AmberELEC's prerelease-20261019_1835 read as 1835 and the page would
announce a version gap that does not exist. The site now removes the
platform part the builder writes (platform_pack_prefix).
--emulator handy --required-only removed the empty ZIP, printed the
path it had just removed and exited 1, while verify reported 0/0 OK and
exited 0. The builder now says no file is needed and succeeds.
MiSTer and BizHawk declare cores: []. --region, --one-per-slot and
--no-game-data read the profiles of the cores a platform runs, so their
packs and manifests withdrew nothing under a name and README that said
they had. generate_pack and verify now refuse the flags for a named
platform and leave it out under --all.
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.
_ships and _emulator_ships resolved without the data directories, the
archive index and the slot override the builder uses: 400 platform
declarations and 178 emulator entries read as missing while the pack
held them.
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.