From 5714555847d7220f30e937ba57d54d4a37c8c0c4 Mon Sep 17 00:00:00 2001 From: 1jamie Date: Thu, 20 Aug 2026 20:12:41 -0500 Subject: [PATCH 1/2] 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. --- src/import/RomExtractorGen2.lua | 58 +++++++++----- src/import/RomImporter.lua | 6 ++ src/ui/gen2/CardFlip.lua | 12 ++- src/ui/gen2/SlotMachine.lua | 136 +++++++++++++++++--------------- tools/make_gold_manifest.py | 8 ++ 5 files changed, 133 insertions(+), 87 deletions(-) diff --git a/src/import/RomExtractorGen2.lua b/src/import/RomExtractorGen2.lua index d3736b8e..a044833c 100644 --- a/src/import/RomExtractorGen2.lua +++ b/src/import/RomExtractorGen2.lua @@ -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) diff --git a/src/import/RomImporter.lua b/src/import/RomImporter.lua index 3b2c2ac2..5b7fe222 100644 --- a/src/import/RomImporter.lua +++ b/src/import/RomImporter.lua @@ -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. diff --git a/src/ui/gen2/CardFlip.lua b/src/ui/gen2/CardFlip.lua index a1b596ce..b9db6352 100644 --- a/src/ui/gen2/CardFlip.lua +++ b/src/ui/gen2/CardFlip.lua @@ -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) diff --git a/src/ui/gen2/SlotMachine.lua b/src/ui/gen2/SlotMachine.lua index 83bd38fb..a85006dd 100644 --- a/src/ui/gen2/SlotMachine.lua +++ b/src/ui/gen2/SlotMachine.lua @@ -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 diff --git a/tools/make_gold_manifest.py b/tools/make_gold_manifest.py index 0176f072..02828172 100644 --- a/tools/make_gold_manifest.py +++ b/tools/make_gold_manifest.py @@ -785,6 +785,14 @@ REQUIRED_SYMBOLS = { "KabutoPuzzleLZ", "OmanytePuzzleLZ", "AerodactylPuzzleLZ", "HoOhPuzzleLZ", "UnownPuzzleStartCancelLZ", "UnownPuzzleCursorGFX", "PuzzlePieceBorderData.TileBordersGFX", + # Goldenrod Game Corner (engine/games/slot_machine.asm + card_flip.asm). + # Slots1LZ/2LZ/3LZ are the reel + actor sheets; SlotsTilemap is the 20x12 + # BG map. CardFlipLZ01..03 + On/Off button tiles and CardFlipTilemap are + # the odds-board art. Without these in the manifest the extractor skips + # the files and SlotMachine/CardFlip fall back to labelled cells. + "Slots1LZ", "Slots2LZ", "Slots3LZ", "SlotsTilemap", + "CardFlipLZ01", "CardFlipLZ02", "CardFlipLZ03", + "CardFlipOnButtonGFX", "CardFlipOffButtonGFX", "CardFlipTilemap", # Emote bubbles (data/sprites/emotes.asm): showemote's ! over a trainer # who just spotted the player, and the other faces scripts use. "ShockEmote", "QuestionEmote", "HappyEmote", "SadEmote", From 780246c4f6626f09914dd5b7249481429a8798e3 Mon Sep 17 00:00:00 2001 From: 1jamie Date: Thu, 20 Aug 2026 20:40:37 -0500 Subject: [PATCH 2/2] feat: add deinterleave function and enhance graphics extraction for Slots and Card Flip Implemented a new deinterleave function in ImageWriter to restore row-major order for PNGs. Updated RomExtractorGen2 to utilize this function for extracting Slot Machine graphics, and added new functions for padding and writing graphics sheets. Updated the ROM manifests to include necessary symbols for Slots and Card Flip assets. --- src/import/ImageWriter.lua | 20 +++++ src/import/RomExtractorGen2.lua | 149 ++++++++++++++++++++++++++++---- tools/rom_manifest_gold.json | 40 +++++++++ tools/rom_manifest_silver.json | 40 +++++++++ 4 files changed, 230 insertions(+), 19 deletions(-) diff --git a/src/import/ImageWriter.lua b/src/import/ImageWriter.lua index 9ab64fc7..3b57e347 100644 --- a/src/import/ImageWriter.lua +++ b/src/import/ImageWriter.lua @@ -125,6 +125,26 @@ function ImageWriter.columnsToRows(raw, tilesWide, tilesHigh, bytesPerTile) return out end +-- Inverse of pret tools/gfx --interleave (pokecrystal tools/gfx.c). +-- Build-time interleave stores each vertical 8x16 pair as consecutive 8x8 +-- tiles for OBJ mode; this restores row-major sheet order for PNGs. +function ImageWriter.deinterleave(raw, width, bytesPerTile) + bytesPerTile = bytesPerTile or 16 + local widthTiles = width / 8 + local numTiles = #raw / bytesPerTile + local out = {} + for i = 0, numTiles - 1 do + local row = math.floor(i / widthTiles) + local src = i * 2 - (row % 2 == 1 + and widthTiles * (row + 1) - 1 + or widthTiles * row) + for offset = 1, bytesPerTile do + out[i * bytesPerTile + offset] = raw[src * bytesPerTile + offset] + end + end + return out +end + function ImageWriter.save(image, path) local ok, fileData = pcall(image.encode, image, "png") if not ok then error("could not encode " .. path .. ": " .. tostring(fileData)) end diff --git a/src/import/RomExtractorGen2.lua b/src/import/RomExtractorGen2.lua index a044833c..d2ff2435 100644 --- a/src/import/RomExtractorGen2.lua +++ b/src/import/RomExtractorGen2.lua @@ -5272,36 +5272,146 @@ function RomExtractorGen2:extractMenuGfx() end end + -- Canonical sheet sizes match the cart art the UI indexes (and pret's + -- gfx/slots + gfx/card_flip PNGs). ROM LZ streams are those sheets after + -- Makefile gfx transforms; reverse what the decompressed bytes still carry. + local SLOTS1_W, SLOTS1_H = 16, 152 + local SLOTS2_W, SLOTS2_H = 16, 256 + local SLOTS3_W, SLOTS3_H = 24, 240 + local CARD1_W, CARD1_H = 128, 32 + local CARD2_W, CARD2_H = 24, 160 + local CARD3_W, CARD3_H = 8, 56 + + local function pad2bpp(raw, width, height) + local need = width * height / 4 + while #raw < need do raw[#raw + 1] = 0 end + while #raw > need do table.remove(raw) end + return raw + end + + local function writeSheet(raw, width, height, relative, transparent) + self:write2bpp(pad2bpp(raw, width, height), width, height, relative, + transparent) + end + + -- Slots3LZ is unique 8x16 OBJ columns (interleave + remove-duplicates + + -- remove-xflip). Rebuild the 24x240 actor sheet the UI quads expect from + -- OAMData_SlotsGolem / Chansey* / Egg (data/sprite_anims/oam.asm), same + -- pattern as title-screen Ho-Oh frame composition above. + local function composeSlotsActors(raw) + local tileCount = math.floor(#raw / 16) + local tiles = {} + for index = 0, tileCount - 1 do + local one = {} + for b = 1, 16 do one[b] = raw[index * 16 + b] or 0 end + tiles[index] = ImageWriter.decode2bpp(one, 8, 8, true) + end + local sheet = ImageWriter.blank(SLOTS3_W, SLOTS3_H, 1, 1, 1, 0) + local function blit8x16(tileId, dx, dy, flipX) + local top, bot = tiles[tileId], tiles[tileId + 1] + if not (top and bot) then return end + ImageWriter.blit(sheet, top, dx, dy, 0, 0, 8, 8, flipX) + ImageWriter.blit(sheet, bot, dx, dy + 8, 0, 0, 8, 8, flipX) + end + local function blitPose(poseY, base, entries) + for _, e in ipairs(entries) do + blit8x16(base + e.t, (e.x + 2) * 8, poseY + (e.y + 2) * 8, e.xf) + end + end + local golem = { + { x = -2, y = -2, t = 0x00 }, { x = -1, y = -2, t = 0x02 }, + { x = 0, y = -2, t = 0x00, xf = true }, + { x = -2, y = 0, t = 0x04 }, { x = -1, y = 0, t = 0x06 }, + { x = 0, y = 0, t = 0x04, xf = true }, + } + local chansey = { + { + { x = -2, y = -2, t = 0x00 }, { x = -1, y = -2, t = 0x02 }, + { x = 0, y = -2, t = 0x04 }, + { x = -2, y = 0, t = 0x06 }, { x = -1, y = 0, t = 0x08 }, + { x = 0, y = 0, t = 0x0a }, + }, + { + { x = -2, y = -2, t = 0x00 }, { x = -1, y = -2, t = 0x02 }, + { x = 0, y = -2, t = 0x04 }, + { x = -2, y = 0, t = 0x0c }, { x = -1, y = 0, t = 0x0e }, + { x = 0, y = 0, t = 0x10 }, + }, + { + { x = -2, y = -2, t = 0x00 }, { x = -1, y = -2, t = 0x02 }, + { x = 0, y = -2, t = 0x04 }, + { x = -2, y = 0, t = 0x12 }, { x = -1, y = 0, t = 0x14 }, + { x = 0, y = 0, t = 0x16 }, + }, + { + { x = -2, y = -2, t = 0x00 }, { x = -1, y = -2, t = 0x02 }, + { x = 0, y = -2, t = 0x04 }, + { x = -2, y = 0, t = 0x18 }, { x = -1, y = 0, t = 0x1a }, + { x = 0, y = 0, t = 0x1c }, + }, + { + { x = -2, y = -2, t = 0x1e }, { x = -1, y = -2, t = 0x20 }, + { x = 0, y = -2, t = 0x22 }, + { x = -2, y = 0, t = 0x24 }, { x = -1, y = 0, t = 0x26 }, + { x = 0, y = 0, t = 0x28 }, + }, + } + blitPose(0, 0x00, golem) + blitPose(32, 0x08, golem) + for index, frame in ipairs(chansey) do + blitPose(32 + index * 32, 0x10, frame) + end + blit8x16(0x3a, 0, 224, false) + return sheet + end + + -- card_flip_2.2bpp uses --remove-whitespace: blank tiles in column 2 of the + -- 3-wide header strip (indices 2,5,...,23) are dropped from the ROM stream. + -- Re-insert them so HEADER_TILE_MAP / MON_ANCHORS (pret sheet indices) work. + local function expandCardFlip2(compact) + local need = CARD2_W * CARD2_H / 4 + local out = {} + for i = 1, need do out[i] = 0 end + local whitespace = { + [2] = true, [5] = true, [8] = true, [11] = true, + [14] = true, [17] = true, [20] = true, [23] = true, + } + local src = 0 + for tile = 0, 59 do + if not whitespace[tile] then + for b = 1, 16 do + out[tile * 16 + b] = compact[src * 16 + b] or 0 + end + src = src + 1 + end + end + return out + end + local slots = nil if self.symbols["Slots1LZ"] then + -- --trim-whitespace drops the final empty tile (37 of 38). local raw1 = self:decompressLz3Symbol("Slots1LZ") - self:write2bpp(raw1, 16, #raw1 / 4, "slots/gold_slots_1.png") + writeSheet(raw1, SLOTS1_W, SLOTS1_H, "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") - -- In Pokemon Gold ROM, Seven symbol (first 4 tiles = 64 bytes) has inverted bit polarity + local raw2 = ImageWriter.deinterleave( + self:decompressLz3Symbol("Slots2LZ"), SLOTS2_W) + -- Commercial Gold stores the Seven symbol with inverted bit polarity. for i = 1, math.min(64, #raw2) do raw2[i] = bit.band(bit.bnot(raw2[i]), 0xFF) end - self:write2bpp(raw2, 16, #raw2 / 4, "slots/gold_slots_2.png") + writeSheet(raw2, SLOTS2_W, SLOTS2_H, "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") - self:write2bpp(raw3, 24, #raw3 / 6, "slots/gold_slots_3.png", true) - -- Slots3LZ is a 24px-wide (3 tiles), 240px-tall (30 tiles) sprite sheet containing: - -- Y=0: Golem 1 (Standing, 24x32) - -- Y=32: Golem 2 (Ball, 24x32) - -- Y=64: Chansey 1 (Standing / Step 1, 24x32) - -- Y=96: Chansey 2 (Step 2, 24x32) - -- Y=128: Chansey 3 (Step 3, 24x32) - -- Y=160: Chansey 4 (Arm raised / Step 4, 24x32) - -- 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) + local actors = composeSlotsActors(raw3) + self:save(actors, "slots/gold_slots_3.png") + self:save(actors, "slots/gold_slots_actors.png") slots = slots or {} slots.sheet3 = "assets/generated/slots/gold_slots_3.png" end @@ -5317,20 +5427,21 @@ function RomExtractorGen2:extractMenuGfx() -- Goldenrod Game Corner: Card Flip graphics assets local cardFlip = nil if self.symbols["CardFlipLZ01"] then + -- --trim-whitespace: 62 of 64 tiles in the ROM stream. local raw1 = self:decompressLz3Symbol("CardFlipLZ01") - self:write2bpp(raw1, 128, #raw1 / 32, "card_flip/card_flip_1.png") + writeSheet(raw1, CARD1_W, CARD1_H, "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") + local raw2 = expandCardFlip2(self:decompressLz3Symbol("CardFlipLZ02")) + writeSheet(raw2, CARD2_W, CARD2_H, "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") + writeSheet(raw3, CARD3_W, CARD3_H, "card_flip/card_flip_3.png") cardFlip = cardFlip or {} cardFlip.sheet3 = "assets/generated/card_flip/card_flip_3.png" end diff --git a/tools/rom_manifest_gold.json b/tools/rom_manifest_gold.json index 2a54f118..8e191e63 100644 --- a/tools/rom_manifest_gold.json +++ b/tools/rom_manifest_gold.json @@ -17667,6 +17667,46 @@ "wBaseUnusedFrontpic": [ 1, 53554 + ], + "Slots1LZ": [ + 36, + 31138 + ], + "Slots2LZ": [ + 36, + 31522 + ], + "Slots3LZ": [ + 36, + 32130 + ], + "SlotsTilemap": [ + 36, + 30898 + ], + "CardFlipLZ01": [ + 56, + 21795 + ], + "CardFlipLZ02": [ + 56, + 22197 + ], + "CardFlipLZ03": [ + 56, + 21736 + ], + "CardFlipOnButtonGFX": [ + 56, + 21779 + ], + "CardFlipOffButtonGFX": [ + 56, + 21763 + ], + "CardFlipTilemap": [ + 56, + 22809 ] }, "tilesets": { diff --git a/tools/rom_manifest_silver.json b/tools/rom_manifest_silver.json index 2992385f..ff9b91d4 100644 --- a/tools/rom_manifest_silver.json +++ b/tools/rom_manifest_silver.json @@ -17667,6 +17667,46 @@ "wBaseUnusedFrontpic": [ 1, 53554 + ], + "Slots1LZ": [ + 36, + 31138 + ], + "Slots2LZ": [ + 36, + 31522 + ], + "Slots3LZ": [ + 36, + 32130 + ], + "SlotsTilemap": [ + 36, + 30898 + ], + "CardFlipLZ01": [ + 56, + 21795 + ], + "CardFlipLZ02": [ + 56, + 22197 + ], + "CardFlipLZ03": [ + 56, + 21736 + ], + "CardFlipOnButtonGFX": [ + 56, + 21779 + ], + "CardFlipOffButtonGFX": [ + 56, + 21763 + ], + "CardFlipTilemap": [ + 56, + 22809 ] }, "tilesets": {