Merge pull request #1498 from bryanthaboi/dev

more stuff
This commit is contained in:
bryanthaboi
2026-08-17 20:41:58 -04:00
committed by GitHub
21 changed files with 1130 additions and 109 deletions
+15
View File
@@ -94,6 +94,21 @@ return function(game)
imp:_useSkin(nil)
shot("skins_tab_imported.png")
-- import button: the picker hands back an absolute host path, not a drop
local picked = dir .. "/picked_skin.zip"
local pf = io.open(picked, "wb")
pf:write(raw)
pf:close()
imp:_installSkinZip(picked)
U.log("path import notice:", imp._skinNotice.text)
U.log("picked_skin found:", tostring(TouchSkin.find("picked_skin") ~= nil))
U.log("import label:", imp:_skinsImportButtonLabel())
shot("skins_tab_path_imported.png")
love.window.setMode(520, 760, { resizable = true, highdpi = true })
U.wait(3)
shot("skins_tab_narrow.png")
U.log("done")
love.event.quit()
while true do coroutine.yield() end
+51
View File
@@ -0,0 +1,51 @@
-- Driver: the studio's side of the alt-tab crash. Leaving the window mid-drag
-- must drop the drag and the queued click and still draw; main.lua's routing of
-- love.focus / love.visible / pad events while the studio owns the window is
-- pinned by tests/engine/skin_studio_image_import.lua (a driver run boots a
-- game, so main.lua's Studio branch is not live here).
-- POKEPORT_DRIVER=tests/drivers/skin_studio_focus_test.lua love .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Studio = require("src.ui.SkinStudio")
local TouchControls = require("src.core.TouchControls")
love.window.setMode(1280, 720, { resizable = true, highdpi = true })
U.wait(2)
Studio.load({ version = "red", skinId = "gb_anim", onClose = function() end })
U.wait(2)
U.log("skin:", Studio.skin and Studio.skin.id)
Studio.drag = { kind = "control-move", mx = 1, my = 1, bx = 0, by = 0,
bw = 10, bh = 10 }
Studio.clicked = true
local okFocus, errFocus = pcall(Studio.focus, false)
U.log("focus lost:", okFocus, errFocus or "")
U.log("drag:", tostring(Studio.drag), "click:", tostring(Studio.clicked))
local droppedDrag = Studio.drag == nil and Studio.clicked == false
-- a held test press must not survive the trip out of the window either
Studio.testing = true
TouchControls:setPreview(false)
TouchControls.held = TouchControls.held or {}
TouchControls.held.start = true
pcall(Studio.focus, false)
local held = next(TouchControls.held or {})
U.log("held after focus loss:", tostring(held))
local okVisible = pcall(Studio.visible, false)
U.log("minimize:", okVisible)
local okDraw, errDraw = pcall(Studio.draw)
U.log("draw after alt-tab:", okDraw, errDraw or "")
Studio.unload()
if droppedDrag and held == nil and okVisible and okDraw then
U.log("RESULT pass")
else
U.log("RESULT FAIL")
end
love.event.quit()
while true do coroutine.yield() end
end
+120
View File
@@ -0,0 +1,120 @@
-- Driver: imports a bezel image into a fresh skin through the studio's Import
-- button and shoots the result, with the native dialog stubbed out (the real
-- one blocks on osascript / zenity / PowerShell). Also covers button art and
-- the drag-and-drop path.
-- SHOT_DIR=/tmp/studioimport POKEPORT_DRIVER=tests/drivers/skin_studio_import_shot.lua love .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Studio = require("src.ui.SkinStudio")
local FilePicker = require("src.core.FilePicker")
local TouchSkin = require("src.core.TouchSkin")
local dir = os.getenv("SHOT_DIR") or "/tmp/studioimport"
os.execute('mkdir -p "' .. dir .. '" 2>/dev/null')
love.window.setMode(1440, 900, { resizable = true, highdpi = true })
U.wait(2)
local pending = nil
love.draw = function()
Studio.draw()
if pending then
local path = pending
pending = nil
love.graphics.captureScreenshot(function(imagedata)
local f = io.open(path, "wb")
if f then f:write(imagedata:encode("png"):getString()) f:close() end
end)
end
end
local function shot(name)
pending = dir .. "/" .. name
for _ = 1, 90 do
if not pending then break end
coroutine.yield()
end
U.wait(3)
local f = io.open(dir .. "/" .. name, "rb")
U.log(f and "shot" or "FAIL shot", name)
if f then f:close() end
end
Studio.load({ version = "red", onClose = function() end })
U.wait(3)
-- start from New so the run does not depend on the saved skin choice
Studio.skin = TouchSkin.newSkin("import_probe")
Studio.skinIdField = "import_probe"
Studio.pageIndex, Studio.selected, Studio.images = 1, nil, {}
U.log("skin:", Studio.skin.id, "bezel:", tostring(Studio.page().imagePath))
-- what the native dialog would hand back
local picked = "assets/skins/gb_anim/img/gb_back.png"
local realOpen = FilePicker.open
FilePicker.open = function(prompt, kind)
U.log("picker prompt:", prompt, "exts:", table.concat(kind.exts, "/"))
return picked
end
Studio.importImageFile("bezel")
U.log("status:", tostring(Studio.status))
U.log("bezel now:", tostring(Studio.page().imagePath),
"loaded:", tostring(Studio.page().image ~= nil))
U.log("skin root:", Studio.skin.root)
U.log("file on disk:",
tostring(love.filesystem.getInfo(Studio.skin.root .. "/img/gb_back.png") ~= nil))
local viewport = Studio.page().viewport
U.log("viewport left alone:", viewport and
("%.3f %.3f %.3f %.3f"):format(viewport.x, viewport.y, viewport.w, viewport.h)
or "none")
shot("import_bezel.png")
-- button art goes onto the selected control, not the page
Studio.addControl()
FilePicker.open = function() return "assets/skins/gb_anim/img/gbc_a.png" end
Studio.importImageFile("idle")
local ctl = Studio.selectedControl()
U.log("idle art:", tostring(ctl.imagePath),
"bezel untouched:", tostring(Studio.page().imagePath))
FilePicker.open = function() return "assets/skins/gb_anim/img/gbc_b.png" end
Studio.importImageFile("pressed")
U.log("pressed art:", tostring(ctl.pressedImagePath))
shot("import_button_art.png")
-- cancelling the dialog changes nothing
local before = Studio.page().imagePath
FilePicker.open = function() return nil end
Studio.importImageFile("bezel")
U.log("after cancel:", tostring(Studio.page().imagePath),
"unchanged:", tostring(before == Studio.page().imagePath))
-- the drop path shares the import, and refuses non-art
Studio.imageTarget = "bezel"
local raw = love.filesystem.read("assets/skins/tv_crt/img/tv-integer.png")
Studio.filedropped({
getFilename = function() return "/tmp/tv_frame.png" end,
open = function() return true end,
read = function() return raw end,
close = function() return true end,
})
U.log("dropped bezel:", tostring(Studio.page().imagePath))
Studio.filedropped({
getFilename = function() return "/tmp/notes.txt" end,
open = function() return true end,
read = function() return "nope" end,
close = function() return true end,
})
U.log("after dropping a txt:", tostring(Studio.page().imagePath),
"status:", tostring(Studio.status))
shot("import_dropped.png")
love.window.setMode(760, 900, { resizable = true, highdpi = true })
U.wait(3)
shot("import_narrow.png")
FilePicker.open = realOpen
local saved = TouchSkin.find("import_probe")
U.log("skin discoverable:", tostring(saved ~= nil))
Studio.unload()
U.log("done")
love.event.quit()
while true do coroutine.yield() end
end
@@ -0,0 +1,68 @@
-- The museum 1F ticket clerk's take-your-time/not-enough-money/come-again
-- lines used to be bare English literals, invisible to game.data.text no
-- matter what a translation mod put there. tests/engine/museum_money_box_
-- bug1335.lua only ever runs with an empty game.data.text, so it can't
-- tell a properly-wired t._Key or "..." fallback apart from a literal that
-- never looked at t at all -- every assertion there passes either way.
-- This test populates game.data.text with translated values and checks
-- they actually reach the pushed TextBox, for all three lines.
package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.harness")
package.loaded["src.render.TextBox"] = {
new = function(_, text, onDone, opts)
return { text = text, onDone = onDone, opts = opts }
end,
}
local M = assert(loadfile("data/scripts/story2.lua"))()
local clerk = M.MUSEUM_1F.talk.TEXT_MUSEUM1F_SCIENTIST1
local TRANSLATED = {
_Museum1FScientist1TakePlentyOfTimeText = "Prends ton temps\net profite bien !",
_Museum1FScientist1DontHaveEnoughMoneyText = "Tu n'as pas assez\nd'argent.",
_Museum1FScientist1ComeAgainText = "Reviens vite !",
}
local pushed
local function mkGame(cash)
pushed = {}
return {
data = { text = TRANSLATED },
save = { money = cash, flags = {} },
stack = { push = function(_, box) pushed[#pushed + 1] = box end },
}
end
-- already ticketed: take-your-time line
local g = mkGame(3000)
g.save.flags.EVENT_BOUGHT_MUSEUM_TICKET = true
clerk(g, nil, nil, function() end)
T.eq(pushed[1].text, TRANSLATED._Museum1FScientist1TakePlentyOfTimeText,
"an existing ticket holder gets the translated take-your-time line")
-- YES but short on cash: not-enough-money line
g = mkGame(20)
clerk(g, nil, nil, function() end)
pushed[1].opts.choice(true)
T.eq(pushed[2].text, TRANSLATED._Museum1FScientist1DontHaveEnoughMoneyText,
"short on cash opens the translated not-enough-money box")
-- NO: come-again line
g = mkGame(3000)
clerk(g, nil, nil, function() end)
pushed[1].opts.choice(false)
T.eq(pushed[2].text, TRANSLATED._Museum1FScientist1ComeAgainText,
"declining opens the translated come-again box")
-- unpopulated game.data.text still falls back to the English literal
g = { data = { text = {} }, save = { money = 3000, flags = {} },
stack = { push = function(_, box) pushed[#pushed + 1] = box end } }
pushed = {}
g.save.flags.EVENT_BOUGHT_MUSEUM_TICKET = true
clerk(g, nil, nil, function() end)
T.check(tostring(pushed[1].text):find("Take your time", 1, true) ~= nil,
"an empty catalog still falls back to the English take-your-time line")
T.finish("museum_1f_clerk_translation_test")
+118
View File
@@ -0,0 +1,118 @@
-- Skins tab Import button: picker plumbing + path/drop install (SKINIMP-01..05).
package.path = "./?.lua;./?/init.lua;" .. package.path
if not _G.love then _G.love = require("tests.love_stub") end
local S = require("tests.harness").suite("skin import picker")
local eq = S.eq
local check = S.check
local RomImporter = require("src.import.RomImporter")
local TouchSkin = require("src.core.TouchSkin")
love.system = love.system or {}
local saved = {
pickFile = love.system.pickFile,
getPickedFile = love.system.getPickedFile,
getPickError = love.system.getPickError,
installArchive = TouchSkin.installArchive,
}
local installed
TouchSkin.installArchive = function(name, data)
installed = { name = name, data = data }
if not data or data == "" then return nil, "empty archive" end
return (name:match("([^/\\]+)$") or name):gsub("%.[Zz][Ii][Pp]$", "")
end
local function freshImporter(fields)
local imp = RomImporter.new(function() end, { launcher = true })
imp.tab = "skins"
imp._skins = {}
imp._ensureSkins = function(self) return self._skins end
for k, v in pairs(fields or {}) do imp[k] = v end
return imp
end
-- SKINIMP-01: a picker path installs as a skin, not a mod
installed = nil
local imp = freshImporter()
local tmp = os.tmpname() .. ".zip"
local f = assert(io.open(tmp, "wb"))
f:write("PK\3\4skin-archive")
f:close()
imp:_installSkinZip(tmp)
os.remove(tmp)
check(installed ~= nil, "an absolute picker path reaches TouchSkin.installArchive")
eq(installed.data, "PK\3\4skin-archive", "the picked file's bytes are installed")
check(imp._skinNotice and imp._skinNotice.ok, "a good pick reports success")
-- SKINIMP-02: a dropped file still installs, and both paths land on the tab
installed = nil
imp = freshImporter({ tab = "red" })
imp:filedropped({
getFilename = function() return "/tmp/dropped_skin.zip" end,
getSize = function() return 4 end,
open = function() return true end,
read = function() return "PKZP" end,
close = function() return true end,
})
check(installed == nil, "a zip dropped off the skins tab is still a mod")
installed = nil
imp = freshImporter()
imp:filedropped({
getFilename = function() return "/tmp/dropped_skin.zip" end,
getSize = function() return 4 end,
open = function() return true end,
read = function() return "PKZP" end,
close = function() return true end,
})
eq(installed and installed.name, "/tmp/dropped_skin.zip",
"a zip dropped on the skins tab installs as a skin")
eq(imp.tab, "skins", "the skin install stays on the skins tab")
-- SKINIMP-03: the mobile bridge borrows the mod picker kind
local requestedKind
love.system.pickFile = function(kind)
requestedKind = kind
return true
end
love.system.getPickedFile = function() return nil end
love.system.getPickError = function() return nil end
imp = freshImporter({ nativePicker = true })
imp:chooseSkin()
eq(requestedKind, "mod", "chooseSkin opens the .zip picker")
eq(imp.pickerPendingKind, "skin", "the pending pick is routed to the skins tab")
-- SKINIMP-04: the picked file comes back through update() as a skin
installed = nil
love.system.getPickedFile = function()
love.system.getPickedFile = function() return nil end
return "/picked/from_bridge.zip"
end
imp._installSkinZip = function(self, source)
self.installedSource = source
self._skinNotice = { ok = true, text = "Imported from_bridge" }
end
imp:update(0)
eq(imp.installedSource, "/picked/from_bridge.zip",
"update() hands the picked path to the skin installer")
eq(imp.pickerPendingKind, nil, "the pending kind clears once consumed")
-- SKINIMP-05: a picker error lands on the skins notice, not the mods one
love.system.getPickedFile = function() return nil end
love.system.getPickError = function()
love.system.getPickError = function() return nil end
return "picker refused"
end
imp = freshImporter({ nativePicker = true, pickerPendingKind = "skin" })
imp:update(0)
check(imp._skinNotice and not imp._skinNotice.ok,
"a failed skin pick reports on the skins tab")
eq(imp.modNotice, nil, "a failed skin pick leaves the mods notice alone")
love.system.pickFile = saved.pickFile
love.system.getPickedFile = saved.getPickedFile
love.system.getPickError = saved.getPickError
TouchSkin.installArchive = saved.installArchive
S.finish()
+137
View File
@@ -0,0 +1,137 @@
-- Skin Studio art import: the native image picker behind the "Import" buttons
-- (bezel / idle / pressed), and the alt-tab crash that took the studio down --
-- love.focus fell through to Game:focus with no game booted.
-- luajit tests/engine/skin_studio_image_import.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 FilePicker = require("src.core.FilePicker")
local TouchSkin = require("src.core.TouchSkin")
local Studio = require("src.ui.SkinStudio")
local function session()
Studio.skin = TouchSkin.newSkin("t")
Studio.skinIdField = "t"
Studio.pageIndex = 1
Studio.selected = nil
Studio.canvasIndex = 1
Studio.drag = nil
Studio.dirty = false
Studio.status = nil
Studio.testing = false
Studio.images = {}
Studio.imageTarget = "idle"
return Studio.skin
end
-- ------------------------------------------------------------ file picker
check(FilePicker.matches("bezel.png", FilePicker.IMAGE), "png is art")
check(FilePicker.matches("BEZEL.PNG", FilePicker.IMAGE), "case does not matter")
check(FilePicker.matches("shot.jpg", FilePicker.IMAGE), "jpg is art")
check(FilePicker.matches("shot.jpeg", FilePicker.IMAGE), "jpeg is art")
check(not FilePicker.matches("skin.zip", FilePicker.IMAGE), "a zip is not art")
eq(FilePicker.basename("/home/me/art/bezel.png"), "bezel.png", "posix basename")
eq(FilePicker.basename("C:\\Users\\me\\bezel.png"), "bezel.png", "windows basename")
eq(FilePicker.basename("bezel.png"), "bezel.png", "a bare name is its own basename")
-- ----------------------------------------------------------- import target
session()
Studio.addControl()
local ctl = Studio.selectedControl()
check(Studio.adoptImage("bezel.png", "PNGDATA", "bezel"), "bezel import succeeds")
eq(Studio.page().imagePath, "img/bezel.png", "bezel art lands on the page")
eq(ctl.imagePath, nil, "and not on the selected control")
check(Studio.dirty, "importing marks the skin dirty")
check(Studio.status:find("bezel", 1, true) ~= nil, "the status names the target")
check(Studio.adoptImage("a_button.png", "PNGDATA", "idle"), "idle import succeeds")
eq(ctl.imagePath, "img/a_button.png", "idle art lands on the control")
eq(Studio.page().imagePath, "img/bezel.png", "and leaves the bezel alone")
check(Studio.adoptImage("a_down.png", "PNGDATA", "pressed"), "pressed import succeeds")
eq(ctl.pressedImagePath, "img/a_down.png", "pressed art lands on the control")
eq(ctl.imagePath, "img/a_button.png", "idle art survives")
check(not Studio.adoptImage("notes.txt", "junk", "bezel"), "non-art is refused")
eq(Studio.page().imagePath, "img/bezel.png", "and changes nothing")
-- the imported files are listed for the cycle buttons
local listed = {}
for _, rel in ipairs(Studio.images or {}) do listed[rel] = true end
check(listed["img/bezel.png"], "the imported bezel joins the image list")
-- -------------------------------------------------------- picker plumbing
local realOpen, realRead = FilePicker.open, FilePicker.read
local asked
FilePicker.open = function(prompt, kind)
asked = { prompt = prompt, kind = kind }
return "/tmp/some folder/frame.png"
end
FilePicker.read = function() return "PNGDATA" end
session()
Studio.importImageFile("bezel")
check(asked ~= nil, "the bezel button opens a picker")
eq(asked.kind, FilePicker.IMAGE, "and asks for images, not archives")
check(asked.prompt:lower():find("bezel", 1, true) ~= nil, "with a bezel prompt")
eq(Studio.page().imagePath, "img/frame.png", "the pick becomes the bezel")
eq(Studio.imageTarget, "bezel", "and the target sticks for the cycle button")
-- no control selected: an art import says so instead of writing to the page
session()
asked = nil
Studio.importImageFile("idle")
check(asked == nil, "no picker opens without a control to paint")
eq(Studio.page().imagePath, nil, "and the bezel is not overwritten")
check(Studio.status ~= nil, "the studio explains why")
-- a cancelled dialog leaves the skin exactly as it was
session()
FilePicker.open = function() return nil end
Studio.importImageFile("bezel")
eq(Studio.page().imagePath, nil, "cancelling imports nothing")
check(not Studio.dirty, "and does not dirty the skin")
FilePicker.open, FilePicker.read = realOpen, realRead
-- --------------------------------------------------------- focus / alt-tab
session()
Studio.drag = { kind = "control-move", mx = 1, my = 1, bx = 0, by = 0, bw = 1, bh = 1 }
Studio.clicked = true
Studio.focus(false)
eq(Studio.drag, nil, "losing focus drops the in-flight drag")
eq(Studio.clicked, false, "and the queued click")
check(pcall(Studio.visible, false), "minimizing is survivable too")
-- main.lua used to hand focus / visibility / pad events straight to Game while
-- the studio owned the window, and the launcher has no Game -- alt-tab took the
-- app down with "attempt to index a nil value (global 'Game')".
local f = assert(io.open("main.lua", "r"))
local src = f:read("*a")
f:close()
local checked = 0
for name, body in ("\n" .. src):gmatch("\nfunction love%.([%w_]+)%b()\n(.-)\nend\n") do
if body:find("Game:") then
checked = checked + 1
check(body:find("Studio") ~= nil,
"love." .. name .. " checks Studio before dispatching to Game")
check(body:find("not Game then") ~= nil
or body:find("if Game then") ~= nil
or body:find("Game and ") ~= nil,
"love." .. name .. " tolerates a nil Game (launcher / studio session)")
end
end
check(checked >= 10, "the handler scan actually found handlers (" .. checked .. ")")
check(src:find("Studio.focus", 1, true) ~= nil, "love.focus routes to the studio")
check(src:find("Studio.visible", 1, true) ~= nil, "so does love.visible")
T.finish("skin_studio_image_import")
+5
View File
@@ -490,6 +490,11 @@ check("hurt is easier to catch", hurtRate > fullRate, true)
local asleepRate = Catching.rate({
maxHp = 60, hp = 1, catchRate = 45, ball = "POKE_BALL", status = "sleep" })
check("sleep adds 10", asleepRate, math.min(255, hurtRate + 10))
checkNear("preview converts the exact byte roll", Catching.chance({
maxHp = 60, hp = 60, catchRate = 45, ball = "POKE_BALL" }),
fullRate * 100 / 256, 0.000001)
check("master ball preview is certain", Catching.chance({
maxHp = 60, hp = 60, catchRate = 1, ball = "MASTER_BALL" }), 100)
-- The cart's bug: burn/poison/paralysis add nothing.
check("poison adds nothing (cart bug)", Catching.rate({
maxHp = 60, hp = 1, catchRate = 45, ball = "POKE_BALL",
+10
View File
@@ -282,6 +282,16 @@ local function runToMenu(screen, cap)
return false
end
do
local screen = newScreen({ inventory = {
MASTER_BALL = 1, POKE_BALL = 1,
} })
eq(screen:catchChance("MASTER_BALL"), 100,
"the battle screen exposes a certain Master Ball preview")
check(type(screen:catchChance("POKE_BALL")) == "number",
"the battle screen exposes the live wild catch preview")
end
-- ---- BattleMenu empties the textbox ---------------------------------------
do
local screen = newScreen()
+13 -1
View File
@@ -163,14 +163,23 @@ function screen2:chooseMove(slot)
return true
end
function screen2:cancelMove() self.phase = "menu" return true end
function screen2:catchChance(ball)
return ball == "MASTER_BALL" and 100 or 37.5
end
local game2 = {
data = {
pokemon = { CHIKORITA = { name = "CHIKORITA" },
RATTATA = { name = "RATTATA" } },
moves = { TACKLE = { name = "TACKLE", type = "NORMAL",
power = 35, accuracy = 95, pp = 35 } },
items = {
MASTER_BALL = { name = "MASTER BALL", pocket = "BALL" },
POTION = { name = "POTION", pocket = "ITEM" },
},
},
save = { party = { player2 } }, stack = { states = { screen2 } },
save = { party = { player2 }, inventory = {
MASTER_BALL = 1, POTION = 2,
} }, stack = { states = { screen2 } },
}
local api2 = require("src.battle.gen2.BattleAPI").new(game2)
@@ -179,6 +188,9 @@ check(snapshot2 and snapshot2.kind == "wild" and snapshot2.prompt == "menu",
"Gold battle is discovered through its screen id")
eq(snapshot2.player.maxHp, 21, "Gold max HP uses the mon field")
eq(snapshot2.moves[1].name, "TACKLE", "Gold moves are copied")
eq(#snapshot2.items, 1, "Gold exposes balls without guessing targeted items")
eq(snapshot2.items[1].catchChance, 100,
"Gold ball records expose the exact catch preview")
snapshot2.player.hp = 0
snapshot2.moves[1].pp = 0
eq(player2.hp, 20, "changing a snapshot cannot change a Gold Pokemon")
@@ -166,6 +166,47 @@ eq(ri._requiredImported.importId, "source", "focus routes to the pending declara
check(love.filesystem.getInfo("picked_required_import.bin") == nil,
"focus removes the staged required-file pick")
-- Android releases with the updated launcher but the older native bridge do
-- not advertise required_import. They still support the established ROM SAF
-- picker, whose result must be quarantined to the pending dependency request.
love.system.pickFileKinds = function() return "rom,mod,sav" end
pickCalls = {}
ri = freshImporter({ red = true, blue = true })
ri.nativePicker = true
ri.mobileFileBridge = true
ri.mods = { {
id = "needs_source",
manifest = { id = "needs_source", name = "Needs Source",
required_imports = { { id = "source", name = "Source", file = "source.bin",
format = "raw", md5 = { "00000000000000000000000000000000" } } } },
} }
ri:chooseRequiredImport("needs_source", "source")
eq(pickCalls[1], "rom", "legacy Android bridge falls back to its ROM SAF picker")
check(ri.requiredImportLegacyRomPick,
"legacy Android ROM picker result is marked as a required import")
ri._importRequiredSource = function(self, modId, importId, source)
self._requiredImported = { modId = modId, importId = importId, source = source }
return true
end
love.filesystem.write("picked_rom.gb", "source bytes")
ri:focus(true)
eq(ri._requiredImported.source, "picked_rom.gb",
"legacy Android ROM staging name is routed to the required import")
eq(ri._requiredImported.modId, "needs_source",
"legacy Android picker preserves the requested mod")
check(love.filesystem.getInfo("picked_rom.gb") == nil,
"legacy Android dependency pick is removed after import")
-- A legacy bridge reports a failed copy using that same staging basename; it
-- must stay on the dependency page rather than becoming a game-ROM error.
ri:chooseRequiredImport("needs_source", "source")
love.filesystem.write("pick_error.flag", "picked_rom.gb")
ri:focus(true)
check(ri.modNotice ~= nil and ri.modNotice.ok == false,
"legacy Android picker errors are shown as dependency import errors")
check(ri.pickerPendingKind == nil,
"legacy Android picker error clears the pending dependency request")
love.system.getOS = saved.getOS
love.system.pickFile = saved.pickFile
love.system.pickFileKinds = saved.pickFileKinds
@@ -174,5 +215,6 @@ love.filesystem.remove("usb_mod.zip")
love.filesystem.remove("picked_mod.zip")
love.filesystem.remove("picked_save.sav")
love.filesystem.remove("picked_required_import.bin")
love.filesystem.remove("picked_rom.gb")
S.finish()