validation: [romdir] walks a PS2 ROM directory the way the profile's
fork reads it, masked_sha1 hashes a file with a byte range cleared, and
size_multiple bounds a size to a granule. The resolver name steps, the
pack and manifest scans, the emulator expansion and verify call the same
content_fits. A PCSX2 companion is taken only beside an image that
passes, and a companion placeholder no longer seeds a scan of its own.
KNULLI sets AetherSX2's [Folders] Bios to bios/ps2
(aethersx2Generator.py:219), yet its 112 PS2 BIOS files went to the
BIOS root. Three causes:
- a record's passes_to was split into words, so "[Folders] Bios"
could not be told from "[Filenames] BIOS". ini_keys() adds the
section-qualified key a profile names in standalone_directories.
- scans_dir records hand a folder too (ROCKNIX's bios/armsx2,
EmuDeck's PCSX2 and ARMSX2) and were not read as handed.
- the agnostic scan looked its seeds up by the id of the profile's
entries, while it walks the copies platform_layouts makes: any
bound layout scanned nothing. Copies map back to the profile's
entries by position.
An agnostic profile's entry the layout reads outside the BIOS tree
is now named by the gap report (outside_bios_folder) instead of
dropped: ARMSX2's arcade/proverb.zip on ROCKNIX and EmuDeck.
RetroPie lists KS 1.3 as kick34005.A500, and its fs-uae launcher
passes $biosdir/kick13.rom. The listed copy settles fs-uae's entry by
content, so the launcher layer never offered it, and kick13.rom
reached the pack only as a copy of UAE4All2's amiga/kick13.rom: the
x86 boards, without UAE4All2, lost it. An entry the list settles under
another name now answers the launcher's place by its own name.
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.
Batocera and KNULLI start shadPS4 from CONFIGS/shadps4, so its user
folder sits below /userdata/system/configs. The scrapers read that root
from batoceraPaths and write configs_root with its source_ref. The
manifest names it configs_directory, and install.py honours it through
the same checks as roms_directory: character rules, at most two ups,
the ancestor match and the lexical bound.
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.
ROM_5200_A, ROM_BASIC_A and six more are atari800.cfg keys, not names
atari800 opens: MatchByName knows only atariosa.rom and its kin
(sysrom.c:278-294), and the keys are config_key since 969907e29. The
twelve labels the scan identifies by CRC32 are found_by: content, and a
platform declaration of a file such an entry accepts by hash, in the
folder the core scans, now meets it: RetroDECK's ROM_BASIC_A answers
atari-basic-rev-a.rom without the alias.
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 launcher name that a reader's profile entry declares unsourceable is
no file left to find. Its row carries the reason and both reports
print it under its own count, never mixed with the names to acquire.
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.
emulatorLauncher 228731e0 lists ten PS2 BIOS in one group: one held file
is enough and Program.cs reports the others OPTIONAL. The scraper keeps
group and optional, verify and the pack check apply the same rule.
With no required firmware among the bundled entries the line read
'20 (20 more, --verbose lists them)'. It now reads '20 (--verbose lists
them)', and reading-reports.md quotes the run as it prints.
Only verify.py and generate_pack.py moved to the repository root. From
any other folder slots, region_audit, provenance_report and bundled_only
said database.json was missing and to rebuild it, identity and
list_platforms named a missing folder, and game_rules ended on a
FileNotFoundError traceback. work_from_repo_root moves to common.py and
the eight tools call it: defaults are read from the root, a path the
user typed from where it was typed.
Without --verbose each missing core file was followed by a bare '[size]'
line that told the reader nothing. It now reads 'validates size', the
verbose wording without the expected values.
'Bundled with their emulator, not collected: 154' listed the 14 required
firmware names and nothing said the rest existed. The line now ends with
'139 more, --verbose lists them', as the Core files line does.
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.
The closing summary of verify --all grouped platform to names: sfiii2a.zip,
missing on 15 platforms, was printed 15 times on a 1 060-character line and
counted 24 files for 10 distinct ones. It now groups by file and names the
platforms by the shorter side (every platform but BizHawk, MinUI, MiSTer
FPGA).
_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.
reading-reports quotes a RetroDECK run, which now prints the bundled count and 30 missing game data instead of 38. The comment introducing the bundled names also loses its semicolon.
verify counted game data installed with its emulator as missing, which cross_reference files as bundled: 24 of Batocera's 427. Bundled firmware already left the lists, and the line naming it never printed. Every bundled entry is now counted on that line, required firmware named and the rest under --verbose.
The previous commit counted a name with no resolved copy as not held, which also dropped release assets the builder holds while no copy is on disk (test_install_selection AHeldFileTheInstallerCannotFetch). Only the case-insensitive name match now leaves out copies in another emulator's tree, as resolve_local_file does.
dosbox-x's et4000.bin matched only pcem's et4000.BIN. resolve_local_file, asked as dosbox-x, refuses another emulator's tree in its case-insensitive step, so the pack and installer leave it out, while the gap report and verify's core files resolved without the asker and counted it held. Both now resolve as the profile, and the manifests of Batocera and KNULLI no longer flag it held.
The --destinations branch of _main rebound held, the ExitStack that
holds the data directory locks, to the dict it writes. It is now
named destinations, and a test refuses a rebinding of the stack.
A platform entry that resolves only through a data directory tree read as missing with no cause on a checkout where data/ was never refreshed, while an online pack build refreshes the trees and packs it. verify_platform now reports the uncached trees and prints them when files are missing.
generate_emulator_pack resolves with offline=offline and fetches a release asset the cache lacks, while verify_emulator resolved offline and called it missing. verify --emulator now applies pending_release_asset as verify --platform does, accepts --offline, and the pipeline compares emulator packs offline on both sides.
cross_reference --platform listed core files the platform's launcher already settles, as Batocera's DuckStation scph1002a.bin while the PAL slot holds scph5502.bin. It now hands the launcher layer the same entries verify does, and counts what it settles as set aside.
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.
Step 5 built emulator packs one profile at a time, where two profiles never share a path. It now also builds each system pack that gathers several profiles and compares it with verify. The first run found five standalone MSX and ColecoVision packs where verify skipped the data tree of a profile the system filter left without files, and credited msx-emu's copies the pack never wrote. verify now places those trees too: 541 system packs, all equal.
verify --platform recalbox --system nec-pc-engine printed no Core files line while the pack held six syscard copies for geargrafx: the section returned on an empty gap report before reading packed_extras. It now prints the pack's counts whenever they are not zero.
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).