Files
gen1recomp/src/import/gba/text_placeholders_extract.lua
T
thibautbus b5988d4654 Expand Ruby and Sapphire's placeholders from the script cache
The runtime expanded Ruby and Sapphire's {EVIL_TEAM}, {GOOD_LEADER},
{VERSION}, {RIVAL} and {KUN} (and the other version-dependent names)
from text/placeholders.lua, the values the extractor reads from the US
cart, so a translation never reached them: the team leaders stayed MAXIE
and ARCHIE, the rival MAY or BRENDAN, the version RUBY or SAPPHIRE, and
the Japanese honorific empty. Each value is a cart string the script
cache also holds under its pokeruby label (gExpandedPlaceholder_Maxie,
_May, _Ruby, _Kun...); the placeholders now read it from there, with the
extract's value as the fallback, the way Emerald's do.

The labels are defined once, in text_placeholders_extract.lua
(rsSymbols, the evil and good teams swapped by edition), and both the
Ruby/Sapphire import and the message box read them from there.
2026-10-06 22:27:41 +02:00

126 lines
4.2 KiB
Lua

local TextPlaceholders = {}
TextPlaceholders.CACHE_SUB = "text"
TextPlaceholders.FILE = "placeholders.lua"
TextPlaceholders.REQUIRED = { "text/placeholders.lua" }
-- pokeemerald/src/strings.c:6
TextPlaceholders.SYMBOLS = {
UNKNOWN = "gText_ExpandedPlaceholder_Empty",
KUN_MALE = "gText_ExpandedPlaceholder_Kun",
KUN_FEMALE = "gText_ExpandedPlaceholder_Chan",
RIVAL_MALE = "gText_ExpandedPlaceholder_May",
RIVAL_FEMALE = "gText_ExpandedPlaceholder_Brendan",
VERSION = "gText_ExpandedPlaceholder_Emerald",
AQUA = "gText_ExpandedPlaceholder_Aqua",
MAGMA = "gText_ExpandedPlaceholder_Magma",
ARCHIE = "gText_ExpandedPlaceholder_Archie",
MAXIE = "gText_ExpandedPlaceholder_Maxie",
KYOGRE = "gText_ExpandedPlaceholder_Kyogre",
GROUDON = "gText_ExpandedPlaceholder_Groudon",
}
-- pokeruby/src/string_util.c:476
function TextPlaceholders.rsSymbols(game)
local stems = {
UNKNOWN = "Empty", KUN_MALE = "Kun", KUN_FEMALE = "Chan",
RIVAL_MALE = "May", RIVAL_FEMALE = "Brendan",
VERSION = game == "sapphire" and "Sapphire" or "Ruby",
AQUA = "Aqua", MAGMA = "Magma", ARCHIE = "Archie", MAXIE = "Maxie",
KYOGRE = "Kyogre", GROUDON = "Groudon",
}
local evil, good = { "Magma", "Maxie", "Groudon" }, { "Aqua", "Archie", "Kyogre" }
if game == "sapphire" then evil, good = good, evil end
for i, name in ipairs({ "TEAM", "LEADER", "LEGENDARY" }) do
stems["EVIL_" .. name], stems["GOOD_" .. name] = evil[i], good[i]
end
local out = {}
for name, stem in pairs(stems) do out[name] = "gExpandedPlaceholder_" .. stem end
return out
end
function TextPlaceholders.symbolsFor(game)
if game == "ruby" or game == "sapphire" then return TextPlaceholders.rsSymbols(game) end
return TextPlaceholders.SYMBOLS
end
-- pokeemerald/src/string_util.c:448
TextPlaceholders.BY_GENDER = {
KUN = { male = "KUN_MALE", female = "KUN_FEMALE" },
RIVAL = { male = "RIVAL_MALE", female = "RIVAL_FEMALE" },
}
local TEXT_MAX = 64
local function read_plain(rom, off)
local TextIR = require("src.core.game3.scripting.text_ir")
local bytes = {}
for i = 0, TEXT_MAX - 1 do
local b = rom:get(off + i)
bytes[#bytes + 1] = b
if b == 0xFF then break end
end
assert(bytes[#bytes] == 0xFF, string.format("placeholder text at 0x%X has no terminator", off))
return TextIR.toPlain(TextIR.decode(bytes, { dialect = "rse" }), {})
end
function TextPlaceholders.extract(rom, offsets)
offsets = offsets or require("src.import.gba.versions").TEXT_PLACEHOLDERS
assert(type(offsets) == "table", "Versions.TEXT_PLACEHOLDERS is missing for this game")
local out = { byGender = {} }
for name in pairs(TextPlaceholders.SYMBOLS) do
out[name] = read_plain(rom, assert(offsets[name], "no offset for placeholder " .. name))
end
for name, off in pairs(offsets) do
if out[name] == nil then out[name] = read_plain(rom, off) end
end
for name, pair in pairs(TextPlaceholders.BY_GENDER) do
out.byGender[name] = { male = out[pair.male], female = out[pair.female] }
end
return out
end
local function serialize(t)
local keys = {}
for k in pairs(t) do keys[#keys + 1] = k end
table.sort(keys)
local parts = { "{\n" }
for _, k in ipairs(keys) do
local v = t[k]
if type(v) == "table" then
local inner = {}
local ik = {}
for k2 in pairs(v) do ik[#ik + 1] = k2 end
table.sort(ik)
for _, k2 in ipairs(ik) do
local v2 = v[k2]
if type(v2) == "table" then
inner[#inner + 1] = string.format("%s = { female = %q, male = %q }", k2, v2.female, v2.male)
else
inner[#inner + 1] = string.format("%s = %q", k2, v2)
end
end
parts[#parts + 1] = string.format(" %s = { %s },\n", k, table.concat(inner, ", "))
else
parts[#parts + 1] = string.format(" %s = %q,\n", k, v)
end
end
parts[#parts + 1] = "}"
return table.concat(parts)
end
function TextPlaceholders.serialize(data)
return "return " .. serialize(data) .. "\n"
end
function TextPlaceholders.run(rom, cache, opts)
opts = opts or {}
local root = opts.cacheRoot or "data/generated/gba"
local data = TextPlaceholders.extract(rom, opts.offsets)
cache:write(root .. "/" .. TextPlaceholders.CACHE_SUB .. "/" .. TextPlaceholders.FILE,
TextPlaceholders.serialize(data))
return data
end
return TextPlaceholders