skins and skin studio

This commit is contained in:
bryanthaboi
2026-08-17 06:47:33 -04:00
parent a5b674f9da
commit 3cca70608f
45 changed files with 4194 additions and 64 deletions
+82 -2
View File
@@ -1224,6 +1224,7 @@ function RomImporter.new(onComplete, opts)
forceImport = opts.forceImport or false,
onEditSave = opts.onEditSave,
onEditTouchControls = opts.onEditTouchControls,
onOpenSkinStudio = opts.onOpenSkinStudio,
isNX = isNX,
romImportMode = romImportMode,
mobileFileBridge = mobileFileBridge,
@@ -1784,7 +1785,12 @@ function RomImporter:filedropped(file)
-- readDroppedFile does here.
local name = file:getFilename() or ""
if name:lower():match("%.zip$") then
self:_installMod(file)
-- On the SKINS tab a zip is a skin; everywhere else it is a mod archive.
if self.tab == "skins" then
self:_installSkinZip(file)
else
self:_installMod(file)
end
return
end
-- A dropped .sav is a battery save: import it to a new slot for the active
@@ -2621,7 +2627,7 @@ function RomImporter:resumeAfterOverlay()
end
function RomImporter:_cycleTab(delta)
local order = { "red", "blue", "yellow", "gold", "mods", "find" }
local order = { "red", "blue", "yellow", "gold", "mods", "find", "skins" }
local idx = 1
for i, id in ipairs(order) do
if id == self.tab then idx = i; break end
@@ -2987,11 +2993,78 @@ function RomImporter:_switchTab(id)
self.tab = id
self._findSearchFocus = false
self:_disarmTextInput()
-- the skins list is cheap and can change behind the launcher's back
-- (an export, a hand-dropped folder), so re-read it on every visit
if id == "skins" then self:_ensureSkins(true) end
if GameVersion.VERSIONS[id] then
self:_setModScope(id)
end
end
-- ------- skins tab (touch skins + the desktop Skin Studio)
function RomImporter:_ensureSkins(force)
if self._skins and not force then return self._skins end
local TouchSkin = require("src.core.TouchSkin")
local out = {}
for _, entry in ipairs(TouchSkin.list()) do
local skin = TouchSkin.load(entry.root, entry.id)
local page = skin and skin.pages[1]
local controls = 0
for _, ctl in ipairs(page and page.controls or {}) do
if not ctl.decorative then controls = controls + 1 end
end
out[#out + 1] = {
id = entry.id,
source = entry.source,
pages = skin and #skin.pages or 0,
controls = controls,
screen = page ~= nil and page.viewport ~= nil,
ok = skin ~= nil,
}
end
self._skins = out
return out
end
function RomImporter:_activeSkin()
local opts = require("src.core.SaveData").loadOptions()
local tc = type(opts.touchControls) == "table" and opts.touchControls or {}
return tc.skin
end
function RomImporter:_useSkin(id)
local SaveData = require("src.core.SaveData")
local opts = SaveData.loadOptions()
local tc = type(opts.touchControls) == "table" and opts.touchControls or {}
tc.enabled = true
tc.skin = id
opts.touchControls = tc
SaveData.saveOptions(opts)
self._skinNotice = {
ok = true,
text = id and ("Now using " .. id) or "Now using the built-in pad",
}
end
function RomImporter:_installSkinZip(file)
local TouchSkin = require("src.core.TouchSkin")
local name = file:getFilename() or ""
local data, readError = readDroppedFile(file)
if not data then
self._skinNotice = { ok = false,
text = "Could not read the dropped file: " .. tostring(readError) }
return
end
local id, err = TouchSkin.installArchive(name, data)
self:_ensureSkins(true)
if not id then
self._skinNotice = { ok = false, text = "Import failed: " .. tostring(err) }
return
end
self._skinNotice = { ok = true, text = "Imported " .. id }
end
function RomImporter:_toggleFindSearchFocus()
self._findSearchFocus = not self._findSearchFocus
if self._findSearchFocus then
@@ -3018,6 +3091,13 @@ function RomImporter:_openSettings()
self.onEditTouchControls(version)
end
end
if self.onOpenSkinStudio then
local version = self.tab
hooks.openSkinStudio = function(skinId)
self:_closeSettings()
self.onOpenSkinStudio(version, skinId)
end
end
-- The tab the gear was opened on decides the row set: Gold reads a
-- different option block entirely, and offering it Gen 1's rows meant a
-- dozen controls that changed nothing (see LauncherSettings.gen2Rows).