feat: enhance Goldenrod Game Corner graphics extraction

Added functions to write raw graphics assets for the Slot Machine and Card Flip games, ensuring that the necessary files are generated when the corresponding symbols are present in the ROM. Updated the manifest to include required symbols for these assets, preventing fallback to labelled cells when graphics are missing.
This commit is contained in:
1jamie
2026-08-20 20:12:41 -05:00
parent f5b8b6c85f
commit 5714555847
5 changed files with 133 additions and 87 deletions
+40 -18
View File
@@ -5258,9 +5258,26 @@ function RomExtractorGen2:extractMenuGfx()
end
-- Goldenrod Game Corner: Slot Machine graphics assets
local CacheFs = require("src.import.CacheFs")
local function packBytes(bytes)
local chars = {}
for i = 1, #bytes do chars[i] = string.char(bytes[i]) end
return table.concat(chars)
end
local function writeRaw(relative, bytes)
local ok, writeError = CacheFs.write(
"assets/generated/" .. relative, packBytes(bytes))
if not ok then
error("could not write " .. relative .. ": " .. tostring(writeError))
end
end
local slots = nil
if self.symbols["Slots1LZ"] then
local raw1 = self:decompressLz3Symbol("Slots1LZ")
self:write2bpp(raw1, 16, #raw1 / 4, "slots/gold_slots_1.png")
slots = slots or {}
slots.sheet1 = "assets/generated/slots/gold_slots_1.png"
end
if self.symbols["Slots2LZ"] then
local raw2 = self:decompressLz3Symbol("Slots2LZ")
@@ -5269,6 +5286,8 @@ function RomExtractorGen2:extractMenuGfx()
raw2[i] = bit.band(bit.bnot(raw2[i]), 0xFF)
end
self:write2bpp(raw2, 16, #raw2 / 4, "slots/gold_slots_2.png")
slots = slots or {}
slots.sheet2 = "assets/generated/slots/gold_slots_2.png"
end
if self.symbols["Slots3LZ"] then
local raw3 = self:decompressLz3Symbol("Slots3LZ")
@@ -5283,55 +5302,58 @@ function RomExtractorGen2:extractMenuGfx()
-- Y=192: Chansey 5 (Egg Drop pose, 24x32)
-- Y=224: Egg (8x16 at X=0)
self:write2bpp(raw3, 24, #raw3 / 6, "slots/gold_slots_actors.png", true)
slots = slots or {}
slots.sheet3 = "assets/generated/slots/gold_slots_3.png"
end
if self.symbols["SlotsTilemap"] then
local symbol = self:symbol("SlotsTilemap")
local tm = self.rom:bytes(symbol.bank, symbol.address, 20 * 12)
self:save(tm, "slots/gold_slots.tilemap")
writeRaw("slots/gold_slots.tilemap", tm)
slots = slots or {}
slots.tilemap = "assets/generated/slots/gold_slots.tilemap"
end
if slots then out.slots = slots end
-- Goldenrod Game Corner: Card Flip graphics assets
local cardFlip = nil
if self.symbols["CardFlipLZ01"] then
local raw1 = self:decompressLz3Symbol("CardFlipLZ01")
self:write2bpp(raw1, 128, #raw1 / 32, "card_flip/card_flip_1.png")
cardFlip = cardFlip or {}
cardFlip.sheet1 = "assets/generated/card_flip/card_flip_1.png"
end
if self.symbols["CardFlipLZ02"] then
local raw2 = self:decompressLz3Symbol("CardFlipLZ02")
self:write2bpp(raw2, 24, #raw2 / 6, "card_flip/card_flip_2.png")
cardFlip = cardFlip or {}
cardFlip.sheet2 = "assets/generated/card_flip/card_flip_2.png"
end
if self.symbols["CardFlipLZ03"] then
local raw3 = self:decompressLz3Symbol("CardFlipLZ03")
self:write2bpp(raw3, 8, #raw3 / 2, "card_flip/card_flip_3.png")
cardFlip = cardFlip or {}
cardFlip.sheet3 = "assets/generated/card_flip/card_flip_3.png"
end
if self.symbols["CardFlipOnButtonGFX"] then
local symbol = self:symbol("CardFlipOnButtonGFX")
self:write2bpp(self.rom:bytes(symbol.bank, symbol.address, 16), 8, 8, "card_flip/on.png")
cardFlip = cardFlip or {}
cardFlip.on = "assets/generated/card_flip/on.png"
end
if self.symbols["CardFlipOffButtonGFX"] then
local symbol = self:symbol("CardFlipOffButtonGFX")
self:write2bpp(self.rom:bytes(symbol.bank, symbol.address, 16), 8, 8, "card_flip/off.png")
cardFlip = cardFlip or {}
cardFlip.off = "assets/generated/card_flip/off.png"
end
if self.symbols["CardFlipTilemap"] then
local symbol = self:symbol("CardFlipTilemap")
local tm = self.rom:bytes(symbol.bank, symbol.address, 11 * 12)
self:save(tm, "card_flip/card_flip.tilemap")
writeRaw("card_flip/card_flip.tilemap", tm)
cardFlip = cardFlip or {}
cardFlip.tilemap = "assets/generated/card_flip/card_flip.tilemap"
end
out.slots = {
sheet1 = "assets/generated/slots/gold_slots_1.png",
sheet2 = "assets/generated/slots/gold_slots_2.png",
sheet3 = "assets/generated/slots/gold_slots_3.png",
tilemap = "assets/generated/slots/gold_slots.tilemap",
}
out.cardFlip = {
sheet1 = "assets/generated/card_flip/card_flip_1.png",
sheet2 = "assets/generated/card_flip/card_flip_2.png",
sheet3 = "assets/generated/card_flip/card_flip_3.png",
on = "assets/generated/card_flip/on.png",
off = "assets/generated/card_flip/off.png",
tilemap = "assets/generated/card_flip/card_flip.tilemap",
}
if cardFlip then out.cardFlip = cardFlip end
self:write("menu_gfx", out)
self:tick("Menu graphics", 1, 1)
+6
View File
@@ -140,6 +140,12 @@ local VERSION_REQUIRED_FILES_OVERRIDE = {
-- before the four ball tiles were extracted (#1502).
"assets/generated/battle/hud/balls.png",
"assets/generated/audio/programs.bin",
-- Goldenrod Game Corner reel + board art (#1581). menu_gfx.lua used to
-- advertise these paths even when Slots*LZ / CardFlip* were absent from
-- the manifest, so a cache that never wrote the PNGs still looked
-- complete and SlotMachine crashed on its labelled-cell fallback.
"assets/generated/slots/gold_slots_1.png",
"assets/generated/card_flip/card_flip_1.png",
},
}
-- Same Gen 2 extract, so a Silver cache is complete when the same files exist.
+5 -7
View File
@@ -49,9 +49,9 @@
-- Textbox at (0,12) with an 18x4 interior
--
-- The cart's own art (gfx/card_flip/card_flip_1..3.2bpp.lz and
-- gfx/card_flip/card_flip.tilemap) is NOT in the cache: no `cardFlip` entry is
-- written into menu_gfx.lua yet, so the board draws as labelled cells until one
-- appears.
-- gfx/card_flip/card_flip.tilemap) is extracted into assets/generated/card_flip/
-- when the Gold/Silver manifest carries CardFlip*. Until those files exist,
-- the board draws as labelled cells.
local Chrome = require("src.ui.gen2.Chrome")
local CoinCase = require("src.core.gen2.CoinCase")
@@ -616,10 +616,8 @@ local TILEMAP = nil
local function getCardFlipTilemap()
if TILEMAP == nil then
local path = "assets/generated/card_flip/card_flip.tilemap"
local f = io.open(path, "rb")
if f then
local data = f:read("*a")
f:close()
local data = love and love.filesystem and love.filesystem.read(path)
if data and #data > 0 then
TILEMAP = {}
for i = 1, #data do
TILEMAP[i] = string.byte(data, i)
+74 -62
View File
@@ -39,11 +39,9 @@
-- tiles at (2,13),(3,13),(2,14),(3,14) and the ▼ at
-- (18,17)
--
-- The cart's own reel art (gfx/slots/slots_1..3.2bpp.lz plus
-- gfx/slots/slots.tilemap) is NOT in the cache: src/import/RomExtractorGen2.lua
-- writes no `slots` entry into menu_gfx.lua yet. SlotMachine:sheet() reads one
-- the moment it appears and falls back to labelled cells until then, the same
-- way src/ui/gen2/PackGfx.lua degrades.
-- Reel art (gfx/slots/slots_1..3.2bpp.lz + slots.tilemap) is extracted into
-- assets/generated/slots/ when the Gold/Silver manifest carries Slots*LZ.
-- Until those files exist, drawReels falls back to labelled cells.
local Chrome = require("src.ui.gen2.Chrome")
local CoinCase = require("src.core.gen2.CoinCase")
@@ -1160,10 +1158,8 @@ local TILEMAP = nil
local function getTilemap()
if TILEMAP == nil then
local path = "assets/generated/slots/gold_slots.tilemap"
local f = io.open(path, "rb")
if f then
local data = f:read("*a")
f:close()
local data = love and love.filesystem and love.filesystem.read(path)
if data and #data > 0 then
TILEMAP = {}
for i = 1, #data do
TILEMAP[i] = string.byte(data, i)
@@ -1175,6 +1171,14 @@ local function getTilemap()
return TILEMAP or nil
end
-- Placeholder 2x2 symbol cell used when reel sheets are not in the cache yet.
local function cell(tx, ty, label)
local G = love.graphics
G.setColor(0, 0, 0, 1)
G.rectangle("line", tx * 8, ty * 8, 16, 16)
Chrome.print(label, tx, ty + 1)
end
function SlotMachine:sheets()
if self.sheet1 == nil then
self.sheet1 = TileSheet.new({ path = "assets/generated/slots/gold_slots_1.png", wide = 2, firstTile = 0 })
@@ -1253,36 +1257,40 @@ function SlotMachine:drawReels()
-- Draw 4 consecutive 2x2 symbols from bottom to top, exactly matching SlotMachine.window
for row = 0, 3 do
local sym = strip[a + row + 1]
local py = 64 - (row * 16) + dy
local pal = GBC_PALS.obj[math.floor(sym / 4)] or GBC_PALS.obj[0]
s2.palette = pal
-- 2x2 tiles in 2-wide sheet:
-- sym + 0 = top-left (col 0, row 0)
-- sym + 1 = top-right (col 1, row 0)
-- sym + 2 = bottom-left (col 0, row 1)
-- sym + 3 = bottom-right (col 1, row 1)
local t0 = s2:quad(sym + 0)
local t1 = s2:quad(sym + 1)
local t2 = s2:quad(sym + 2)
local t3 = s2:quad(sym + 3)
local img = s2:image()
if img and t0 and t1 and t2 and t3 then
local function drawSym()
G.draw(img, t0, rx, py)
G.draw(img, t1, rx + 8, py)
G.draw(img, t2, rx, py + 8)
G.draw(img, t3, rx + 8, py + 8)
end
if GbcPalette.available() then
GbcPalette.with(pal, drawSym)
else
drawSym()
end
if type(sym) ~= "number" then
cell(REEL_X[i], REEL_ROW[row + 1], "?")
else
-- Fallback label
cell(REEL_X[i], REEL_ROW[row + 1], SlotMachine.LABELS[sym] or "?")
local py = 64 - (row * 16) + dy
local pal = GBC_PALS.obj[math.floor(sym / 4)] or GBC_PALS.obj[0]
s2.palette = pal
-- 2x2 tiles in 2-wide sheet:
-- sym + 0 = top-left (col 0, row 0)
-- sym + 1 = top-right (col 1, row 0)
-- sym + 2 = bottom-left (col 0, row 1)
-- sym + 3 = bottom-right (col 1, row 1)
local t0 = s2:quad(sym + 0)
local t1 = s2:quad(sym + 1)
local t2 = s2:quad(sym + 2)
local t3 = s2:quad(sym + 3)
local img = s2:image()
if img and t0 and t1 and t2 and t3 then
local function drawSym()
G.draw(img, t0, rx, py)
G.draw(img, t1, rx + 8, py)
G.draw(img, t2, rx, py + 8)
G.draw(img, t3, rx + 8, py + 8)
end
if GbcPalette.available() then
GbcPalette.with(pal, drawSym)
else
drawSym()
end
else
-- Fallback label
cell(REEL_X[i], REEL_ROW[row + 1], SlotMachine.LABELS[sym] or "?")
end
end
end
end
@@ -1439,31 +1447,35 @@ function SlotMachine:drawMessage()
and self.phase == "payoutText" then
local _, s2 = self:sheets()
local sym = self.matched
local pal = GBC_PALS.obj[math.floor(sym / 4)] or GBC_PALS.obj[0]
s2.palette = pal
local t0 = s2:quad(sym + 0)
local t1 = s2:quad(sym + 1)
local t2 = s2:quad(sym + 2)
local t3 = s2:quad(sym + 3)
local img = s2:image()
local G = love.graphics
local px, py = PAYOUT_SYMBOL_X * 8, PAYOUT_SYMBOL_Y * 8
if img and t0 and t1 and t2 and t3 then
local function drawWin()
G.setColor(1, 1, 1, 1)
G.draw(img, t0, px, py)
G.draw(img, t1, px + 8, py)
G.draw(img, t2, px, py + 8)
G.draw(img, t3, px + 8, py + 8)
end
G.setColor(1, 1, 1, 1)
if GbcPalette.available() then
GbcPalette.with(pal, drawWin)
else
drawWin()
end
if type(sym) ~= "number" then
cell(PAYOUT_SYMBOL_X, PAYOUT_SYMBOL_Y, "?")
else
cell(PAYOUT_SYMBOL_X, PAYOUT_SYMBOL_Y, SlotMachine.LABELS[self.matched] or "?")
local pal = GBC_PALS.obj[math.floor(sym / 4)] or GBC_PALS.obj[0]
s2.palette = pal
local t0 = s2:quad(sym + 0)
local t1 = s2:quad(sym + 1)
local t2 = s2:quad(sym + 2)
local t3 = s2:quad(sym + 3)
local img = s2:image()
local G = love.graphics
local px, py = PAYOUT_SYMBOL_X * 8, PAYOUT_SYMBOL_Y * 8
if img and t0 and t1 and t2 and t3 then
local function drawWin()
G.setColor(1, 1, 1, 1)
G.draw(img, t0, px, py)
G.draw(img, t1, px + 8, py)
G.draw(img, t2, px, py + 8)
G.draw(img, t3, px + 8, py + 8)
end
G.setColor(1, 1, 1, 1)
if GbcPalette.available() then
GbcPalette.with(pal, drawWin)
else
drawWin()
end
else
cell(PAYOUT_SYMBOL_X, PAYOUT_SYMBOL_Y, SlotMachine.LABELS[sym] or "?")
end
end
end
end