mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 21:35:42 -04:00
184 lines
6.9 KiB
Lua
184 lines
6.9 KiB
Lua
-- The SHADER FX preset picker: OFF, every .slangp ShaderFX.list() finds, then a
|
|
-- permanent DOWNLOAD SHADERS action row. `slot` picks which of ShaderFX.SLOTS
|
|
-- A activates and which save.options key persists it. See docs/shaderfx.md.
|
|
|
|
local ListMenu = require("src.ui.ListMenu")
|
|
local ShaderFX = require("src.render.ShaderFX")
|
|
local Strings = require("src.core.Strings")
|
|
local Screens = require("src.ui.Screens")
|
|
|
|
local ShaderFXScreen = setmetatable({}, { __index = ListMenu })
|
|
ShaderFXScreen.__index = ShaderFXScreen
|
|
|
|
-- OFF plus the preset's display name with its extension stripped.
|
|
local function label(entry)
|
|
if not entry then return Strings("OFF") end
|
|
return Strings((entry.name:gsub("%.slangp$", "")))
|
|
end
|
|
|
|
-- One row's display state from its entry.converted, shared by new() and
|
|
-- onChoose() so a successful convert updates a row exactly as building it did.
|
|
local function applyRowState(item, canConvert)
|
|
if not item.entry then return end
|
|
-- NOT `cond and a or b`: that idiom breaks when `a` is itself nil/false.
|
|
if item.entry.converted then
|
|
item.muted = false
|
|
item.right = nil
|
|
elseif canConvert then
|
|
item.muted = true
|
|
item.right = Strings("CONVERT")
|
|
else
|
|
item.muted = true
|
|
item.right = Strings("UPDATE")
|
|
end
|
|
end
|
|
|
|
-- OFF, every real preset, then the permanent DOWNLOAD SHADERS action row.
|
|
local function buildItems(active, canConvert)
|
|
local items = { { label = label(nil), entry = nil } }
|
|
local selected = 1
|
|
for _, entry in ipairs(ShaderFX.list()) do
|
|
local item = { label = label(entry), entry = entry }
|
|
applyRowState(item, canConvert)
|
|
items[#items + 1] = item
|
|
if active and active.name == entry.name then selected = #items end
|
|
end
|
|
items[#items + 1] = { label = Strings("DOWNLOAD SHADERS"), download = true }
|
|
return items, selected
|
|
end
|
|
|
|
-- `slot` defaults to "main" so a caller that still pushes this screen with no
|
|
-- argument keeps today's behavior rather than erroring on a nil slot key.
|
|
function ShaderFXScreen.new(game, slot)
|
|
slot = slot or "main"
|
|
local optKey = ShaderFX.OPTION_KEY[slot]
|
|
local title = (slot == "secondary") and "SHADER FX 2" or "SHADER FX"
|
|
local canConvert = ShaderFX.canConvert()
|
|
local items, selected = buildItems(ShaderFX.activeEntry(slot), canConvert)
|
|
|
|
local self = setmetatable(ListMenu.new(game, title, items, {
|
|
-- 7 rows leaves the title line and the one-line footer hint free.
|
|
rows = 7,
|
|
footer = Strings("SELECT:EDIT PARAMS"),
|
|
}), ShaderFXScreen)
|
|
-- Opens on the active preset (or OFF).
|
|
self.index = selected
|
|
self.downloadJob = nil
|
|
|
|
-- Rebuilds from ShaderFX.list() (e.g. after an install adds presets), keeping
|
|
-- the cursor on the same row index (clamped) rather than resetting to OFF.
|
|
local function refresh()
|
|
local newItems = buildItems(ShaderFX.activeEntry(slot), canConvert)
|
|
self.items = newItems
|
|
self.index = math.max(1, math.min(self.index, #newItems))
|
|
end
|
|
|
|
-- Polls the in-flight buildbot download alongside ListMenu's own input
|
|
-- handling. installDownloaded() itself is synchronous, a sub-second job.
|
|
local baseUpdate = ListMenu.update
|
|
self.update = function(self_, dt)
|
|
if self_.downloadJob then
|
|
local st = ShaderFX.downloadStatus(self_.downloadJob)
|
|
local row = self_.items[#self_.items]
|
|
if st.status == "pending" then
|
|
local pct = st.progress and (" %d%%"):format(st.progress * 100) or ""
|
|
row.right = Strings("...") .. pct
|
|
elseif st.status == "ok" then
|
|
local copied, err, unchanged = ShaderFX.installDownloaded(st.notModified)
|
|
self_.downloadJob = nil
|
|
if unchanged then
|
|
require("src.core.Logger").info("ShaderFXScreen: buildbot presets already up to date")
|
|
row.right = Strings("UP TO DATE")
|
|
elseif copied then
|
|
require("src.core.Logger").info("ShaderFXScreen: buildbot install copied %d files", copied)
|
|
refresh()
|
|
else
|
|
require("src.core.Logger").error("ShaderFXScreen: buildbot install failed: %s", tostring(err))
|
|
self_.items[#self_.items].right = Strings("FAILED")
|
|
end
|
|
else -- "error" or "cancelled"
|
|
self_.downloadJob = nil
|
|
require("src.core.Logger").error("ShaderFXScreen: buildbot download failed: %s", tostring(st.err))
|
|
row.right = Strings("FAILED")
|
|
end
|
|
end
|
|
baseUpdate(self_, dt)
|
|
end
|
|
|
|
self.onChoose = function(item)
|
|
if item.download then
|
|
-- Ignore a repeat press while one is already in flight.
|
|
if self.downloadJob then return end
|
|
self.downloadJob = ShaderFX.downloadPresets()
|
|
item.right = Strings("...")
|
|
return
|
|
end
|
|
|
|
-- A on an unconverted preset converts it in place instead of activating;
|
|
-- the screen stays open either way, since a convert is not a selection.
|
|
if item.entry and not item.entry.converted then
|
|
if not canConvert then
|
|
self.footer = Strings("Reinstall the app")
|
|
return
|
|
end
|
|
ShaderFX.clearBridgeQuarantine()
|
|
local ok, err = ShaderFX.convert(item.entry)
|
|
if not ok then
|
|
require("src.core.Logger").error("ShaderFXScreen: convert failed for %s: %s",
|
|
item.entry.name, tostring(err))
|
|
end
|
|
applyRowState(item, canConvert)
|
|
if not ok then item.right = Strings("FAILED") end
|
|
return
|
|
end
|
|
|
|
local opts = game.save and game.save.options
|
|
if not item.entry then
|
|
ShaderFX.deactivate(slot)
|
|
if opts then opts[optKey] = nil end
|
|
else
|
|
-- isConverted() is existence-only with no staleness check, so an explicit
|
|
-- selection always reconverts. Human-paced, CPU-only, never per frame.
|
|
if canConvert then
|
|
ShaderFX.clearBridgeQuarantine()
|
|
local convOk, convErr = ShaderFX.convert(item.entry)
|
|
if not convOk then
|
|
require("src.core.Logger").error("ShaderFXScreen: reconvert failed for %s: %s",
|
|
item.entry.name, tostring(convErr))
|
|
end
|
|
end
|
|
local overrides = opts and opts.shaderfxParams and opts.shaderfxParams[item.entry.name]
|
|
local ok, err = ShaderFX.activate(slot, item.entry, overrides)
|
|
if opts then opts[optKey] = ok and item.entry.name or nil end
|
|
if not ok then
|
|
require("src.core.Logger").error("ShaderFXScreen: activate failed for %s: %s",
|
|
item.entry.name, tostring(err))
|
|
item.right = Strings("FAILED")
|
|
if game.writeOptions then
|
|
game:writeOptions()
|
|
elseif game.persistOptions then
|
|
game:persistOptions()
|
|
end
|
|
return
|
|
end
|
|
end
|
|
if game.writeOptions then
|
|
game:writeOptions()
|
|
elseif game.persistOptions then
|
|
game:persistOptions()
|
|
end
|
|
self:close()
|
|
end
|
|
|
|
-- SELECT on a real, converted row opens its pragma-parameter editor; OFF, the
|
|
-- action row and an unconverted preset have nothing to read metadata from.
|
|
self.onSelectKey = function(item)
|
|
if not item.entry or item.download or not item.entry.converted then return end
|
|
Screens.push(game, "ShaderFXParamsScreen", item.entry)
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
return ShaderFXScreen
|