mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-25 23:11:15 +02:00
ca4d3d283c
Crystal boots from a user-supplied ROM, imports a full cache and is playable: copyright, the Crystal intro movie, the animated title, gender select, Oak, and out into Johto. 122 of the cart's 169 script specials are implemented. Import and data - tools/make_crystal_manifest.py derives the manifest by importing make_gold_manifest as a library, with three additive keyword seams. Gold and Silver still regenerate byte-identical, which is the standing requirement for touching that generator. - crystal_symbol_deltas.py and crystal_movie_symbols.py carry the symbol delta: Crystal renames the credits mons, splits the trainer card, Pokegear and pack-pal blocks by gender, and replaces the intro and title outright. - Crystal-only manifest keys: engineFlagOrder (162 flags to Gold's 93, so the badge block sits one higher) and unownCharmap (the main charmap parser stops at the first newcharmap so the two cannot contaminate each other). Extractor - RomExtractorGen2 becomes three-edition. Crystal corrections: PAL_MAP_BANK 0x13, a flat PICS_FIX pic bank, audio bank 0x5e, the mapSongs id-100 hole, seven NPC trades, a TradeTexts stride of 8, the five Crystal tileset anim steps with per-row degrade, and the column-major trainer card portraits. - New: animated front sprites (frames, bitmasks, play and idle scripts), the Battle Tower roster, Kris assets, Mobile System GB art, and the Crystal intro and title via src/import/CrystalMovie.lua. Engine - GameVersion gains engine(id) and fixes(id). Gold and Silver keep their original bugs where the bug is not hardware dependent; Crystal gets the fixes Crystal shipped: Lucky Number boxes 10-14, surfing onto an NPC, and the Reflect and Light Screen defence overflow. - Crystal story: Suicune and Eusine, Celebi behind the GS Ball flag, the Ruins of Alph chambers, Buena, the Move Tutor, the Poke Seer, and the Battle Tower including the wInBattleTowerBattle badge-boost guard. - Kris and the gender flag, animated fronts in battle and the summary screen, and mon caught data. Verification - Every extracted asset is pixel-compared against pret's own source PNGs. - Gold caches are byte-identical before and after, file for file. - New Crystal suites plus a T2 Gen 2 tier; the full suite passes.
120 lines
5.4 KiB
Lua
120 lines
5.4 KiB
Lua
-- Launcher SKINS tab (#1386): the tab next to Find that lists skins, imports
|
|
-- a dropped .zip and opens the desktop Skin Studio. The rendered panel is
|
|
-- exercised by tests/drivers/launcher_skins_tab_shot.lua; this pins the
|
|
-- archive installer and the launcher wiring around it.
|
|
-- luajit tests/engine/launcher_skins_tab.lua
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
if not _G.love then _G.love = require("tests.love_stub") end
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
|
|
local TouchSkin = require("src.core.TouchSkin")
|
|
|
|
local function read(path)
|
|
local f = assert(io.open(path, "r"))
|
|
local src = f:read("*a")
|
|
f:close()
|
|
return src
|
|
end
|
|
|
|
-- ------------------------------------------------------ archive installer
|
|
|
|
eq(select(1, TouchSkin.installArchive("skin.zip", nil)), nil,
|
|
"an empty archive is refused")
|
|
eq(select(1, TouchSkin.installArchive("skin.zip", "")), nil,
|
|
"a zero-byte archive is refused")
|
|
eq(select(1, TouchSkin.installArchive("notes.txt", "data")), nil,
|
|
"a non-zip is refused")
|
|
eq(select(1, TouchSkin.installArchive("", "data")), nil,
|
|
"a nameless drop is refused")
|
|
|
|
-- A zip carrying no skin file must not leave a stray archive behind: the
|
|
-- list would keep trying to mount it on every visit to the tab.
|
|
local junk = "PK\3\4 not really a skin"
|
|
local id, err = TouchSkin.installArchive("bogus.zip", junk)
|
|
eq(id, nil, "a zip with no skin.lua or .cfg is refused")
|
|
check(tostring(err):find("bogus.zip", 1, true) ~= nil,
|
|
"and the error names the file")
|
|
eq(love.filesystem.read(TouchSkin.USER_ROOT .. "/bogus.zip"), nil,
|
|
"the rejected archive is cleaned up, not left to fail on every listing")
|
|
|
|
-- the id a good archive would land under is the file name without .zip
|
|
check(TouchSkin.find("bogus") == nil, "a refused archive lists nothing")
|
|
|
|
-- ------------------------------------------------------- launcher wiring
|
|
|
|
local view = read("src/import/LauncherView.lua")
|
|
local imp = read("src/import/RomImporter.lua")
|
|
|
|
check(view:find('id = "skins"', 1, true) ~= nil,
|
|
"LauncherView registers a skins tab")
|
|
check(view:find('id = "bug"', 1, true) ~= nil,
|
|
"LauncherView registers a bug tab")
|
|
check(view:find("drawSkinGlyph", 1, true) ~= nil,
|
|
"the skins tab draws its own glyph rather than shipping an asset")
|
|
check(view:find('assets/launcher/bug.png', 1, true) ~= nil,
|
|
"the bug tab uses the standard bug report asset")
|
|
-- the tab has to be next to Find, which is what the request was
|
|
local order = view:match("local HEADER_TABS = %{(.-)%}\n")
|
|
check(order ~= nil, "HEADER_TABS found")
|
|
if order then
|
|
local findAt = order:find('id = "find"', 1, true)
|
|
local skinsAt = order:find('id = "skins"', 1, true)
|
|
local bugAt = order:find('id = "bug"', 1, true)
|
|
check(findAt and skinsAt and skinsAt > findAt,
|
|
"the skins tab sits immediately after Find")
|
|
check(skinsAt and bugAt and bugAt > skinsAt,
|
|
"the bug tab sits after Skins")
|
|
end
|
|
check(view:find('imp.tab == "skins"', 1, true) ~= nil,
|
|
"the panel dispatch routes the skins tab")
|
|
check(view:find("buildSkinsPanel", 1, true) ~= nil, "and a panel builds it")
|
|
check(view:find('imp.tab == "bug"', 1, true) ~= nil,
|
|
"the panel dispatch routes the bug tab")
|
|
check(view:find("buildBugPanel", 1, true) ~= nil, "and the bug panel builds it")
|
|
check(view:find('Kit.toggle', 1, true) ~= nil,
|
|
"the bug panel uses a switch for safe mode")
|
|
check(view:find('bug-report', 1, true) ~= nil,
|
|
"the bug panel has a report action")
|
|
-- the panel must not offer the studio when the host did not supply it
|
|
check(view:find("if imp.onOpenSkinStudio then", 1, true) ~= nil,
|
|
"the Studio button is hidden without a host hook (mobile)")
|
|
-- the studio boots a game on Play, so it needs a cartridge, not the tab id
|
|
check(view:find("imp.modScope or \"red\"", 1, true) ~= nil,
|
|
"the studio is handed a real game version, never the skins tab id")
|
|
|
|
check(imp:find('if self.tab == "skins" then', 1, true) ~= nil,
|
|
"a dropped zip on the skins tab installs a skin")
|
|
check(imp:find("_installSkinZip", 1, true) ~= nil, "skin zip installer exists")
|
|
check(imp:find("_installMod", 1, true) ~= nil,
|
|
"and a zip elsewhere still installs a mod")
|
|
local RomImporter = require("src.import.RomImporter")
|
|
local GameVersion = require("src.core.GameVersion")
|
|
local cycled, probe = {}, nil
|
|
probe = setmetatable({ tab = GameVersion.ORDER[1] }, { __index = RomImporter })
|
|
probe._switchTab = function(self, id) self.tab = id; cycled[#cycled + 1] = id end
|
|
for _ = 1, #GameVersion.ORDER + 3 do RomImporter._cycleTab(probe, 1) end
|
|
local reached = " " .. table.concat(cycled, " ") .. " "
|
|
check(reached:find(" skins ", 1, true) ~= nil,
|
|
"shoulder-button tab cycling reaches the skins tab")
|
|
check(reached:find(" bug ", 1, true) ~= nil,
|
|
"shoulder-button tab cycling reaches the bug tab")
|
|
for _, id in ipairs(GameVersion.ORDER) do
|
|
if id ~= GameVersion.ORDER[1] then
|
|
check(reached:find(" " .. id .. " ", 1, true) ~= nil,
|
|
"shoulder-button tab cycling reaches " .. id)
|
|
end
|
|
end
|
|
local switch = imp:match("function RomImporter:_switchTab%(id%)(.-)\nend")
|
|
check(switch and switch:find("_ensureSkins(true)", 1, true) ~= nil,
|
|
"switching to the tab re-reads the skin list")
|
|
check(imp:find('function RomImporter:_safeModeEnabled', 1, true) ~= nil
|
|
and imp:find('function RomImporter:_toggleSafeMode', 1, true) ~= nil,
|
|
"the importer owns the safe mode state")
|
|
check(imp:find('function RomImporter:_reportIssue', 1, true) ~= nil,
|
|
"the importer owns issue report opening")
|
|
|
|
T.finish("launcher_skins_tab")
|