refresh_data_dirs printed one line per entry and no total, a dry run named
the cached tag only, and cutting a quoted ETag at 12 characters kept one
quote. The run now ends on refreshed / up to date / failed counts, a dry
run shows the remote tag beside the cached one, and tags lose their quotes
before they are cut.
The role a part writes (source or upstream) was kept per path, first
met first kept, so a later part naming the other role was read in the
other repository. In prose the word source made a citation external
and the word upstream was dropped. The role now travels on each part.
An element whose md5 the truth contradicts without naming another is
written back whole. It was counted as an entry the format cannot keep.
It now counts as kept, and the md5 it keeps is named as withheld.
check_freshness --only native rebuilds the truth model to compare it, and
the hash conflict notes generate_truth reports opened the freshness output.
They are dropped there, and any other stderr line still passes through.
RomM compares file_size_bytes with the entry's size before any hash. A
new hash the truth gives no size for went out beside the old size and
was reported as a size removal. The entry now stays as written and the
correction is withheld and named.
8dc485dc moved the TOS names into each platform's launchers: records,
but the tos.img notes still described the Batocera, Recalbox and
RetroBat generators and an aliases field the entry no longer has.
Data directory owners were picked among every profile, aliases included,
and an alias has no page: the 90 FreeIntv overlays linked to
emulators/freeintv_ts_overlay/, a 404. Only a profile with a page owns a
tree now, and a page lists the trees of the aliases that point at it.
The coreinfo area compared .info names only: a firmwareN_path added to a
profiled core's .info came out OK. check_freshness now compares each
profiled .info's git blob with platforms/_core_info.json, recorded on
2026-10-21 from the current listing, and --record-coreinfo CORE records
one as reviewed.
A directory entry was joined to the collection by its own name, so the
11,976 files under alephone's Plugins/ and the cdogs, thextech and
easyrpg trees named no emulator on their card. FileConsumers now expands
the directory and records which entry reads each file. The emulator links
on a file card land on that entry, and a directory card names its copies
and the folder that holds them.
The check walked the published keys only and skipped null lists: a
target the builder knows and the installer lacks, or a null left behind
once the builder filters it, passed. It now compares both key sets and
every value, null included.
Downloader rejects an entry without an integer size (db_entity.py:187)
and compares it after each fetch (fetch_file_worker.py:127). A new hash
the truth gives no size for is withheld and named, not written beside
the old size nor reported as a size removal.
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 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.
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 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.
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.
The scraper reads each component's prepare, launcher and functions scripts at the components commit the BIOS list comes from, and the presets they copy: the fixed paths set_setting_value writes (melonDS seven, xemu three, Dolphin GBA), the folders handed over, the dir_prep links (LRPS2 pcsx2/bios back to bios/, Dolphin and PrimeHack GC regions under saves/), package copies into bios/, RetroArch core options naming a file (pcsx2_bios, DuckStation per region), XRoar romlists, the PICO-8 exec and the RPCS3 and Vita3K firmware downloads.
platforms/retrodeck.yml is regenerated at fcd0d729: only the launchers section is new.
The scraper walks each package's functions the way RetroPie runs them in one shell (assignments, arrays, for loops, brace expansion, $md_id, calls into other packages) and records every link, copy, move and setting that names $biosdir, the addEmulator flags (sdltrs -romfile3), the BIOS read outside $biosdir (basilisk --rom, gngeopi -B) and the launch scripts that exit without their file (fs-uae kick13.rom, reicast dc_boot.bin).
Each package also gets a package record with its rp_module_repo and the systems it registers, which maps a package to the build it compiles. The schema gains package, repo, systems and the moves and package kinds. platforms/retropie.yml is regenerated at 46128cd2: only the launchers section is new.
The scraper now records, from configgen at the pinned tag, the names each Recalbox launcher tries and where it hands them: the Hatari TOS cascades (standalone, hatari and hatarib cores), GSplus, Amiberry kickstarts, the PCSX2 region choice, the Dolphin IPL presence test, xemu, the mupen64plus 64DD IPL, the Mini vMac boot disk and the o2em_bios option. The system_directory libretroRetroarch gives each core, and the per-system overrides after it, are passes_path records.
The scraper reads the EmuScripts at the commit checkBIOS.sh is pinned to: the BIOS paths changeLine, iniFieldUpdate and RetroArch_setConfigOverride write, the ares and melonDS configs whose paths sed rewrites, and the bios/ directories linked to Flycast, Azahar, the Switch emulators and shadPS4. The script to emulator mapping moves beside the extractors so both read one table.