The default Docker image symlinked /romm/assets into the nginx static web
root (/assets/romm/assets), where it was served by an unauthenticated
`location /assets { try_files ... }` block. /romm/assets holds private user
data (save files, save states, screenshots, avatars) that is meant to be
accessible only through the authenticated /api/raw/assets/{path} route
(Scope.ASSETS_READ). The static symlink bypassed that protection, letting any
unauthenticated caller read another user's files given a (guessable) path.
Avatar URLs leaked the hex user ID through the same static route, making path
construction straightforward.
Fix:
- Drop the /romm/assets symlink from the Docker image build and both
entrypoint scripts; only /romm/resources (public cover art, screenshots,
manuals) remains statically served.
- Point the frontend avatar URLs at the authenticated /api/raw/assets/ route
instead of /assets/romm/assets/. Browser <img> loads authenticate via the
existing session cookie.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- genesis_plus_gx_wide added alongside genesis_plus_gx for all Sega
MD/MS/GG/CD platforms
- bsnes added alongside snes9x for SNES platforms
- freeintv added for Intellivision
- azahar added for Nintendo 3DS (3ds, new-nintendo-3ds) and marked as
requiring threads
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The paginated ROM list eager-loaded sibling_roms via selectinload, which
hydrated full Rom ORM instances (including heavy JSON metadata columns)
for every sibling even though only an existence/count check was needed
on the frontend. On large collections this dominated request latency.
Split sibling handling by response shape:
- SimpleRomSchema (list): siblings is now list[int]; populated per page
by a single SELECT against the sibling_roms view projecting only
(rom_id, sibling_rom_id) — no Rom row hydration.
- DetailedRomSchema (detail): keeps full SiblingRomSchema objects, with
load_only on (id, name, fs_name_no_tags, fs_name_no_ext) so sibling
rows stop dragging in JSON metadata.
Frontend usage already only consumes siblings.length on list views; the
detail-page VersionSwitcher continues to receive the richer schema.
The gallery list endpoint was eager-loading every rom_file row for each
paginated ROM via selectinload, then re-joining each row back to its
parent rom for the is_top_level computation. For platforms with extracted
multi-file ROMs (Xbox 360 ~1394 files/ROM, Switch ~199 files/ROM), this
made /api/roms time out at 120s even with a rom_id index.
Cards never displayed individual files — only the has_simple_single_file
/ has_nested_single_file / has_multiple_files booleans that derive from
the file list. Denormalize the underlying state onto roms as multi_file
(folder-based vs single-file) and top_level_file_count, recompute the
booleans from those columns, drop the selectinload from filter_roms, and
move the files field from SimpleRomSchema to DetailedRomSchema so the
gallery payload no longer ships file rows.
Also drop the redundant joinedload(RomFile.rom) and switch the relation
to lazy="select" so subsequent file.rom accesses resolve from the
session identity map instead of re-JOINing the parent rom per file row.
ShowQRCode.vue's folder-based DS/3DS fallback now fetches the detailed
rom on demand, since SimpleRom no longer carries files.
For ROMs tagged with multiple regions (e.g. "(Japan, USA)"), filename order
previously decided which region's name and box art won. Now reorder the rom's
filename-tagged regions by SCAN_REGION_PRIORITY before prepending, so the
user's configured preference wins among the regions the file is actually
tagged as. Untagged priority regions still cannot outrank a filename-tagged
region.
Also tweak the Total Rescan → Complete Rescan label in en_GB/en_US scan
locales.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CHD files now follow the same hash logic as all other file types — CRC32,
MD5, and SHA1 are computed from raw container bytes. This allows
ScreenScraper to log KO entries for unrecognised CHD files, which it
could not do when only the disc-data SHA1 was being computed.
The CHD header SHA1 (disc-data SHA1) is separately extracted and stored
in a new chd_sha1_hash field on RomFile, with a migration adding the
column to rom_files. Hasheous receives only this disc-data SHA1 (no
CRC/MD5) since it indexes disc-based games by disc-data SHA1, not raw
file hashes.
The RAHasher multi-file path now passes the largest CHD directly instead
of a /* wildcard, which RAHasher cannot expand. Hash computations are
wrapped in asyncio.to_thread to avoid blocking the event loop during
large reads.
Hash-lookup metadata handlers (ScreenScraper, Hasheous, Playmatch) now
fall back to rom.files (stored DB hashes) when fs_rom files are not
rehashed, fixing hash-based matching for UNMATCHED and UPDATE scan types.
The Disc SHA-1 is displayed in the ROM detail view for both single-file
(FileInfo.vue) and multi-file (FileSelectItem.vue) CHD games.
When a ROM is rescanned and its file IDs change, disc IDs stored in
localStorage become stale. This caused file_ids query params with invalid
IDs to be sent to /api/roms/{id}/content/{name}, resulting in 404 errors.
Validate the stored disc ID against the actual ROM files before use. If
stale, clear localStorage and fall back to the first available file.
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/3579d577-13ff-4288-9a9c-909b6f891c9e
Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
Covers now use object-fit: contain inside a fixed slot, so each image
keeps its own aspect ratio (letterboxed where needed) instead of being
cropped to the platform's configured ratio. The slot itself still uses
a single default aspect ratio for grid stability, so cards don't reflow
as images load.
With per-platform aspect ratio no longer doing anything visible, remove
the Cover style picker from the platform info drawer and clean up dead
plumbing: galleryViewStore.getAspectRatio no longer reads
platform.aspect_ratio, platformId is dropped from Skeleton / SearchCover
/ MatchRom / showSearchCoverDialog, and the orphaned i18n keys
(cover-style, settings, old-squared-cases, old-horizontal-cases) are
stripped from all platform.json locale files.
Backend aspect_ratio column is left in place; no client sends it anymore.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The animated SVG background causes lag on Firefox. Swap in the static
variant via @-moz-document url-prefix() so other engines keep the
animation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>