Importing Red unpacked data/generated, assets/generated and
rom-cache.complete straight into the save-dir root, while Blue and
Yellow land under blue/ and yellow/. Red now uses cachePrefix red/
like the others.
CacheFs.migrateLegacyRedCache moves a pre-existing root cache into
red/ on first boot, from RomImporter.new before the readiness loop
and from mountVersion, so existing installs keep their import instead
of being asked for the ROM again. The move only runs when the root
marker resolves to the save directory, so a dev checkout's source
tree is never touched, and the portable game folder is skipped when
it is the physfs source.
Closes#899
The modkit validate and pack drivers run the real loader under plain
luajit, with no love global. With --base imported, Data:load falls
back to CacheFs.readActive for a generated module require cannot find
(an optional module like data/generated/audio.lua is legitimately
absent from developer and stale caches), and CacheFs.read indexed
love.filesystem once there was no portable root, so validate and pack
died with MK100 before the mod was even looked at. Headless there is
no save directory to read from, so return nil like any other cache
miss.
Refs #850
NX fused mount often cannot expose assets/generated; open the real
yellow|blue/assets/generated file with newImage instead of FileData.
Co-authored-by: Cursor <cursoragent@cursor.com>
Probe generated canaries after mountVersion and always readActive for
prefixed caches so sprites are not blanked by empty PhysFS stubs.
Co-authored-by: Cursor <cursoragent@cursor.com>
Mirror Data:load's versioned CacheFs read in Assets so Yellow-only NX
Play survives intro without needing a Red root cache mask.
Co-authored-by: Cursor <cursoragent@cursor.com>
NX Play for Blue failed because mountVersion relied on absolute
PHYSFS_mount first. Prefer love.filesystem.mount of blue|yellow, overlay
generated trees by version prefix, and align CacheFs.prefix in bootGame.
Co-authored-by: Cursor <cursoragent@cursor.com>
PhysFS does not merge archive data/ with save-dir data/generated, so
fused NX Play crashed after import. Prepend-mount generated trees,
fall back to CacheFs.read in Data:load, keep multiline lua-error logs,
and skip Boot.run when network is unvalidated. T24 stays open.
Co-authored-by: Cursor <cursoragent@cursor.com>
love.filesystem looks for "mods/" in two places: the save directory, and
-- portable installs only -- the game folder, which CacheFs mounts. So a
player who unzips a mod next to the executable of an ordinary install,
which is where very nearly every other game would want it, gets no error
and no mod. The panel just comes up empty, with nothing on screen to
suggest the files are sitting in the wrong folder twenty centimetres away.
That is a hard failure to self-diagnose, and it is worse behind a
launcher: the install lives somewhere the player never opens, so "the
game's mods folder" is a guess to begin with.
The mods panel now looks in those folders before its first listing and
copies what it finds into the tree the game really reads, reporting what
it took in the notice line. It happens on open rather than behind a
button because the failure being fixed is one where nothing suggests
there is anything to press.
Looking is scoped: CacheFs.withMounted puts the folder on the read path
at its own mount point, runs the scan, and takes it straight back off.
Nothing a stray folder contains can shadow a game file or change what the
running game resolves, which is what makes it safe to point at a folder
whose contents nobody has validated. Adoption skips ids the game can
already see, so it is idempotent and never nags twice, and it leaves the
loose folder alone -- deleting files outside the save directory on the
player's behalf is not this code's call to make.
Which strays are worth taking is pure (LauncherMods.pickStrays), matching
how deriveList and locateRoot are already split out, so the engine tier
covers the rules without needing love. SaveData.gameFolders is the old
detectPortable candidate list lifted out unchanged -- portable mode is
just the case where one of those folders holds the marker.
Claude-Session: https://claude.ai/code/session_01JvEthuoNBPfxpvHUD9Pd4N