mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-06 09:05:48 -04:00
b305652fdb
Same decoded pixels, about 60 percent smaller caches. Bumps the FireRed/LeafGreen, Emerald and Ruby/Sapphire cache versions.
134 lines
5.5 KiB
Lua
134 lines
5.5 KiB
Lua
-- CPU-only preparation shared by the render thread and the asset worker.
|
|
local Pack = require("src.import.gba.native_pack")
|
|
local Palette = require("src.core.game3.palette")
|
|
local OwExtract = require("src.import.gba.ow_extract")
|
|
local CacheBlob = require("src.import.CacheBlob")
|
|
local D = {}
|
|
|
|
function D.imageData(rgba, w, h)
|
|
if not (love and love.image) then return nil, "love.image unavailable" end
|
|
local ok, data = pcall(love.image.newImageData, w, h, "rgba8", rgba)
|
|
if ok then return data end
|
|
data = love.image.newImageData(w, h)
|
|
local i = 1
|
|
for y = 0, h - 1 do
|
|
for x = 0, w - 1 do
|
|
data:setPixel(x, y, (rgba:byte(i) or 0) / 255, (rgba:byte(i + 1) or 0) / 255,
|
|
(rgba:byte(i + 2) or 0) / 255, (rgba:byte(i + 3) or 0) / 255)
|
|
i = i + 4
|
|
end
|
|
end
|
|
return data
|
|
end
|
|
|
|
function D.pair(cache, root, pair)
|
|
local path = root .. "/" .. pair
|
|
local blob, pals = cache:read(path .. "/mids.idx"), cache:read(path .. "/palettes.bin")
|
|
if not blob or not pals then return nil, "missing native blobs for " .. pair end
|
|
local idx, err = Pack.decodeIdx(blob)
|
|
if not idx then return nil, err end
|
|
local rgb, bgr = Palette.load(pals)
|
|
if not rgb then return nil, bgr end
|
|
local overBlob = cache:read(path .. "/mids_over.idx")
|
|
local out = { pair = pair, cols = idx.atlasCols, rows = idx.atlasRows, midCount = idx.midCount,
|
|
idxBlob = blob, bgr = bgr, midToSlot = {}, layers = {} }
|
|
for i, mid in ipairs(idx.midIds) do out.midToSlot[mid] = i - 1 end
|
|
local function layer(key, dataKey, indexed, source, tag, transparent)
|
|
local hash = Palette.hash(bgr, source)
|
|
local rel = path .. "/atlas_" .. tag .. "_" .. hash .. ".rgba"
|
|
local w, h = indexed.atlasCols * 16, indexed.atlasRows * 16
|
|
local rgba = cache:read(rel)
|
|
if not rgba or #rgba ~= w * h * 4 then
|
|
rgba = Pack.bakeRgba(indexed, rgb, { transparentZero = transparent, cancelled = cache.cancelled })
|
|
if not rgba then return nil, "asset preparation cancelled" end
|
|
if cache.write then pcall(cache.write, cache, rel, rgba) end
|
|
end
|
|
local data, why = D.imageData(rgba, w, h)
|
|
if not data then return nil, why end
|
|
out.layers[#out.layers + 1] = { imageKey = key, dataKey = dataKey, data = data, bytes = w * h * 4 }
|
|
return true
|
|
end
|
|
local ok, why = layer("image", "imageData", idx, blob, overBlob and "u" or "flat", false)
|
|
if not ok then return nil, why end
|
|
local over = overBlob and Pack.decodeIdx(overBlob)
|
|
if over then
|
|
ok, why = layer("overImage", "overImageData", over, overBlob, "o", true)
|
|
if not ok then return nil, why end
|
|
out.overBlob, out.layered = overBlob, true
|
|
end
|
|
if overBlob then
|
|
local midBlob = cache:read(path .. "/mids_mid.idx")
|
|
local mid = midBlob and Pack.decodeIdx(midBlob)
|
|
if mid then
|
|
ok, why = layer("midImage", "midImageData", mid, midBlob, "m", true)
|
|
if not ok then return nil, why end
|
|
end
|
|
end
|
|
out.animFiles = {}
|
|
local manifestRel = path .. "/anim_manifest.lua"
|
|
local manifestBlob = cache:read(manifestRel)
|
|
out.animFiles[manifestRel] = manifestBlob
|
|
local chunk = manifestBlob and load(manifestBlob, "@anim_manifest.lua", "t", {})
|
|
local man = chunk and chunk()
|
|
if man then
|
|
local function read(file)
|
|
if file then out.animFiles[path .. "/" .. file] = cache:read(path .. "/" .. file) end
|
|
end
|
|
out.animFiles[path .. "/palettes.bin"] = pals
|
|
for _, row in ipairs(man.banks or {}) do read(row.file); read(row.overFile) end
|
|
for _, kind in ipairs({ "water", "sand", "flower" }) do
|
|
if man[kind] then read("anim_" .. kind .. ".rgba") end
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
function D.sprite(cache, root, gid)
|
|
local metaBlob, rgba = cache:read(root .. "/" .. gid .. ".meta"), cache:read(root .. "/" .. gid .. ".rgba")
|
|
local meta = metaBlob and OwExtract.decodeMeta(metaBlob)
|
|
if not meta or not rgba then return nil, "missing OW sheet " .. tostring(gid) end
|
|
local w, h, n = meta.width, meta.height, meta.frameCount
|
|
if w < 1 or h < 1 or n < 1 or w > 256 or h > 256 or n > 512 then return nil, "bad OW dimensions" end
|
|
local bytes = w * h * n * 4
|
|
if #rgba ~= bytes then
|
|
if #rgba < w * h * 4 then return nil, "truncated OW sheet" end
|
|
rgba = rgba:sub(1, bytes) .. string.rep("\0", math.max(0, bytes - #rgba))
|
|
end
|
|
local data, err = D.imageData(rgba, w, h * n)
|
|
if not data then return nil, err end
|
|
meta.imageData, meta.bytes = data, bytes
|
|
return meta
|
|
end
|
|
|
|
-- Snapshots never consult a later GameVersion, cache prefix, or mod mount.
|
|
function D.cache(spec, cancelSignal)
|
|
local cache = { cancelled = cancelSignal and function() return cancelSignal:getCount() > 0 end or nil }
|
|
local function checkCancel()
|
|
if cache.cancelled and cache.cancelled() then error("asset preparation cancelled") end
|
|
end
|
|
local function path(rel) return (spec.prefix or "") .. rel end
|
|
function cache:read(rel)
|
|
checkCancel()
|
|
if spec.directory then
|
|
local f = io.open(spec.directory .. "/" .. path(rel), "rb")
|
|
if not f then return nil end
|
|
local bytes = f:read("*a"); f:close(); return CacheBlob.decode(rel, bytes)
|
|
end
|
|
return CacheBlob.readFs(path(rel))
|
|
end
|
|
function cache:write(rel, bytes)
|
|
checkCancel()
|
|
bytes = CacheBlob.encode(rel, bytes)
|
|
-- No directory creation or mounting in a worker; derived caches are optional.
|
|
if spec.directory then
|
|
local f = io.open(spec.directory .. "/" .. path(rel), "wb")
|
|
if not f then return false end
|
|
f:write(bytes); f:close(); return true
|
|
end
|
|
return love.filesystem.write(path(rel), bytes)
|
|
end
|
|
return cache
|
|
end
|
|
|
|
return D
|