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
+2
-1
@@ -16,7 +16,7 @@ make -C .reference/pokecrystal crystal crystal_au -j4
|
||||
npm run check:reference
|
||||
```
|
||||
|
||||
Install RGBDS 1.0.3 separately for local checks. `CRYSTAL_ROM_PATH` optionally selects another local location; checks otherwise use `Pokemon - Crystal Version (UE) (V1.1) [C][!].gbc` at the root. The private CI workflow downloads the user's ROM from the `CRYSTAL_ROM_URL` repository secret, verifies its size and SHA-1, and builds this reference with the pinned RGBDS 1.0.3 source. It runs only for `main`, never pull requests.
|
||||
Install RGBDS 1.0.3 separately for local checks. With the French, German, Italian and Spanish ROMs at the root (`Pokemon - Version Cristal (France).gbc`, `Pokemon - Kristall-Edition (Germany).gbc`, `Pokemon - Versione Cristallo (Italy).gbc`, `Pokemon - Edicion Cristal (Spain).gbc`), `node scripts/map-revision.mjs` regenerates `src/game/rom/revision-addresses.ts` from the 1.1 build (its objects and map file) and `check:reference` verifies it. `CRYSTAL_ROM_PATH` optionally selects another local location; checks otherwise use `Pokemon - Crystal Version (UE) (V1.1) [C][!].gbc` at the root. The private CI workflow downloads the user's ROM from the `CRYSTAL_ROM_URL` repository secret, verifies its size and SHA-1, and builds this reference with the pinned RGBDS 1.0.3 source. It runs only for `main`, never pull requests.
|
||||
|
||||
The reference checkout, rebuilt ROM, extracted graphics, saves and gameplay screenshots must stay in local or private CI workspaces and remain ignored. Private CI deletes them and does not upload artifacts or cache game data. The shipping profile contains addresses and format constants, not game content.
|
||||
|
||||
@@ -66,6 +66,7 @@ These paths refer to the pinned [PRET source](https://github.com/pret/pokecrysta
|
||||
| `game/party/hall-of-fame.ts`, `game/scripts/hall-of-fame.ts`, `game/scripts/pc.ts` | `engine/events/halloffame.asm` (`GetHallOfFameParty`, `_HallOfFamePC`, `LoadHOFTeam`, `DisplayHOFMon`), `engine/menus/save.asm` (`AddHallOfFameEntry`), `engine/events/pokecenter_pc.asm` (`.ChooseWhichPCListToUse`, `HallOfFamePC`) | Up to 30 recorded teams, newest first, with the capped win count; saved and migrated by absence, separate from the League flag. Nothing records a team until the League is ported. The PC lists HALL OF FAME from the count; pages browse with A, Start and B as the original does, and keep its unreachable "HOF Master!" title. A cartridge comparison of 255 teams checks the tilemap, attributes, graphics and palette; the frontpic animation (`HOF_AnimateFrontpic`) is not presented. |
|
||||
| `game/scripts/league.ts`, the League, the Hall of Fame and the credits | `engine/events/halloffame.asm` (`HallOfFame`, `RedCredits`, `AnimateHallOfFame`, `AnimateHOFMonEntrance`, `HOF_SlideBackpic`, `HOF_SlideFrontpic`, `HOF_AnimatePlayerPic`), `engine/movie/credits.asm` (`Credits`, `ParseCredits`, `Credits_LoadBorderGFX`, `Credits_LYOverride`, `ConstructCreditsTilemap`, `GetCreditsPalette`), `engine/movie/init_hof_credits.asm`, `engine/events/prof_oaks_pc.asm` (`ProfOaksPCBoot`, `ProfOaksPCRating`, `Rate`, `FindOakRating`), `engine/gfx/player_gfx.asm` (`GetPlayerBackpic`, `HOF_LoadTrainerFrontpic`), `home/vblank.asm` (`VBlank_Credits`), `engine/overworld/scripting.asm` (`Script_halloffame`, `Script_credits`, `Script_wait`), `home/text.asm` (`PlaceEnemysName`) | The ceremony records the team and win, pauses the play time and shows each Pokémon's slides and page, the player's card and Oak's rating; the credits run frame by frame (text by thirds, banner tiles by halves, palette VBlanks, border scroll), with each command group's measured spill frames; after the Hall of Fame the world continues at New Bark as Continue would, after Red at Mt. Silver. Checked against the cartridge by `tests/reference-credits.spec.ts` and `tests/reference-hall-of-fame-ceremony.spec.ts`. |
|
||||
| `rom/validate.ts`, `game/rom/revision.ts` | `Makefile` (`crystal`, `crystal11`, `crystal_au`: rgbfix's game code and version), `engine/events/battle_tower/load_trainer.asm` and `trainer_text.asm` (their `_CRYSTAL11` conditionals) | The three English cartridges by SHA-1, and their revision from the header. Revision 1.0 draws Battle Tower trainers from the first 21 and picks their texts' gender from the name's sixth character; the profile's addresses hold for all three (`check:reference`). |
|
||||
| `game/rom/revision-addresses.ts` (generated by `scripts/map-revision.mjs`), `game/rom/charset.ts` | `pokecrystal11.o` patches and `.map` (RGBDS object format, rgbds(5)), `home/text.asm` (`PlaceNextChar`'s European $1d and $1e), `gfx/font/font.1bpp` in each release | The French, German, Italian and Spanish releases' addresses for every profile label, derived from 1.1 and checked against each release's bytes; their characters and text codes. `useRevision` switches the profile and characters for the cartridge `CrystalAssets` reads. The releases are recognized but not yet accepted (board #84). |
|
||||
| `game/scripts/kanto.ts` | `engine/events/specials.asm` (`SnorlaxAwake`, `TrainerHouse`), `engine/events/haircut.asm` (`BillsGrandfather`), `engine/events/diploma.asm` (`PlaceDiplomaOnScreen`, `Diploma`, `PrintDiploma`), `engine/gfx/cgb_layouts.asm` (`_CGB_Diploma`) | Snorlax wakes to the Poké Flute channel (wMapMusic) from `.ProximityCoords`; the Trainer House always fights CAL, as without a Mystery Gift partner; Bill's grandpa reads the party Pokémon shown. The Diploma is drawn from DiplomaGFX and its tilemap with the player's name in `PalPacket_Diploma`'s palette; printing it finds no printer. Checked by `tests/reference-kanto.spec.ts`. |
|
||||
| `game/scripts/magnet-train.ts` | `engine/events/magnet_train.asm` (`MagnetTrain`, `MagnetTrain_Jumptable`, `MagnetTrain_UpdateLYOverrides`, `MagnetTrain_LoadGFX_PlayMusic`, `DrawMagnetTrain`, `MagnetTrainBGTiles`, `MagnetTrainTilemap`), `engine/overworld/scripting.asm` (`Script_newloadmap`'s `MAPSETUP_TRAIN`) | The ride frame by frame both ways: the player's sprite (Chris or Kris) running its animation with the last frame flipped, the bushes' and train's row scrolls, the station's palettes with an extra palette frame and eight frames of teardown. Checked against the cartridge by `tests/reference-magnet-train.spec.ts`. |
|
||||
| `game/scripts/tin-tower.ts`, Tin Tower and Whirl Islands | `engine/pokemon/search_owned.asm` (`BeastsCheck`, `MonCheck`, `CheckOwnMonAnywhere`, `CheckOwnMon`) | `CheckOwnMonAnywhere` returns nothing with an empty party, then reads the party structs' species (an Egg counts as its species), `sBox` and the thirteen other `sBoxN` copies, matching the player's ID and the OT's first five letters (`NAME_LENGTH_JAPANESE - 2` compares, stopping early at a shared terminator); the Day-Care is not searched, preserving the original's explicitly documented bug even now that deposits run. The original searches the current `sBox` and skips its numbered `sBoxN` copy; the port searches each logical box once. The sages, Suicune, Ho-Oh and Lugia run on ported primitives (`BATTLETYPE_SUICUNE`, `BATTLETYPE_FORCEITEM`). `tests/tin-tower.test.ts` and two playthrough stages cover them. |
|
||||
|
||||
+10
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
Translate Crystal's Game Boy assembly systems into a native TypeScript game. The browser runs the translated engine; the user's original ROM supplies data and assets. The emulator is a separate development reference for checking behavior, never a dependency of the native release.
|
||||
|
||||
PRET's [disassembly](https://github.com/pret/pokecrystal) is pinned at `7a7881d0d62e0ddbd82dcf10e7116807487ac651`. Its `crystal11` build with RGBDS 1.0.3 was verified byte-for-byte against the supplied English US/Europe 1.1 ROM (SHA-1 `f2f52230b536214ef7c9924f483392993e226cfb`). Its `crystal` and `crystal_au` builds reproduce the retail English 1.0 (`f4cd194bdee0d04ca4eac29e09b8e4e9d818c133`) and Australian (`a0fc810f1d4e124434f7be2c989ab5b5892ddf36`) cartridges, which the same profile reads (board #84). Other languages require their own verified profiles.
|
||||
PRET's [disassembly](https://github.com/pret/pokecrystal) is pinned at `7a7881d0d62e0ddbd82dcf10e7116807487ac651`. Its `crystal11` build with RGBDS 1.0.3 was verified byte-for-byte against the supplied English US/Europe 1.1 ROM (SHA-1 `f2f52230b536214ef7c9924f483392993e226cfb`). Its `crystal` and `crystal_au` builds reproduce the retail English 1.0 (`f4cd194bdee0d04ca4eac29e09b8e4e9d818c133`) and Australian (`a0fc810f1d4e124434f7be2c989ab5b5892ddf36`) cartridges, which the same profile reads (board #84). The French, German, Italian and Spanish releases (SHA-1 `c055992b…`, `accb5842…`, `6cee05e5…`, `889a06fc…`) get their addresses from `scripts/map-revision.mjs`; the Japanese release is not covered.
|
||||
|
||||
The emulator-backed prototype is now at `/reference.html`. The default application is the native port. The previous plan to progressively modify an emulated ROM has been superseded.
|
||||
|
||||
@@ -556,7 +556,15 @@ The numbered sections below keep their full scope. Each one opens with a checkli
|
||||
- Saves: each cartridge keeps its own list (slots are keyed by the ROM's SHA-1), and a native backup from one of the others imports, with a "Cartridge" line in the preview; the three share every map, event and object, so nothing is converted. Cartridge `.sav` files have the same layout in all three.
|
||||
- Reference: `tests/reference-revisions.spec.ts` runs the cartridge's `BattleTowerText` on 1.1, 1.0 and the Australian rebuild for every trainer, six random values and the three texts. Tests: `tests/revisions.test.ts` (each cartridge is accepted under its name and recognised from its header, a changed byte is refused, the Battle Tower's trainer range), `tests/save-slots.test.ts` (a backup carried over between cartridges). The whole unit suite and the playthrough from the introduction to Red pass with `CRYSTAL_ROM_PATH` set to each rebuild (only the private CI downloader's 1.1 check is specific).
|
||||
- Not affected: 1.0's other differences, the Pokédex's `wPokedexStatus` sharing a byte with `wPrevDexEntryBackup` (it misplaces the cursor after a search result's entry; the search mode is not ported) and the mobile Stadium 2 tilemap.
|
||||
- Not supported: the French, German, Italian, Spanish and Japanese releases. PRET builds none of them, and no copy was available to verify a profile against; each needs its own addresses, character set and name lengths (the Japanese release most of all).
|
||||
- The French, German, Italian and Spanish releases: increment B below. The Japanese release is not covered (no copy, and it differs the most).
|
||||
|
||||
## In progress: the French, German, Italian and Spanish releases (board #84, increment B)
|
||||
|
||||
- Addresses: `scripts/map-revision.mjs` writes `game/rom/revision-addresses.ts`, where each release keeps the profile's labels. PRET builds none of these releases, so they are derived from its 1.1 build: each bank of the 1.1 ROM is aligned with the release's (unique byte runs, refined inside unequal gaps; equal-length gaps keep their offset), and everything else through the exact references RGBDS recorded while linking 1.1 (every patch in `main11.o` and the other objects, placed with `pokecrystal11.map`), read at the matching place in the release: inside an alignment run, a walk from a resolved label over equal bytes (patched bytes excepted), or the instruction around the pointer near a changed routine. Pointers vote with their bank (the bank byte beside them, the `BANK()` load, or the section's); references resolved while assembling are found by their pointer bytes in a pointer context; labels sharing an address share it; a short text that looks alike in two languages is repaired by the release's own pointers. Each result is checked against the release's bytes (equal data, its patched pointers naming the resolved targets, the same instructions, or a text boundary). Of the 1,358 ROM labels the profile pins, French, German, Italian and Spanish each leave 1-3 unfound, all in code the release changed (the Diploma's certification string; in French and German two Bill's PC strings); they are written as -1, so reading them refuses instead of using 1.1's address. Work and save RAM keep 1.1's layout except the Seer's buffers in French, Italian and Spanish. `check:reference` regenerates the file and compares it when the four ROMs are present.
|
||||
- Runtime: `CrystalAssets` reads the cartridge's revision from its header (game codes BYTF, BYTD, BYTI, BYTS) and `useRevision` points the profile tables at its addresses (and back to 1.1's for another cartridge). Two tables that copied addresses at import now read them when used.
|
||||
- Characters (`game/rom/charset.ts`): the releases' fonts, identified glyph by glyph: French and German share à è é ù ß ç ë ï â ô û ê î, the apostrophe ligatures c' d' j' l' m' n' p' s' 's t' u' y' and +; Italian and Spanish share à è é ù À Á È É Ì Í Ñ Ò Ó Ù Ú á ì í ñ ò ó ú º & ¿ ¡ and English's 'd 'l 'm 'r 's 't 'v moved to $d8. All four add two `PlaceNextChar` codes: $1d places "-" and breaks the line, $1e prints nothing (a word break). Names accept those letters and English's umlauts, which the tile encoder now knows.
|
||||
- Checked: the introduction, Mom's scene and the first steps outside play in each language (`tests/languages.test.ts`), and the local sweep of every open map (each object, sign and warp) runs in all four with the same results as in English.
|
||||
- Not yet accepted: the cartridge selector recognizes the four releases and refuses them, because parts of their own logic, which the port reimplements, differ from English's. Comparing every routine's code with 1.1's (pointers masked) lists them; besides strings and menus read from the ROM, they include the units of Magikarp's length (`CalcMagikarpLength`, `PrintMagikarpLength`, the Lake of Rage checks in `LoadEnemyMon`) and of the Pokédex (`DisplayDexEntry`), the enemy's name order in French, Italian and Spanish (`PlaceEnemysName`), the clock and calendar (`SetDayOfWeek`, `PrintHour`, `GetTimeOfDayString`, Spanish's "¿" before the hour), the start menu, pack, PC, Bill's PC, Mom's bank and mailbox layouts and texts, the naming screen, the Hall of Fame and trainer card layouts, German's Pokémon Talk and Buena texts, the Diploma, and some French-only scripts (`GiveDratini`, `CheckCaughtCelebi`). These are the next increments; each language is accepted once its playthrough passes.
|
||||
|
||||
## Native battles: current state
|
||||
|
||||
|
||||
Reference in new issue
Block a user