This commit is contained in:
bryanthaboi
2026-07-23 11:14:40 -04:00
parent 5128b840b1
commit 78aa1f3c0a
15 changed files with 45826 additions and 208 deletions
+6 -1
View File
@@ -99,7 +99,12 @@ end
-- total conversion overrides. Threaded into SaveData so persistence stays
-- free of a Data dependency.
function Game:bootConfig()
return self.data and self.data.field and self.data.field.boot
local boot = self.data and self.data.field and self.data.field.boot
-- stamp the running game version onto the boot config so a New Game records
-- it (SaveData.newGame reads boot.version); this is what routes a Blue
-- playthrough to save_blue.lua and Blue's version-gated content
if boot then boot.version = require("src.core.GameVersion").get() end
return boot
end
-- the title screen with its NEW GAME / CONTINUE wiring; used at boot
+77
View File
@@ -0,0 +1,77 @@
-- Which Gen-1 game this process is running: Red (the historical default) or
-- Blue. One source of truth for everything that differs by version -- the
-- accepted ROM hash, the import manifest, where the extracted cache lives,
-- and the save-file suffix -- so the importer, cache mount, SaveData, title
-- screen and palette all agree.
--
-- Red keeps every un-suffixed path it always used (save.lua, the root cache),
-- so existing installs are untouched; Blue is namespaced under blue/ and
-- _blue so both can be imported and played side by side.
--
-- Zero requires, so it loads during love.conf and under plain Lua for tools
-- and tests. The active version is a process-global set once at boot from
-- the launcher's column choice (main.lua); it defaults to Red.
local GameVersion = {}
GameVersion.VERSIONS = {
red = {
id = "red",
label = "Red",
displayName = "Pokemon Red",
sha1 = "ea9bcae617fdf159b045185467ae58b2e4a48b9a",
manifest = "tools/rom_manifest.json",
cachePrefix = "", -- Red owns the cache root (backwards compatible)
saveSuffix = "", -- save.lua / save.lua.bak / save.lua.tmp
},
blue = {
id = "blue",
label = "Blue",
displayName = "Pokemon Blue",
sha1 = "d7037c83e1ae5b39bde3c30787637ba1d4c48ce2",
manifest = "tools/rom_manifest_blue.json",
cachePrefix = "blue/", -- blue/data/generated, blue/assets/generated
saveSuffix = "_blue", -- save_blue.lua / .bak / .tmp
},
}
-- Launcher column order (Yellow is still a placeholder, handled by the UI).
GameVersion.ORDER = { "red", "blue" }
GameVersion.current = "red"
function GameVersion.set(id)
GameVersion.current = GameVersion.VERSIONS[id] and id or "red"
return GameVersion.current
end
function GameVersion.get()
return GameVersion.current
end
function GameVersion.isBlue()
return GameVersion.current == "blue"
end
-- Metadata for a version id, defaulting to the active one.
function GameVersion.info(id)
return GameVersion.VERSIONS[id or GameVersion.current]
end
function GameVersion.saveSuffix(id)
return GameVersion.info(id).saveSuffix
end
function GameVersion.cachePrefix(id)
return GameVersion.info(id).cachePrefix
end
-- The version a ROM belongs to, by its SHA-1, or nil for an unknown ROM.
function GameVersion.forSha1(sha1)
for id, info in pairs(GameVersion.VERSIONS) do
if info.sha1 == sha1 then return id end
end
return nil
end
return GameVersion
+28 -6
View File
@@ -18,14 +18,30 @@ local Semver = require("src.mods.Semver")
local Boxes = require("src.pokemon.Boxes")
local Bag = require("src.inventory.Bag")
local GameVersion = require("src.core.GameVersion")
local SaveData = {}
local FILENAME = "save.lua"
-- Progress files carry the game-version suffix so Red and Blue saves coexist:
-- Red keeps save.lua / .bak / .tmp exactly as before; Blue is save_blue.lua
-- (+ .bak/.tmp). options.lua is deliberately shared across versions (it holds
-- global preferences and the mod enable-state, not per-playthrough data).
local OPTIONS_FILENAME = "options.lua"
-- one rolling backup plus the staged-write witness; load promotes either
-- when the main file is missing or fails to parse
local BACKUP_FILENAME = FILENAME .. ".bak"
local TMP_FILENAME = FILENAME .. ".tmp"
-- Main / backup / staged-witness names for a version (defaults to the active
-- one). The backup is a rolling copy and .tmp is the staged-write witness;
-- load promotes either when the main file is missing or fails to parse.
local function saveNames(version)
local main = "save" .. GameVersion.saveSuffix(version) .. ".lua"
return main, main .. ".bak", main .. ".tmp"
end
-- The main save filename for a version -- used by the title screen's
-- CONTINUE gate so it looks for the right game's save.
function SaveData.saveFilename(version)
local main = saveNames(version)
return main
end
-- ------- portable mode
-- LÖVE's save directory is always the OS per-user path derived from the
@@ -469,6 +485,9 @@ end)
-- itself rolls the last good save into .bak and stages the new bytes as
-- a .tmp witness before the swap, so a crash mid-write is recoverable.
function SaveData.save(data, mods)
-- write to the file matching this save's own version, not just the active
-- one, so a Blue playthrough always lands in save_blue.lua
local FILENAME, BACKUP_FILENAME, TMP_FILENAME = saveNames(data.version)
if data.options then
SaveData.saveOptions(data.options)
end
@@ -508,7 +527,10 @@ end
-- returns the parsed save plus "tmp"/"bak" when the main file was gone
-- or corrupt and a staged/backup copy was promoted; Game surfaces the
-- recovery on the load report
function SaveData.load()
function SaveData.load(version)
-- version defaults to the active game (set at boot from the launcher);
-- an explicit version lets callers/tests load a specific game's save.
local FILENAME, BACKUP_FILENAME, TMP_FILENAME = saveNames(version)
local fs = persistFs(nil)
local data, err = readTable(fs, FILENAME)
local recovered