Merge pull request #1420 from anxiousintrovert/fix/required-import-platform-pickers

Fix required imports on Android legacy picker bridges
This commit is contained in:
bryanthaboi
2026-08-17 19:58:13 -04:00
committed by GitHub
2 changed files with 74 additions and 5 deletions
+32 -5
View File
@@ -988,8 +988,18 @@ local function pickerHasKind(kind)
return false return false
end end
local function findPendingRequiredImport() local function findPendingRequiredImport(self)
local names = { "picked_required_import.bin", "picked_stadium.z64" } local names = { "picked_required_import.bin", "picked_stadium.z64" }
-- Builds released before required_import was added to the Android JNI bridge
-- only understand the long-standing "rom" picker kind. While a required
-- import request is in flight, it is safe to treat its staging name as a
-- dependency file: the pending IDs below select the same validation/copy
-- path as a current bridge. Never scan picked_rom.gb otherwise, since that
-- remains reserved for an ordinary game-ROM import.
if self and self.requiredImportLegacyRomPick
and self.pickerPendingKind == "required_import" then
names[#names + 1] = "picked_rom.gb"
end
for _, name in ipairs(names) do for _, name in ipairs(names) do
if love.filesystem.getInfo(name, "file") then return name end if love.filesystem.getInfo(name, "file") then return name end
end end
@@ -1471,12 +1481,16 @@ function RomImporter:focus(f)
local text = "Could not read the picked file. Reopen the picker and choose " local text = "Could not read the picked file. Reopen the picker and choose "
.. "it with the Files (Documents) app, or copy it into: " .. "it with the Files (Documents) app, or copy it into: "
.. love.filesystem.getSaveDirectory() .. love.filesystem.getSaveDirectory()
local legacyRequiredPick = self.requiredImportLegacyRomPick
and self.pickerPendingKind == "required_import"
if pickError:find("picked_required_import", 1, true) if pickError:find("picked_required_import", 1, true)
or pickError:find("picked_stadium", 1, true) then or pickError:find("picked_stadium", 1, true)
or (legacyRequiredPick and pickError:find("picked_rom", 1, true)) then
self.modNotice = { ok = false, text = text } self.modNotice = { ok = false, text = text }
self.pickerPendingKind = nil self.pickerPendingKind = nil
self.pickerPendingModId = nil self.pickerPendingModId = nil
self.pickerPendingImportId = nil self.pickerPendingImportId = nil
self.requiredImportLegacyRomPick = nil
elseif pickError:find("picked_mod", 1, true) then elseif pickError:find("picked_mod", 1, true) then
self.modNotice = { ok = false, text = text } self.modNotice = { ok = false, text = text }
elseif pickError:find("picked_save", 1, true) then elseif pickError:find("picked_save", 1, true) then
@@ -1488,11 +1502,12 @@ function RomImporter:focus(f)
end end
return return
end end
local requiredName = findPendingRequiredImport() local requiredName = findPendingRequiredImport(self)
if requiredName then if requiredName then
local modId, importId = self.pickerPendingModId, self.pickerPendingImportId local modId, importId = self.pickerPendingModId, self.pickerPendingImportId
self.pickerPendingKind = nil self.pickerPendingKind = nil
self.pickerPendingModId, self.pickerPendingImportId = nil, nil self.pickerPendingModId, self.pickerPendingImportId = nil, nil
self.requiredImportLegacyRomPick = nil
local imported = modId and importId local imported = modId and importId
and self:_importRequiredSource(modId, importId, requiredName) and self:_importRequiredSource(modId, importId, requiredName)
consumePick(self, requiredName, requiredName, imported) consumePick(self, requiredName, requiredName, imported)
@@ -2024,7 +2039,17 @@ function RomImporter:chooseRequiredImport(modId, importId)
return return
end end
if self.nativePicker then if self.nativePicker then
if self.mobileFileBridge and not pickerHasKind("required_import") then -- Android 13+ uses the Storage Access Framework for both paths. Some
-- Android 15 installs carry the newer Lua launcher with an older native
-- bridge, however, so they do not advertise required_import yet. Fall
-- back to that bridge's known "rom" picker and quarantine its result by
-- the pending required-import IDs. iOS has a different asynchronous
-- bridge and deliberately keeps the explicit capability requirement.
local legacyAndroidPicker = self.mobileFileBridge
and love.system.getOS() == "Android"
and not pickerHasKind("required_import")
if self.mobileFileBridge and not pickerHasKind("required_import")
and not legacyAndroidPicker then
requiredImportNotice(self, modId, importId, requiredImportNotice(self, modId, importId,
"This app build cannot pick required mod files yet. Update the app and try again.") "This app build cannot pick required mod files yet. Update the app and try again.")
self.modNotice = nil self.modNotice = nil
@@ -2033,10 +2058,12 @@ function RomImporter:chooseRequiredImport(modId, importId)
self.pickerPendingKind = "required_import" self.pickerPendingKind = "required_import"
self.pickerPendingModId = modId self.pickerPendingModId = modId
self.pickerPendingImportId = importId self.pickerPendingImportId = importId
if not pickFile("required_import") then self.requiredImportLegacyRomPick = legacyAndroidPicker or nil
if not pickFile(legacyAndroidPicker and "rom" or "required_import") then
self.pickerPendingKind = nil self.pickerPendingKind = nil
self.pickerPendingModId = nil self.pickerPendingModId = nil
self.pickerPendingImportId = nil self.pickerPendingImportId = nil
self.requiredImportLegacyRomPick = nil
requiredImportNotice(self, modId, importId, "Could not open the file picker.") requiredImportNotice(self, modId, importId, "Could not open the file picker.")
self.modNotice = nil self.modNotice = nil
elseif self.android then elseif self.android then
@@ -166,6 +166,47 @@ eq(ri._requiredImported.importId, "source", "focus routes to the pending declara
check(love.filesystem.getInfo("picked_required_import.bin") == nil, check(love.filesystem.getInfo("picked_required_import.bin") == nil,
"focus removes the staged required-file pick") "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.getOS = saved.getOS
love.system.pickFile = saved.pickFile love.system.pickFile = saved.pickFile
love.system.pickFileKinds = saved.pickFileKinds 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_mod.zip")
love.filesystem.remove("picked_save.sav") love.filesystem.remove("picked_save.sav")
love.filesystem.remove("picked_required_import.bin") love.filesystem.remove("picked_required_import.bin")
love.filesystem.remove("picked_rom.gb")
S.finish() S.finish()