HiscoreInit opens <g_save_dir>/hiscore.dat (hiscore.cpp:411). Without
load_from the entry was packed at bios/hiscore.dat and took the slot
mame2000 reads on RetroDECK, which now gets its sha1-pinned file.
The release-source check called any uncommitted repo_path gitignored,
pointing at the release assets instead of a missing commit, and its
skip outside a checkout could not fire. It now asks git which paths are
ignored, and a new check requires bios/ to hold no untracked file.
An element lists its cores. Its mandatory flag now follows whether those
cores need the file, and stays as written where none of them reads it.
cdi/cdibios.zip keeps mandatory=false (same_cdi does not read it), the
two amiberry kickstarts keep theirs, saturn_bios.bin becomes optional
for the three HLE cores it names.
Ten notes and comments called .info entries phantom, misleading or
incorrectly written. They now say what the .info declares and what
the code reads, and a prose test refuses those verdicts in profiles.
diff_platform compared only version, files and core lists: a rescrape
that changed launchers, dual_mode_cores or a system's core, native_id or
data_directories came out identical. Every other field is now compared,
without the revision read (source) and the source_ref lines that move with it.
Every member was deflated, archives and PNGs included. On the 2026-10-20
RetroArch pack the .zip, .7z, .chd, .png, .ogg and .pk4 members are 2.1 GB
that took 28 s to deflate again for 9.6 MB saved (0.45 %), and about 16 GB
for 72 MB across all packs. Those formats are now stored. The choice reads
the member name only, so a pack stays byte-reproducible.
The analysis and firmware blocks printed their source_ref as plain code,
dropped any list or map inside a list item (cemu's firmware title
contents), cut values at 200 characters and let a folded note's second
line leave its admonition. Citations are now pinned links and every
value is shown on its own line.
Bios.cpp:64-68 hashes a .zip with Zip::Md5Composite, the md5 of its
members in name order. A profile's md5 on an archive is the container's,
so it neither pairs with, corrects nor fills that field, and an archive
addition with only a container md5 is left out.
mkdocs was installed by the deploy alone, after the merge, and wiki/
was not a validation path: a dead link or a stray heading failed the
deploy on main with nothing in front of it. The test job now runs the
deploy's site build, and every site input triggers validation.
The summary, the provenance table and the per-emulator Missing column
summed the report's gap_missing, which counts files the emulator's own
package installs: 1,356 files to source where 609 are. They get their
own row and column now.
The repository was renamed to CoolbarEmu (PR #78) and its buildbot
recipe now builds coolbaremu. Hold the profile on 4c9b73f7, the last
revision built as nicaiemu, and document the orientation list it reads
from the system directory.
The buildbot system row was planned as refresh_data_dirs.py --key
'buildbot system', which always exits 1 on an unknown key. A data row now
runs only when its subject is a _data_dirs.yml entry; the rest is shown
for a human to add.
scraper_cli wrote a platform YAML with no system when the parse came back
empty, and exited 0. It now exits 1 before writing, unless the scraper
declares lists_files = False (RetroPie, whose files are inherited); RetroPie
in turn raises when its archive declares no package.
The cross-reference page read the label from the shape of cores:, so
RetroPie and RetroDECK, which name retroarch in a list, showed no
"(all libretro)". The label now comes from the resolved core set.
_stable_hashes retried through the hash cache, so both retries returned
the first reading and the file was read once before the manifest run
stopped. The retries now read the file again.
The gaps page called each platform's file a corrected list, a word the
exports page test already kept off that page. The test now reads the
text of every site generator.
Both are written as <platform>.json. --manifest-targets aimed at install/
replaced install/retroarch.json with a target table, and --manifest aimed
at install/targets/ did the reverse, both with exit 0. Each mode now stops
when the output directory holds the other kind and names the directory to
use.
gngeopi and neo-emu declare neogeo.zip loose and, apart, the ROMs they
open inside it. The archive pass took the copy holding them, the loose
entry resolved by name to another copy, and both verify --emulator and
the emulator pack reported the archive missing while the ZIP held it.
with_archive_members() gives the loose entry the members in verify, the
pack, slots and the gap report.
The index of other system spellings was rebuilt only when the page
table changed length, so a table of the same size kept the previous
links. The builder now returns its table and set_system_page_map
installs it with the index derived from it.
The Required packs and manifests carried every data tree, the FreeIntv
overlays included. A data_directories entry now accepts required: false,
and the platform, emulator and manifest walks leave such a tree out under
--required-only. FreeIntv reads its overlays only with the multi-screen
option and draws a placeholder keypad without one (src/libretro.c:294-296,
350-369).
The cache was truncated and dumped in place. A concurrent build read
half a file, took it for empty, and an offline run then wrote {} over
every recorded icon. The file is now replaced through a scratch file,
merged with what another run recorded meanwhile, and left alone when
nothing new was learned.
The MAME and FBNeo hash scrapers cached an empty parse for 24 hours and
reported OK. Both now raise before writing, and an empty cache is never
served as fresh.
--verify-packs after an --all-variants build rebuilt only the full pack's
name and opened it alone. The five narrowed packs of the same run passed
without a line. Each one is now named with the hash check the build ran
on its members.
Names are matched on letters and digits, so the site's display names
work, and a typo gets the closest names instead of a full list. Hints
and advice give back the command the user ran (curl or PowerShell form,
every flag kept), --dest alone follows detection and says whose list it
installs, Ctrl+C at a question exits 130, and --cores-dir names a core
folder. install.ps1 runs in its own scope so irm | iex leaves the
session alone, and an override that causes a fingerprint mismatch is
named. --check lists hash mismatches first, missing required files are
grouped by system, and --help ends with examples.
--emulator accepts a buildbot core name from a profile's cores:, and a
mistyped target gets the closest names instead of every target. diff_truth
shows destinations and reports unpaired entries as evidence, not removals.
The verify summary counts discrepancies, a shared destination names both
systems, a truth hash conflict names both cores, and pipeline steps carry
their place in the real run instead of N/8. Every GitHub API caller takes
the token from github_token(), which falls back to gh auth token.
Troubleshooting, the FAQ and the download page sent people who only ran
the installer to verify.py, which reads the repository, not their BIOS
folder. Download links now name the exact asset of the recorded release,
the --dest example names the BIOS folder on the card, the slice layout
left with the last release that used it, and the shown verify output is
what the tool prints.
A fork's GITHUB_TOKEN is read-only, so the comment never arrived and a
valid file turned red. The report now goes to the job summary for every
pull request, and the comment and labels run only where the token can
write. validate_pr points an oversized file to an issue instead of a
platform setting a contributor cannot use.
Principles, collection rules and the inverted model in architecture.md, research and citation rules in profiling.md, platform and scraper rules in their pages, tool conventions in tools.md, test and commit expectations in testing-guide.md, installer constraints in installer.md. Each rule sits where a reader looks for it, with its reason. Also fixes the stale release-job line, the suite command without -t ., and the unsourceable definition.
On Windows the partial copy of a file of 16 MB or more is locked with msvcrt.locking, a LockFile region: the locking process cannot access that region through a second handle (Microsoft LockFile remarks). Hashing reopened the path, the read failed with a sharing violation, and every large file was reported as 'folder not writable' (issue 82). Both digests are now read in one pass through the handle that holds the lock. A read-only file already at the destination had the same misleading report: on Windows its attribute is now cleared before the verified copy replaces it, and a refusal that remains is named for what it is.
RetroPie's YAML listed no standalone_cores, so every package was laid
out as a libretro core and the standalone files of amiberry, dolphin,
hatari, mame and ppsspp never reached the pack. Packages without the
lr- prefix (bar the retroarch frontend) are now standalone, and names
shipped both ways are dual-mode.
quasi88 writes its list "N88KNJ2.ROM, and N88SUB.ROM". Extended with
N88EXT0.ROM, which QUASI88kai reads, it came back as "N88SUB.ROM and
N88EXT0.ROM": every list was rewritten in one idiom. A list that puts a
comma before its final and keeps it.
EmuDeck passes Complex_4627v1.03.bin as xemu's flash ROM
(emuDeckXemu.sh:60-61), and xemu's entry Complex_4627.bin states no hash,
only the sizes it maps. The layer matched entries by name or content
only, so without the alias the default name was still asked for. A
hashless entry that checks a size now takes a launcher file of a size
it accepts, when no other entry of that core accepts that size too.
The removal candidates looked for a reading in the profiles only, so a
name the platform's own launch code opens, links or copies was listed
as read by no core: Batocera's DuckStation generator walks 22 names
(duckstationGenerator.py:501-515). Every name a launcher record names
is now a reading on that platform. Recalbox's unexplained entries drop
from 20 to 13, RetroBat's from 24 to 20, EmuDeck's from 25 to 22.
The 43 tos.img aliases in hatari.yml and 10 in hatarib.yml were names Batocera, Recalbox and RetroBat launchers pass to Hatari (hatariGenerator.py, Hatari.Generator.cs, libretroCores.py), now recorded in each platform's launchers: records. With standalone Hatari profiled, those records attach. Measured on Batocera, RetroBat, Recalbox, ROCKNIX and RetroPie: verify statuses, manifest files, omissions and Hatari entries are identical before and after.
Batocera (v2.6.1, hatari.mk), RetroBat (v2.4.1 release), Recalbox and ROCKNIX run standalone Hatari, which the profile did not describe, so their launcher records had no build to attach to. Standalone Hatari reads tos.img from its data directory by default (src/configuration.c:810-812, src/paths.c:316-330), retries a missing name with .img, .rom and .tos (src/file.c:275-308, 481-520), and exits when no TOS loads (src/main.c:191-201). The profile is now standalone + libretro, with the keymap file and the window icon as standalone entries and BOOT.ST scoped to libretro.
apple2gs1.rom and apple2gs3.rom come only from Recalbox's gsplusGenerator.py:107-110, 140, a platform launcher now recorded in platforms/recalbox.yml. No platform resolves gsplus.yml (Recalbox, Batocera and RetroBat run gsplus-legacy), and verify and the manifests of Recalbox, RetroDECK, ROCKNIX and EmuDeck are identical before and after.
RetroPie builds minivmac/minivmac, winterheart/quasi88 and TurtleBazooka/px68k under the package names minivmac, quasi88 and px68k (scriptmodules/emulators/minivmac.sh:12, quasi88.sh:12, px68k.sh:12). With built_by retropie and those names in cores, RetroPie resolves them to minivmac37, quasi88kai and px68k-sdl, while its libretro builds stay reached through retroarch. Measured: RetroPie keeps 532 of 532 platform files OK and gains the 14 files the standalone builds read; no other platform's resolution changes.
bios/Sharp/X68000/cgrom.tmp had cgrom.dat's sha1 (8d72c5b4). px68k reads cgrom.tmp only when cgrom.dat is absent (libretro.c:915-919, SDL/SDL2/winx68k.cpp:317-331), and a real cgrom.tmp is a font mkcgrom generates on the user's machine. No platform declares it.
The launcher layer packed a file under the launcher's name whatever the
platform installs: ROCKNIX's xemu config reads hdd/xbox_hdd.qcow2, and
the profile marks that image installed_on rocknix, yet the pack brought
one. A place whose entry the emulator installs on that platform is now
filled by the install, as validation.deliverable has it for the pack.
RetroBat's ares launcher tries v1.05, v1.02 then v1.01 (Ares.Generator.cs:371-437), now recorded under launchers: in platforms/retrobat.yml. ares itself identifies the image by sha256. No file carries the name, so the database is unchanged.
A names_searched list is one place the launcher fills with the first name it finds, so a core file it would never open once another name of the list is held is not reported missing. A place no declared file and no packed core file fills receives, under the launcher's name, a file the core accepts by content. The layer is applied inside find_undeclared_files, which verify and the pack builder both read, and a test reads their source to keep it there.