copy rintro

This commit is contained in:
bryanthaboi
2026-08-24 09:02:56 -04:00
parent da73a4fe25
commit 1bd6e35931
4 changed files with 108 additions and 6 deletions
+1
View File
@@ -98,6 +98,7 @@ CacheContract.VERSION_REQUIRED_FILES_OVERRIDE = {
"assets/generated/title/crystal_logo.png",
"assets/generated/title/crystal_wordmark.png",
"assets/generated/title/crystal_suicune.png",
"assets/generated/title/copyright_splash.png",
"assets/generated/splash/ditto.png",
"assets/generated/intro/chris.png",
"assets/generated/intro/kris.png",
+44 -1
View File
@@ -258,6 +258,44 @@ local function colorize(image, palFor)
return out
end
-- engine/movie/splash.asm:20-30 sets SCGB_GAMEFREAK_LOGO before Copyright, so
-- the card runs on gfx/sgb/predef.pal:79 PREDEFPAL_GAMEFREAK_LOGO_BG.
local COPYRIGHT_TILES = 29
local COPYRIGHT_BG = {
{ 0, 0, 0 }, { 66, 90, 90 }, { 173, 173, 173 }, { 255, 255, 255 },
}
-- data/copyright.asm at hlcoord 2, 7 (engine/menus/intro_menu.asm:1315-1326).
local COPYRIGHT_LINES = {
{ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c },
{ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x7a, 0x7b, 0x7c },
{ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c },
}
local function extractCopyright(self)
local symbol = self:symbol("CopyrightGFX")
local raw = self.rom:bytes(symbol.bank, symbol.address,
COPYRIGHT_TILES * TILE_BYTES)
self:write2bpp(raw, COPYRIGHT_TILES * 8, 8, "title/copyright.png")
local painted = {}
for index, tile in ipairs(tilesFrom2bpp(raw)) do
painted[index] = colorize(tile, function() return COPYRIGHT_BG end)
end
local backdrop = COPYRIGHT_BG[1]
local splash = ImageWriter.blank(160, 144,
backdrop[1] / 255, backdrop[2] / 255, backdrop[3] / 255, 1)
for row, line in ipairs(COPYRIGHT_LINES) do
for column, id in ipairs(line) do
blitTile(splash, painted[id - 0x60 + 1], (1 + column) * 8, (6 + row) * 8)
end
end
self:save(splash, "title/copyright_splash.png")
end
function CrystalMovie.extractTitle(self)
self:beginStage("Title screen")
@@ -348,6 +386,7 @@ function CrystalMovie.extractTitle(self)
local gemTinted = colorize(gem, function() return pals.obj[1] end)
self:save(gemTinted, "title/crystal_gem.png")
self:save(gem, "title/crystal_gem_gray.png")
extractCopyright(self)
self:tick("Title screen", 4, 5)
local function unit(color) return { color[1] / 255, color[2] / 255, color[3] / 255 } end
@@ -356,7 +395,7 @@ function CrystalMovie.extractTitle(self)
generation = 2,
layout = "crystal_title",
source = "ROM:TitleSuicuneGFX + TitleLogoGFX + TitleCrystalGFX"
.. " + TitleScreenPalettes",
.. " + TitleScreenPalettes + CopyrightGFX",
screen = "assets/generated/title/crystal_screen.png",
screenGray = "assets/generated/title/crystal_screen_gray.png",
image = "assets/generated/title/crystal_logo.png",
@@ -371,6 +410,10 @@ function CrystalMovie.extractTitle(self)
suicuneEvery = 8,
gem = "assets/generated/title/crystal_gem.png",
gemGray = "assets/generated/title/crystal_gem_gray.png",
copyright = "assets/generated/title/copyright.png",
copyrightSplash = "assets/generated/title/copyright_splash.png",
copyrightBackdrop = unit(COPYRIGHT_BG[1]),
copyrightInk = unit(COPYRIGHT_BG[4]),
-- Strip 0 starts at OAM (64, -$22) and stops at OAM y 22, i.e. screen
-- (56, 6) (engine/movie/title.asm:306-311,340-362).
gemX = 56,
+18 -5
View File
@@ -23,6 +23,11 @@ local HOLD_FRAMES = 100
-- Only used when the extracted splash image is missing.
local LINE_Y = { 48, 64, 80 }
-- Gold boots on the default BGP; Crystal inverts under SCGB_GAMEFREAK_LOGO
-- (../pokecrystal/engine/movie/splash.asm:20-30).
local DEFAULT_BACKDROP = { 1, 1, 1 }
local DEFAULT_INK = { 0, 0, 0 }
local function tryImage(path)
if not path then return nil end
local ok, image = pcall(Assets.image, path)
@@ -43,6 +48,8 @@ function CopyrightSplash.new(game, opts)
self.image = tryImage(opts.image)
or tryImage(title.copyrightSplash)
or tryImage("assets/generated/title/copyright_splash.png")
self.backdrop = opts.backdrop or title.copyrightBackdrop or DEFAULT_BACKDROP
self.ink = opts.ink or title.copyrightInk or DEFAULT_INK
-- Text fallback only when the ROM extract is missing (tests / bare boots).
self.lines = opts.lines
self.frames = 0
@@ -78,17 +85,24 @@ function CopyrightSplash:update(_dt)
end
end
function CopyrightSplash:fillBackdrop(width, height)
local G = love.graphics
local c = self.backdrop
G.setColor(c[1], c[2], c[3], 1)
G.rectangle("fill", 0, 0, width, height)
end
function CopyrightSplash:drawPanel()
local G = love.graphics
G.setColor(1, 1, 1, 1)
G.rectangle("fill", 0, 0, SCREEN_W, SCREEN_H)
self:fillBackdrop(SCREEN_W, SCREEN_H)
if self.image then
G.setColor(1, 1, 1, 1)
G.draw(self.image, 0, 0)
return
end
if not self.lines then return end
-- The Gen 2 font is a fixed 8px cell, so centring is a character count.
G.setColor(0, 0, 0, 1)
G.setColor(self.ink[1], self.ink[2], self.ink[3], 1)
for index, line in ipairs(self.lines) do
local y = LINE_Y[index]
if y then
@@ -104,8 +118,7 @@ end
function CopyrightSplash:drawWidescreen(winW, winH)
local G = love.graphics
G.setColor(1, 1, 1, 1)
G.rectangle("fill", 0, 0, winW, winH)
self:fillBackdrop(winW, winH)
local scale = Chrome.fitScale(winW, winH)
G.push()
G.translate(Chrome.fitOrigin(winW, winH, scale))
@@ -0,0 +1,45 @@
-- Crystal's boot copyright card is white text on black: SplashScreen sets
-- SCGB_GAMEFREAK_LOGO before calling Copyright, so the card runs on
-- PREDEFPAL_GAMEFREAK_LOGO_BG rather than the default BGP Gold's DMG boot
-- leaves up (../pokecrystal/engine/movie/splash.asm:21-30). The card used to
-- hardcode a white fill and Crystal shipped no extracted splash at all, so the
-- first screen of the game was blank white.
package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.modkit")
local CopyrightSplash = require("src.ui.gen2.CopyrightSplash")
local function colorsEq(got, want, message)
T.eq(#got, 3, message .. " (three channels)")
for index = 1, 3 do
T.eq(got[index], want[index], message .. " channel " .. index)
end
end
local gold = CopyrightSplash.new({}, { title = {} })
colorsEq(gold.backdrop, { 1, 1, 1 }, "Gold keeps the white card")
colorsEq(gold.ink, { 0, 0, 0 }, "Gold keeps black text")
local crystal = CopyrightSplash.new({}, {
title = {
copyrightBackdrop = { 0, 0, 0 },
copyrightInk = { 1, 1, 1 },
},
})
colorsEq(crystal.backdrop, { 0, 0, 0 }, "Crystal's card is black")
colorsEq(crystal.ink, { 1, 1, 1 }, "Crystal's text is white")
-- The extractor has to emit the image too, or the card is an empty backdrop.
local CrystalMovie = require("src.import.CrystalMovie")
T.eq(type(CrystalMovie.extractTitle), "function",
"CrystalMovie still owns the Crystal title stage")
local CacheContract = require("src.import.CacheContract")
local required = CacheContract.VERSION_REQUIRED_FILES_OVERRIDE.crystal
local found = false
for _, path in ipairs(required) do
if path == "assets/generated/title/copyright_splash.png" then found = true end
end
T.eq(found, true, "a Crystal cache without the splash is rebuilt")
T.finish("copyright splash backdrop")