Map the French, German, Italian and Spanish releases' addresses (#84)
PRET builds none of these releases, so scripts/map-revision.mjs derives where each keeps the profile's labels from the 1.1 build: a bank-by-bank alignment, then the exact references RGBDS recorded while linking 1.1, read at the matching place in the release, each result checked against its bytes. All but 1-3 labels per release are found; the rest, in code the release changed, are -1 so reading them refuses. check:reference verifies the generated file when the ROMs are present. CrystalAssets now points the profile at the cartridge's addresses, and the releases' characters (from their own fonts) and two European text codes decode. The introduction and Mom's scene play in each language and every map sweeps as in English, but the releases stay refused until the logic they localize (units, word order, layouts) is ported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3c480a28f0
commit
627f87d8b5
17 files changed
+1202
-190
No files matched your search
+14
-159
@@ -1,6 +1,8 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { PROFILE_RAM, PROFILE_SYMBOLS } from "./profile-symbols.mjs";
|
||||
import { RELEASES, generate } from "./map-revision.mjs";
|
||||
|
||||
const root = ".reference/pokecrystal";
|
||||
const expected = "f2f52230b536214ef7c9924f483392993e226cfb";
|
||||
@@ -37,125 +39,7 @@ const symbols = new Map(
|
||||
];
|
||||
}),
|
||||
);
|
||||
const names = {
|
||||
trainerGroups: "TrainerGroups",
|
||||
trainerClassNames: "TrainerClassNames",
|
||||
trainerClassAttributes: "TrainerClassAttributes",
|
||||
trainerClassDVs: "TrainerClassDVs",
|
||||
statusOnlyEffects: "StatusOnlyEffects",
|
||||
stallMoves: "StallMoves",
|
||||
recklessMoves: "RecklessMoves",
|
||||
residualMoves: "ResidualMoves",
|
||||
riskyEffects: "RiskyEffects",
|
||||
mapGroups: "MapGroupPointers",
|
||||
tilesets: "Tilesets",
|
||||
collisions: "CollisionPermissionTable",
|
||||
environmentColors: "EnvironmentColorsPointers",
|
||||
backgroundPalettes: "TilesetBGPalette",
|
||||
objectPalettes: "MapObjectPals",
|
||||
roofPalettes: "RoofPals",
|
||||
roofTypes: "MapGroupRoofs",
|
||||
roofs: "Roofs",
|
||||
housePalettes: "HousePalette",
|
||||
sprites: "OverworldSprites",
|
||||
facings: "Facings",
|
||||
standardScripts: "StdScripts",
|
||||
mapScenes: "MapScenes",
|
||||
specials: "SpecialsPointers",
|
||||
emotes: "Emotes",
|
||||
landmarks: "Landmarks",
|
||||
weekdayNames: "SetDayOfWeek.WeekdayStrings",
|
||||
whatDayText: "SetDayOfWeek.OakTimeWhatDayIsItText",
|
||||
isItDayText: "SetDayOfWeek.OakTimeIsItText",
|
||||
dstConfirmText: "InitialSetDSTFlag.DSTIsThatOKText",
|
||||
timeConfirmText: "InitialClearDSTFlag.TimeAskOkayText",
|
||||
baseData: "BaseData",
|
||||
pokemonNames: "PokemonNames",
|
||||
pokemonPics: "PokemonPicPointers",
|
||||
picsBanks: "FixPicBank.PicsBanks",
|
||||
pokemonPalettes: "PokemonPalettes",
|
||||
growthRates: "GrowthRates",
|
||||
evosAttacks: "EvosAttacksPointers",
|
||||
moves: "Moves",
|
||||
moveNames: "MoveNames",
|
||||
itemNames: "ItemNames",
|
||||
itemAttributes: "ItemAttributes",
|
||||
itemPocketNames: "ItemPocketNames",
|
||||
giveItemScript: "GiveItemScript",
|
||||
putItemInPocketText: "PutItemInPocketText",
|
||||
pocketIsFullText: "PocketIsFullText",
|
||||
findItemInBallScript: "FindItemInBallScript",
|
||||
caughtAskNicknameText: "CaughtAskNicknameText",
|
||||
stringEgg: "String_Egg",
|
||||
tmhmMoves: "TMHMMoves",
|
||||
eggPic: "EggPic",
|
||||
nicknameStrings: "NamingScreenJumptable.NicknameStrings",
|
||||
rivalNameString: "NamingScreenJumptable.RivalNameString",
|
||||
rivalDefaultName: "NameRival.DefaultName",
|
||||
hiddenItemScript: "HiddenItemScript",
|
||||
talkToTrainerScript: "TalkToTrainerScript",
|
||||
seenByTrainerScript: "SeenByTrainerScript",
|
||||
wildBattleScript: "WildBattleScript",
|
||||
battleWhiteoutScript: "Script_BattleWhiteout",
|
||||
spawnPoints: "SpawnPoints",
|
||||
johtoGrassWildMons: "JohtoGrassWildMons",
|
||||
grassMonProbTable: "GrassMonProbTable",
|
||||
grassCollisions: "CheckGrassCollision.blocks",
|
||||
fruitTreeScript: "FruitTreeScript",
|
||||
fruitTreeItems: "FruitTreeItems",
|
||||
engineFlags: "EngineFlags",
|
||||
spriteMovementData: "SpriteMovementData",
|
||||
getCurTreeFruit: "GetCurTreeFruit",
|
||||
tryResetFruitTrees: "TryResetFruitTrees",
|
||||
checkFruitTree: "CheckFruitTree",
|
||||
pickedFruitTree: "PickedFruitTree",
|
||||
trainerWalkToPlayer: "TrainerWalkToPlayer",
|
||||
setMemEvent: "SetMemEvent",
|
||||
marts: "Marts",
|
||||
martWelcomeText: "MartWelcomeText",
|
||||
martAskMoreText: "MartAskMoreText",
|
||||
martComeAgainText: "MartComeAgainText",
|
||||
martHowManyText: "MartHowManyText",
|
||||
martFinalPriceText: "MartFinalPriceText",
|
||||
martNoMoneyText: "MartNoMoneyText",
|
||||
martPackFullText: "MartPackFullText",
|
||||
martThanksText: "MartThanksText",
|
||||
martCantBuyText: "MartCantBuyText",
|
||||
martSellHowManyText: "MartSellHowManyText",
|
||||
martSellPriceText: "MartSellPriceText",
|
||||
martBoughtText: "MartBoughtText",
|
||||
fadeOutToWhite: "FadeOutToWhite",
|
||||
healParty: "HealParty",
|
||||
healMachineAnim: "HealMachineAnim",
|
||||
randomPhoneMon: "RandomPhoneMon",
|
||||
checkPokerus: "CheckPokerus",
|
||||
stubbedTrainerRankingsHealings: "StubbedTrainerRankings_Healings",
|
||||
warpToSpawnPoint: "WarpToSpawnPoint",
|
||||
battleBGMap: "BattleBGMap",
|
||||
halveMoney: "HalveMoney",
|
||||
getWhiteoutSpawn: "GetWhiteoutSpawn",
|
||||
tryReceiveItem: "FindItemInBallScript.TryReceiveItem",
|
||||
giveItemDummy: "GiveItemScript_DummyFunction",
|
||||
restartMapMusic: "RestartMapMusic",
|
||||
fadeOutMusic: "FadeOutMusic",
|
||||
fadeOutToBlack: "FadeOutToBlack",
|
||||
fadeInFromBlack: "FadeInFromBlack",
|
||||
reloadSpritesNoPalettes: "ReloadSpritesNoPalettes",
|
||||
receiveSpecialPhoneCall: "CheckSpecialPhoneCall.script",
|
||||
specialPhoneCallList: "SpecialPhoneCallList",
|
||||
specialCallOnlyWhenOutside: "SpecialCallOnlyWhenOutside",
|
||||
specialCallWhereverYouAre: "SpecialCallWhereverYouAre",
|
||||
phoneClickText: "PhoneClickText",
|
||||
ringTwiceStartCall: "RingTwice_StartCall",
|
||||
hangUp: "HangUp",
|
||||
initCallReceiveDelay: "InitCallReceiveDelay",
|
||||
nameRival: "NameRival",
|
||||
setDayOfWeek: "SetDayOfWeek",
|
||||
initialSetDst: "InitialSetDSTFlag",
|
||||
initialClearDst: "InitialClearDSTFlag",
|
||||
toggleDecorationsVisibility: "ToggleDecorationsVisibility",
|
||||
toggleMaptileDecorations: "ToggleMaptileDecorations",
|
||||
};
|
||||
const names = PROFILE_SYMBOLS;
|
||||
// Work and hardware RAM symbols are compared by their CPU address, not a
|
||||
// ROM offset. Hardware RAM (hMoneyTemp and similar) lives at $FF80-$FFFE.
|
||||
const ram = new Map(
|
||||
@@ -168,45 +52,7 @@ const ram = new Map(
|
||||
return match ? [[match[2], parseInt(match[1], 16)]] : [];
|
||||
}),
|
||||
);
|
||||
const ramNames = {
|
||||
playerDamageTaken: "wPlayerDamageTaken",
|
||||
enemyDamageTaken: "wEnemyDamageTaken",
|
||||
payDayMoney: "wPayDayMoney",
|
||||
textDecimalByte: "wTextDecimalByte",
|
||||
battleReward: "wBattleReward",
|
||||
stringBuffer1: "wStringBuffer1",
|
||||
stringBuffer2: "wStringBuffer2",
|
||||
stringBuffer3: "wStringBuffer3",
|
||||
stringBuffer4: "wStringBuffer4",
|
||||
stringBuffer5: "wStringBuffer5",
|
||||
playerName: "wPlayerName",
|
||||
momsName: "wMomsName",
|
||||
rivalName: "wRivalName",
|
||||
battleMonNickname: "wBattleMonNickname",
|
||||
enemyMonNickname: "wEnemyMonNickname",
|
||||
curPartyLevel: "wCurPartyLevel",
|
||||
monOrItemNameBuffer: "wMonOrItemNameBuffer",
|
||||
curFruit: "wCurFruit",
|
||||
dailyFlags1: "wDailyFlags1",
|
||||
dailyRematchFlags: "wDailyRematchFlags",
|
||||
dailyPhoneTimeOfDayFlags: "wDailyPhoneTimeOfDayFlags",
|
||||
unusedDailyFlag: "wUnusedDailyFlag",
|
||||
itemQuantityChange: "wItemQuantityChange",
|
||||
movementBuffer: "wMovementBuffer",
|
||||
hiddenItemID: "wHiddenItemID",
|
||||
runningTrainerBattleScript: "wRunningTrainerBattleScript",
|
||||
callerContact: "wCallerContact",
|
||||
surfingPlayerState: "wSurfingPlayerState",
|
||||
strengthSpecies: "wStrengthSpecies",
|
||||
curHPAnimDeltaHP: "wCurHPAnimDeltaHP",
|
||||
visitedSpawns: "wVisitedSpawns",
|
||||
tempWildMonSpecies: "wTempWildMonSpecies",
|
||||
parkBallsRemaining: "wParkBallsRemaining",
|
||||
coins: "wCoins",
|
||||
bgMapMode: "hBGMapMode",
|
||||
blueCardBalance: "wBlueCardBalance",
|
||||
moneyTemp: "hMoneyTemp",
|
||||
};
|
||||
const ramNames = PROFILE_RAM;
|
||||
const profile = readFileSync("src/game/rom/profile.ts", "utf8");
|
||||
// Every label the profile pins, for the other revisions' check below.
|
||||
const used = new Set(["Trainers", "TrainerGroups"]);
|
||||
@@ -474,6 +320,15 @@ for (const [name, rom, sha1] of [
|
||||
throw new Error(`Profile labels at other addresses in ${name}: ${moved.slice(0, 10).join(", ")}`);
|
||||
revisions.push(name);
|
||||
}
|
||||
// The European releases' addresses (src/game/rom/revision-addresses.ts),
|
||||
// regenerated from 1.1 and compared when their ROMs are present.
|
||||
const releases = Object.values(RELEASES).filter(({ rom }) => existsSync(rom));
|
||||
let european = false;
|
||||
if (releases.length === Object.keys(RELEASES).length) {
|
||||
if (generate().text !== readFileSync("src/game/rom/revision-addresses.ts", "utf8"))
|
||||
throw new Error("src/game/rom/revision-addresses.ts is out of date: run node scripts/map-revision.mjs.");
|
||||
european = true;
|
||||
} else if (releases.length) throw new Error("Checking the European releases' addresses needs all four ROMs.");
|
||||
console.log(
|
||||
`User ROM, pinned PRET rebuild and all native profile addresses match${revisions.length ? `, also in ${revisions.join(" and ")}` : ""}.`,
|
||||
`User ROM, pinned PRET rebuild and all native profile addresses match${revisions.length ? `, also in ${revisions.join(" and ")}` : ""}${european ? "; the European releases' addresses are up to date" : ""}.`,
|
||||
);
|
||||
Reference in new issue
Block a user