From 0142468125cf97bb72cc0dcd242b6dc9cd31401b Mon Sep 17 00:00:00 2001 From: bryanthaboi Date: Mon, 24 Aug 2026 13:07:11 -0400 Subject: [PATCH] gender name string --- src/import/CacheContract.lua | 10 +++++++- src/import/RomExtractorGen2.lua | 5 +++- tests/engine/rom_cache_contract_test.lua | 19 ++++++++++------ tests/gen2_rom_text_test.lua | 29 ++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/import/CacheContract.lua b/src/import/CacheContract.lua index 3ae48d21..1937cb21 100644 --- a/src/import/CacheContract.lua +++ b/src/import/CacheContract.lua @@ -9,6 +9,9 @@ local GameVersion = require("src.core.GameVersion") local CacheContract = {} CacheContract.FORMAT = "rom-cache-v10:" +CacheContract.VERSION_FORMAT = { + crystal = "rom-cache-v10-crystal2:", +} CacheContract.MARKER_PATH = "rom-cache.complete" CacheContract.REQUIRED_FILES = { @@ -136,8 +139,13 @@ function CacheContract.requiredFilesFor(version) return CacheContract.REQUIRED_FILES, false end +function CacheContract.formatFor(version) + return CacheContract.VERSION_FORMAT[version] or CacheContract.FORMAT +end + function CacheContract.markerFor(version, sha1) - return CacheContract.FORMAT .. (sha1 or GameVersion.info(version).sha1) + return CacheContract.formatFor(version) + .. (sha1 or GameVersion.info(version).sha1) end function CacheContract.markerMatches(version, marker) diff --git a/src/import/RomExtractorGen2.lua b/src/import/RomExtractorGen2.lua index d9d61276..151ffd71 100644 --- a/src/import/RomExtractorGen2.lua +++ b/src/import/RomExtractorGen2.lua @@ -2849,7 +2849,10 @@ function RomExtractorGen2:decodeGen2Text(bank, address, charmap, buffers) out[#out + 1] = "\f" elseif b == 0x55 then out[#out + 1] = "\v" - elseif b == 0x52 then + elseif b == 0x52 + or (b == 0x14 and inString and self.edition == "crystal") then + -- ../pokecrystal/constants/charmap.asm:6 , + -- ../pokecrystal/home/text.asm:243,380 PlaceGenderedPlayerName out[#out + 1] = "{PLAYER}" elseif b == 0x53 then out[#out + 1] = "{RIVAL}" diff --git a/tests/engine/rom_cache_contract_test.lua b/tests/engine/rom_cache_contract_test.lua index b8960b02..cf81b365 100644 --- a/tests/engine/rom_cache_contract_test.lua +++ b/tests/engine/rom_cache_contract_test.lua @@ -114,21 +114,26 @@ check(CacheContract.VERSION_REQUIRED_FILES.yellow ~= nil, local CRYSTAL_1_0 = "f4cd194bdee0d04ca4eac29e09b8e4e9d818c133" local CRYSTAL_1_1 = "f2f52230b536214ef7c9924f483392993e226cfb" +local CRYSTAL_FORMAT = CacheContract.formatFor("crystal") + eq(CacheContract.markerFor("crystal", CRYSTAL_1_1), - CacheContract.FORMAT .. CRYSTAL_1_1, + CRYSTAL_FORMAT .. CRYSTAL_1_1, "markerFor with an explicit sha1 uses that sha1, not the canonical one") -eq(CacheContract.markerFor("crystal"), CacheContract.FORMAT .. CRYSTAL_1_0, +eq(CacheContract.markerFor("crystal"), CRYSTAL_FORMAT .. CRYSTAL_1_0, "markerFor with no sha1 still defaults to the canonical (1.0) sha1") -check(CacheContract.markerMatches("crystal", CacheContract.FORMAT .. CRYSTAL_1_0), +check(CacheContract.markerMatches("crystal", CRYSTAL_FORMAT .. CRYSTAL_1_0), "a marker written from the 1.0 hash matches crystal") -check(CacheContract.markerMatches("crystal", CacheContract.FORMAT .. CRYSTAL_1_1), +check(CacheContract.markerMatches("crystal", CRYSTAL_FORMAT .. CRYSTAL_1_1), "a marker written from the 1.1 hash also matches crystal") check(not CacheContract.markerMatches("crystal", - CacheContract.FORMAT .. "ea9bcae617fdf159b045185467ae58b2e4a48b9a"), + CRYSTAL_FORMAT .. "ea9bcae617fdf159b045185467ae58b2e4a48b9a"), "a marker written from Red's hash does not match crystal") -check(not CacheContract.markerMatches("red", CacheContract.FORMAT .. CRYSTAL_1_1), +check(not CacheContract.markerMatches("red", CRYSTAL_FORMAT .. CRYSTAL_1_1), "a marker written from Crystal's 1.1 hash does not match red") +check(not CacheContract.markerMatches("crystal", + CacheContract.FORMAT .. CRYSTAL_1_0), + "a crystal marker written on the shared format no longer matches") local crystalFs = { prefix = "crystal-marker-test/", files = {} } function crystalFs.exists(path) return crystalFs.files[crystalFs.prefix .. path] ~= nil end @@ -146,7 +151,7 @@ end local published11 = CacheContract.publish("crystal", crystalFs, CRYSTAL_1_1) check(published11, "publishing with the 1.1 sha1 succeeds") eq(crystalFs.files["crystal/" .. CacheContract.MARKER_PATH], - CacheContract.FORMAT .. CRYSTAL_1_1, "the marker records the 1.1 sha1") + CRYSTAL_FORMAT .. CRYSTAL_1_1, "the marker records the 1.1 sha1") check(CacheContract.isReady("crystal", crystalFs), "a cache published with the 1.1 sha1 still reads ready for crystal") diff --git a/tests/gen2_rom_text_test.lua b/tests/gen2_rom_text_test.lua index 73cf6d7a..1e87f429 100644 --- a/tests/gen2_rom_text_test.lua +++ b/tests/gen2_rom_text_test.lua @@ -91,6 +91,35 @@ do :format(extra)) end +-- ---- the gendered player name --------------------------------------------- +-- ../pokecrystal/constants/charmap.asm:6 ($14), +-- ../pokecrystal/home/text.asm:243,380 PlaceGenderedPlayerName +do + local Extractor = require("src.import.RomExtractorGen2") + local charmap = manifest("tools/rom_manifest_crystal.json").charmap + + local function decode(edition, bytes) + local rom = {} + function rom:byte(_, address) return bytes[address] or 0x50 end + function rom:word(_, address) + return (bytes[address] or 0) + (bytes[address + 1] or 0) * 0x100 + end + local extractor = setmetatable({ rom = rom, edition = edition }, Extractor) + return extractor:decodeGen2Text(0, 0, charmap) + end + + local H, i, comma, space, bang = 0x87, 0xa8, 0xf4, 0x7f, 0xe7 + local inString = { [0] = 0x00, H, i, comma, space, 0x14, bang, 0x57 } + eq(decode("crystal", inString), "Hi, {PLAYER}!", + "an in-string $14 decodes as the player's name") + eq(decode("gold", inString), "Hi, !", + "and only on Crystal, which is the only edition that writes one") + + local command = { [0] = 0x14, 0x03, 0x00, H, i, 0x57 } + eq(decode("crystal", command), "{STRBUF}Hi", + "a $14 outside a string is still TX_STRINGBUFFER and eats its buffer id") +end + -- ---- the slots RomText fills ---------------------------------------------- -- decodeGen2Text emits {USER}, {TARGET} and {ENEMY} for the three names -- PlaceMoveUsersName / PlaceMoveTargetsName / PlaceEnemysName write at