mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-21 13:09:54 +02:00
fix: improve Gen2 data loading and error handling to prevent crashes when cache modules are missing on android
This commit is contained in:
@@ -148,9 +148,44 @@ local function openEditor(version, slotId)
|
|||||||
editorMode = true
|
editorMode = true
|
||||||
resizeForEditor()
|
resizeForEditor()
|
||||||
addEditorRequirePath()
|
addEditorRequirePath()
|
||||||
EditorApp = require("App")
|
local okReq, appOrErr = pcall(require, "App")
|
||||||
EditorApp.load(path, { version = version, slotId = slotId, embedded = true,
|
if not okReq then
|
||||||
onClose = function() closeEditor() end })
|
editorMode = false
|
||||||
|
if version then
|
||||||
|
require("src.import.CacheFs").unmountVersion(version)
|
||||||
|
end
|
||||||
|
restoreWindow()
|
||||||
|
Importer = editorHost
|
||||||
|
editorHost = nil
|
||||||
|
editorVersion = nil
|
||||||
|
if Importer and Importer.resumeAfterOverlay then
|
||||||
|
Importer:resumeAfterOverlay()
|
||||||
|
end
|
||||||
|
refuse("Could not open the save editor (" .. tostring(appOrErr) .. ").")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
EditorApp = appOrErr
|
||||||
|
local okLoad, loadErr = pcall(EditorApp.load, path, {
|
||||||
|
version = version, slotId = slotId, embedded = true,
|
||||||
|
onClose = function() closeEditor() end,
|
||||||
|
})
|
||||||
|
if not okLoad then
|
||||||
|
editorMode = false
|
||||||
|
if EditorApp.unload then pcall(EditorApp.unload) end
|
||||||
|
EditorApp = nil
|
||||||
|
if version then
|
||||||
|
require("src.import.CacheFs").unmountVersion(version)
|
||||||
|
require("src.core.Data"):unloadGenerated()
|
||||||
|
end
|
||||||
|
restoreWindow()
|
||||||
|
Importer = editorHost
|
||||||
|
editorHost = nil
|
||||||
|
editorVersion = nil
|
||||||
|
if Importer and Importer.resumeAfterOverlay then
|
||||||
|
Importer:resumeAfterOverlay()
|
||||||
|
end
|
||||||
|
refuse("Could not open the save editor (" .. tostring(loadErr) .. ").")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Back to the launcher. Everything the editor mounted or cached has to come
|
-- Back to the launcher. Everything the editor mounted or cached has to come
|
||||||
|
|||||||
+33
-12
@@ -14,6 +14,14 @@ local MODULES = {
|
|||||||
-- Optional for compatibility with developer and stale caches.
|
-- Optional for compatibility with developer and stale caches.
|
||||||
local OPTIONAL = { "audio", "palettes", "icons" }
|
local OPTIONAL = { "audio", "palettes", "icons" }
|
||||||
|
|
||||||
|
-- Gold's extractor never writes these Gen 1 tables (RomExtractorGen2 has
|
||||||
|
-- maps/text/pokemon/items, not text_pointers / trainer_headers / field).
|
||||||
|
-- Desktop can still `require` Red's copies from the source tree, so Gold
|
||||||
|
-- Edit appeared to work there; an Android APK has only the per-version
|
||||||
|
-- cache, so Data:load used to throw on the first Gold Edit and take the
|
||||||
|
-- activity down. Empty tables are enough for seedDefaults / the editor.
|
||||||
|
local GEN2_OPTIONAL = { text_pointers = true, trainer_headers = true, field = true }
|
||||||
|
|
||||||
-- Vanilla defaults for rules exposed through the constants registry. A
|
-- Vanilla defaults for rules exposed through the constants registry. A
|
||||||
-- value has to exist before a mod can patch it; each one matches the
|
-- value has to exist before a mod can patch it; each one matches the
|
||||||
-- engine's no-mod behavior, so seeding them changes nothing on a vanilla
|
-- engine's no-mod behavior, so seeding them changes nothing on a vanilla
|
||||||
@@ -99,7 +107,11 @@ end
|
|||||||
-- Fills only what the cache is missing, so an importer that learns to
|
-- Fills only what the cache is missing, so an importer that learns to
|
||||||
-- stamp one of these keys silently takes over from the engine.
|
-- stamp one of these keys silently takes over from the engine.
|
||||||
function Data:seedDefaults()
|
function Data:seedDefaults()
|
||||||
local constants = self.constants
|
local constants = self.constants or {}
|
||||||
|
self.constants = constants
|
||||||
|
self.field = self.field or {}
|
||||||
|
self.maps = self.maps or {}
|
||||||
|
self.pokemon = self.pokemon or {}
|
||||||
for key, value in pairs(CONSTANT_DEFAULTS) do
|
for key, value in pairs(CONSTANT_DEFAULTS) do
|
||||||
if constants[key] == nil then constants[key] = copy(value) end
|
if constants[key] == nil then constants[key] = copy(value) end
|
||||||
end
|
end
|
||||||
@@ -108,7 +120,11 @@ function Data:seedDefaults()
|
|||||||
if constants.dexSize == nil then
|
if constants.dexSize == nil then
|
||||||
local highest = 0
|
local highest = 0
|
||||||
for _, def in pairs(self.pokemon) do
|
for _, def in pairs(self.pokemon) do
|
||||||
if def.dex and def.dex > highest then highest = def.dex end
|
-- Gold's pokemon.lua also carries growthRates / tmhmMoves / generation
|
||||||
|
-- scalars beside species rows.
|
||||||
|
if type(def) == "table" and def.dex and def.dex > highest then
|
||||||
|
highest = def.dex
|
||||||
|
end
|
||||||
end
|
end
|
||||||
constants.dexSize = highest
|
constants.dexSize = highest
|
||||||
end
|
end
|
||||||
@@ -212,37 +228,42 @@ local function loadModule(dir, name)
|
|||||||
if not chunk then return false, err end
|
if not chunk then return false, err end
|
||||||
return pcall(chunk)
|
return pcall(chunk)
|
||||||
end
|
end
|
||||||
local ok, mod = pcall(require, "data.generated." .. name)
|
|
||||||
if ok then return true, mod end
|
|
||||||
-- Fused PhysFS / Blue|Yellow prefix: load bytes from the active version's
|
|
||||||
-- cache explicitly when require cannot see the mounted tree.
|
|
||||||
local CacheFs = require("src.import.CacheFs")
|
local CacheFs = require("src.import.CacheFs")
|
||||||
local GameVersion = require("src.core.GameVersion")
|
local GameVersion = require("src.core.GameVersion")
|
||||||
local path = "data/generated/" .. name .. ".lua"
|
local path = "data/generated/" .. name .. ".lua"
|
||||||
local bytes = CacheFs.readActive(path)
|
local bytes = CacheFs.readActive(path)
|
||||||
if type(bytes) == "string" then
|
if type(bytes) == "string" then
|
||||||
local chunk, err = loadstring(bytes, "@" .. GameVersion.cachePrefix() .. path)
|
local chunk = loadstring(bytes, "@" .. GameVersion.cachePrefix() .. path)
|
||||||
if not chunk then return false, err or mod end
|
if chunk then
|
||||||
return pcall(chunk)
|
local ok, res = pcall(chunk)
|
||||||
|
if ok then return true, res end
|
||||||
end
|
end
|
||||||
return false, mod
|
end
|
||||||
|
local ok, mod = pcall(require, "data.generated." .. name)
|
||||||
|
if ok then return true, mod end
|
||||||
|
return false, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function Data:load()
|
function Data:load()
|
||||||
local dir = os.getenv("POKEPORT_DATA_DIR")
|
local dir = os.getenv("POKEPORT_DATA_DIR")
|
||||||
|
local gen2 = require("src.core.GameVersion").generation() == 2
|
||||||
for _, name in ipairs(MODULES) do
|
for _, name in ipairs(MODULES) do
|
||||||
local ok, mod = loadModule(dir, name)
|
local ok, mod = loadModule(dir, name)
|
||||||
if not ok then
|
if not ok then
|
||||||
if dir then
|
if gen2 and GEN2_OPTIONAL[name] then
|
||||||
|
self[name] = {}
|
||||||
|
elseif dir then
|
||||||
error(("missing data module '%s/%s.lua' (POKEPORT_DATA_DIR).\n(%s)")
|
error(("missing data module '%s/%s.lua' (POKEPORT_DATA_DIR).\n(%s)")
|
||||||
:format(dir, name, mod))
|
:format(dir, name, mod))
|
||||||
end
|
else
|
||||||
error(("missing generated data module 'data/generated/%s.lua'.\n" ..
|
error(("missing generated data module 'data/generated/%s.lua'.\n" ..
|
||||||
"Import the ROM again or rebuild developer data.\n(%s)")
|
"Import the ROM again or rebuild developer data.\n(%s)")
|
||||||
:format(name, mod))
|
:format(name, mod))
|
||||||
end
|
end
|
||||||
|
else
|
||||||
self[name] = mod
|
self[name] = mod
|
||||||
end
|
end
|
||||||
|
end
|
||||||
for _, name in ipairs(OPTIONAL) do
|
for _, name in ipairs(OPTIONAL) do
|
||||||
local ok, mod = loadModule(dir, name)
|
local ok, mod = loadModule(dir, name)
|
||||||
self[name] = ok and mod or nil
|
self[name] = ok and mod or nil
|
||||||
|
|||||||
@@ -373,5 +373,25 @@ do
|
|||||||
check(main ~= nil, "saveFilename resolves for gold")
|
check(main ~= nil, "saveFilename resolves for gold")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Gold's cache has no text_pointers / trainer_headers / field. Data:load
|
||||||
|
-- used to throw in seedDefaults (self.field.boot) after filling pokemon
|
||||||
|
-- with provenance scalars. That is the Android first-Edit CTD: the APK
|
||||||
|
-- cannot fall back to Red's source-tree copies the way a desktop checkout
|
||||||
|
-- can.
|
||||||
|
do
|
||||||
|
GameVersion.set("gold")
|
||||||
|
local Data = require("src.core.Data")
|
||||||
|
Data.constants = {}
|
||||||
|
Data.pokemon = { generation = 2, CYNDAQUIL = { dex = 155 } }
|
||||||
|
Data.maps = {}
|
||||||
|
Data.field = nil
|
||||||
|
Data.trainer_headers = nil
|
||||||
|
local ok, err = pcall(function() Data:seedDefaults() end)
|
||||||
|
check(ok, "gold seedDefaults survives a Gold-shaped cache: " .. tostring(err))
|
||||||
|
check(type(Data.field) == "table", "seedDefaults creates field when Gold omitted it")
|
||||||
|
eq(Data.constants.dexSize, 155, "dexSize ignores pokemon.generation scalar")
|
||||||
|
GameVersion.set("red")
|
||||||
|
end
|
||||||
|
|
||||||
print(string.format("save editor gen2 tests: %d passed, %d failed", passed, failed))
|
print(string.format("save editor gen2 tests: %d passed, %d failed", passed, failed))
|
||||||
if failed > 0 then os.exit(1) end
|
if failed > 0 then os.exit(1) end
|
||||||
|
|||||||
@@ -78,15 +78,28 @@ function Gen.bindGoldData(data)
|
|||||||
if data.palettes and data.gen2Palettes == nil then
|
if data.palettes and data.gen2Palettes == nil then
|
||||||
data.gen2Palettes = data.palettes
|
data.gen2Palettes = data.palettes
|
||||||
end
|
end
|
||||||
if data.gen2Roofs == nil and data.roofs == nil then
|
|
||||||
local ok, roofs = pcall(require, "data.generated.roofs")
|
local loadGen = function(rel)
|
||||||
if ok and type(roofs) == "table" then
|
local CacheFs = require("src.import.CacheFs")
|
||||||
data.roofs = roofs
|
local bytes = CacheFs.readActive("data/generated/" .. rel .. ".lua")
|
||||||
data.gen2Roofs = roofs
|
if type(bytes) == "string" then
|
||||||
|
local chunk = loadstring(bytes, "@gold/data/generated/" .. rel .. ".lua")
|
||||||
|
if chunk then
|
||||||
|
local ok, res = pcall(chunk)
|
||||||
|
if ok and type(res) == "table" then return res end
|
||||||
end
|
end
|
||||||
elseif data.roofs and data.gen2Roofs == nil then
|
|
||||||
data.gen2Roofs = data.roofs
|
|
||||||
end
|
end
|
||||||
|
local ok, res = pcall(require, "data.generated." .. rel)
|
||||||
|
if ok and type(res) == "table" then return res end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
data.gen2Palettes = data.gen2Palettes or loadGen("palettes")
|
||||||
|
data.gen2Icons = data.gen2Icons or loadGen("icons")
|
||||||
|
data.gen2Pokedex = data.gen2Pokedex or loadGen("pokedex")
|
||||||
|
data.gen2Landmarks = data.gen2Landmarks or loadGen("landmarks")
|
||||||
|
data.gen2Roofs = data.gen2Roofs or loadGen("roofs") or data.roofs
|
||||||
|
data.gen2Sprites = data.gen2Sprites or loadGen("sprites")
|
||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -213,10 +226,15 @@ function Gen.playerMap(save)
|
|||||||
if Gen.of(save) == 2 then
|
if Gen.of(save) == 2 then
|
||||||
local p = save.position
|
local p = save.position
|
||||||
if p and p.map then return p.map, p.x or 0, p.y or 0, p.facing end
|
if p and p.map then return p.map, p.x or 0, p.y or 0, p.facing end
|
||||||
|
if type(save.spawn) == "table" then
|
||||||
|
return save.spawn.map or "PLAYERS_HOUSE_2F", save.spawn.x or 0, save.spawn.y or 0, save.spawn.facing
|
||||||
|
elseif type(save.spawn) == "string" then
|
||||||
return save.spawn, 0, 0
|
return save.spawn, 0, 0
|
||||||
end
|
end
|
||||||
|
return "PLAYERS_HOUSE_2F", 3, 3
|
||||||
|
end
|
||||||
local p = save.player or {}
|
local p = save.player or {}
|
||||||
return p.map, p.x or 0, p.y or 0
|
return p.map or "REDS_HOUSE_2F", p.x or 0, p.y or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function Gen.setPlayerHere(save, mapId, x, y, facing)
|
function Gen.setPlayerHere(save, mapId, x, y, facing)
|
||||||
|
|||||||
Reference in New Issue
Block a user