31 Commits

Author SHA1 Message Date
Cyphgirl 7bc8cdd3cf Extract the remaining board strip filler
The last 42 blobs are the flat part of each board strip past its
feature's DMA length. Each becomes an image beside the art it trails,
at the art's own width where the tile count allows it.

ruby_board.inc and sapphire_board.inc now hold no baserom.gba at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 20:18:20 -04:00
Cyphgirl 5d931bb272 Extract the shop signs and fill the solid tails
A scan for pointers to every remaining blob found that six of them are
whole labels, not tails: gShopItemTilePointers reaches them, and they
draw the POKeMON MART and POKeMON CENTER signs in two variants of three
pieces. Bank 6, and the tilemap puts them at x=24.

The other 57 have no pointer anywhere in the ROM. They are the flat part
of the board strip past each feature's DMA length - grey on ruby, white
on sapphire - and 15 of them consist of nothing but solid tiles, so they
need no image at all and become .space and .fill runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 20:14:55 -04:00
Cyphgirl 0fc6193243 Extract the trap indicators and sapphire's P square
Both boards' trap indicators are three states of a 2x2 block - the hole
with its shutters open, part way, and shut. Sapphire's P square is the
same shape over five blink frames.

Palettes follow the same brightness band as the earlier board work: the
band 1 copy, ruby bank 5 and sapphire bank 2 for the traps, bank 5 for
the P square. The band 0 copies of both would have given a different
answer, so this is a convention rather than a proof; the P square's
band 1 hit is a mixed-bank block and was ignored for that reason.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 19:50:09 -04:00
Cyphgirl cdd8518db7 Extract both boards' progress digits
DrawRuby/SapphireProgressDigits draws the progress level from these, so
the labels are digits, not the bonus arrows and bonus multiplier the old
names claimed. Ruby's are one tile per digit in a tens and a ones slot;
sapphire's are 8x16, so each digit is a top and bottom half in each slot.
Every label is exactly its 0x20 DMA length.

Ruby's block sits at x=19 in the board tilemap on bank 3. Sapphire's is
in BG0 at x=17, once per brightness band, and band 1 gives bank 11.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 19:46:50 -04:00
Cyphgirl 4867f38e83 Extract both boards' catch lights
Three lane slots, six states each, split into two 2-tile halves that the
tilemap stacks into a 2x2 block - the CATCH and EVO letters and the ball
icon, lit and unlit. States 0 and 1 are one label shared by all three
slots, which is why 3 x 6 x 2 pointers come to 28 labels.

The blocks sit at x=12, 14 and 16 in each board's BG0 tilemap, drawn once
per brightness band; taking the same band as the wailmer water gives ruby
bank 0 and sapphire bank 8, both of which put the lights on the playfield
tan the boards actually use.

Every label is exactly its DMA length apart from two on ruby, whose tails
become .space and one remaining incbin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 19:43:12 -04:00
Cyphgirl b4e8a6d36e Extract the wailmer water and hatch machine tiles
AnimateRotatingBackground cycles three frames over four rows of 2, 4, 3
and 3 tiles - the water turning under the wailmer. Every tail past the
DMA length is zero, so the whole family becomes .space.

The board's BG0 tilemap draws that block at x=21 three times over, once
per brightness zone, on banks 15/14, 3 and 1; bank 3 is the one whose
fronds are gold the way the board shows them.

gHatchMachineDrawSegment is ten targets, each with an idle and a hit
state split into two halves of 2 tiles, all of them copied whole - so
these 38 need no tails at all. Bank 6, the machine's red and green.

Both families had address-suffixed labels; they now say which segment,
frame and state they are.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 19:20:14 -04:00
Cyphgirl 256cc6d1f3 Extract the board slingshot and saver lights
AnimateRuby/SapphireSlingshotHit copies five rows of 1, 2, 3, 3 and 2
tiles, which is the wedge itself; every byte past a row's DMA length is
zero, so the slingshots come out whole and their tails become .space.
Ruby's wedge is BG palette bank 2, sapphire's bank 1.

The saver rows are 6, 6, 5, 2 tiles on ruby and 7, 7, 6, 2 on sapphire -
Latios over red SAVER and Latias over blue SAVER, both on bank 0. Their
tails are not blank, since other board art shares the same ROM strip, so
those stay as incbins the way the arrows above them already do.

Sapphire's labels for both were address-suffixed placeholders; they now
use ruby's naming, with gSapphireSlingshotTilePtrs and
gSapphireSaverDisplayTilePtrs updated to match.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 19:14:25 -04:00
Retnuhytnuob 6ba75e3e2f Merge pull request #239 from simisonne/sapphire-lights-dump
Dump sapphire ball power up and hole lights
2026-08-09 14:45:51 -05:00
simisonne 7258683fc6 Dump sapphire ball power up and hole lights
Extract the sapphire board's ball power up and 'hole' light tile graphics from baserom into tracked PNG sources, following the folder conventions established by the ruby board. Resolves #231.
2026-08-09 13:08:22 +00:00
Retnuhytnuob 158ba46210 naming patch 2026-08-08 15:36:15 -05:00
Cyphgirl e19e280e0d Extract the catch target collision map from baserom
gCatchTargetCollisionBitmap is not graphics. CheckCatchTargetCollision indexes it
[y * 48 + x] against the ball's offset from the Jirachi centre, bounds-checked to
48 on both axes, which accounts for the label exactly: 2304 bytes, one per
position. Bit 7 is the solid flag and the low seven bits are the surface angle
the collision answers with, scaled by 512.

So it goes to data/board_data/collision as a .bin with its dimensions in the
name, next to the flipper map and the per-board entity maps, rather than through
gbagfx. tools/collision_viewer already reads that family.

Decoding it confirms the reading: the solid cells form a disc filling 41% of the
square, and the angles of the solid cells run the full range around it, which is
what a round target should look like.

make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:55:33 -04:00
Cyphgirl bca8aeeecf Use coloured tile sheets as BG sources instead of assembled screens
Reverts the screen-based approach. Every BG sheet goes back to a plain
tile-grid PNG, which the existing %.4bpp: %.png rule builds, since
tilemap editing is covered by other tooling.

- Drop the 68 assembled screen and unmapped-strip PNGs, the 21 detilemap
  rules, tools/scripts/detilemap.py and the VRAM layout notes.
- Revert Makefile: .SECONDARY goes back to its original unscoped form.
- Write each sheet out coloured rather than restoring the greyscale
  originals. A 4bpp PNG carries one 16-colour palette, so each sheet uses
  the bank most of its cells reference; multi-bank sheets are therefore
  approximate in appearance. Pixel indices are unaffected, so all 25
  sheets still round-trip byte-exactly.
- scene4plussleminun keeps its three-segment form: the sheet is 513 tiles
  because of the align-1 padding tile and does not fit one grid.

Ruby and Sapphire keep their BG tiles and tilemaps as extracted files
rather than reverting to baserom incbins, so the 73,728 bytes those
removed stay removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 21:20:15 -04:00
Cyphgirl 37f87ccee2 Colourize board OBJ sheets, starting with the intro sprites
The 203 segment PNGs behind the seven boards' intro_sprite sheets were
checked in as 4-bit greyscale: a 4bpp sheet carries no palette of its own,
so nothing in the tree recorded which of the 16 OBJ banks a sprite is drawn
through, and every sheet was an unreadable grey blob. All seven are now
indexed colour, byte-for-byte identical through the build.

The inversion
- gbagfx converts a palette-less PNG with invertColors = !image.hasPalette
  (main.c:71), so a greyscale sheet stores 15 - index, and gaining a PLTE
  turns that off. The recolour inverts on the way in; passing the samples
  through unchanged would have quietly rewritten every tile.

Deriving the bank (tools/scripts/objpalette.py)
- data/rom_2.s spells out every OAM entry with its tileNum and paletteNum,
  and the g<Board>BoardSpriteSets tables say which a board uses. Segments lie
  end to end in tile order, so each owns a tile range and the entries landing
  wholly inside it name its bank. An entry crossing a boundary is reading a
  different sheet -- OBJ VRAM at 0x06010000 is rewritten constantly -- and is
  dropped. Second pass over the g<Board>*OamData arrays for the boss sheets,
  which the code indexes frame by frame instead of via a sprite set.
- Tile counts come from the built per-segment .4bpp: -oam and -oamshape
  segments are not width*height/64, and PNG dimensions drift ruby by 13 tiles.
- Banks holding under four distinct colours cannot win the vote. Those are
  flash states and banks a board leaves at zero, and would render solid.
- The result is recorded in each gfx config as "palette"/"palbank", so it is
  reviewable and `objpalette.py check` can verify the PNGs against it.

Scratch regions
Eight tile ranges are streamed over at run time from a table of interchangeable
art, and coloured by a palette that arrives with the replacement tiles rather
than sitting in the board's own set. Which entry the sheet ships is recoverable
by searching the ROM for the segment's tiles -- exact for all but the Geodude
portrait, matched by likeness. Listed with their writers in VRAM_LAYOUT.md.

OBJ bank 11 on the main fields is never its resting ROM value either:
UpdateSpoinkAnimation rewrites it every frame from gFieldPaletteVariants,
three brightness sets crossed with which half of the board is on screen.

Ruby's palset_0 is 0x120 bytes, not the 0x200 the DMA copies, so its OBJ banks
9-15 come from the next label, gBonusStageObjPal. Sapphire leaves banks 12-15
at zero and loads them when the sprites that use them appear.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 14:45:17 -04:00
Cyphgirl 1d3441da53 Extract palettes and rebuild BG sheets from assembled screens
Removes 74 baserom incbins (104,512 bytes) and makes every background
tile sheet in the game readable and correctly coloured, byte-for-byte
identical to the ROM throughout.

Palettes
- Extract 69 standalone palettes to JASC .pal, built via the existing
  %.gbapal: %.pal rule. gbagfx's gbapal->pal direction pads anything that
  is not 16 or 256 colours up to 256 (gfx.c:822), so the JASC is written
  directly from the ROM bytes and gbagfx only does pal->gbapal.
- Convert the 9 legacy raw-binary .pal files to JASC, so a single .pal
  format exists. dusclops_anim held 496 colours, over the JASC limit, so
  it becomes 31 per-frame files.

BG sheets (tools/scripts/detilemap.py)
- BG tile sheets are deduplicated, so the raw sheet is unreadable as an
  image at any width. The checked-in source becomes the assembled screen
  (8bpp, pixel = bank*16 + index, which keeps every per-cell palette bank
  distinct) and detilemap folds the screens back through their tilemaps.
- Covers all 9 intro scenes and all 8 boards: 21 sheets.
- Ruby and Sapphire stream their board through a 22-slot scroll ring, so
  a tilemap row R draws chunk R of a virtual bgtop+bgbottom strip rather
  than indexing the sheet. --row-chunk handles that; the model is written
  up in data/board_data/VRAM_LAYOUT.md.
- 8 sheets have non-blank tiles no tilemap reaches (BG0 tilemaps are built
  at runtime); --extra carries them rather than silently zeroing.

Build
- .SECONDARY was unscoped, which marked $(OBJS) secondary too. Make does
  not rebuild a missing secondary file when its dependant looks current,
  so deleting an object and running `make compare` relinked a stale ELF
  and printed OK. Now scoped to generated art and audio. .PRECIOUS with
  patterns would be terser but defeats .DELETE_ON_ERROR, which would let
  a half-written .4bpp produce a wrong ROM silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 13:50:59 -04:00
Retnuhytnuob ac86c24005 Merge pull request #235 from Cyphgirl/extract-shop-data
Extract shop data
2026-08-07 03:37:59 -05:00
Cyphgirl 73ca456669 Extract the coin arrows for both boards
57 pieces out of baserom.gba, in colour. ROM still matches
(make compare).

Three groups per board, one per coin value, each with lit variants of
three pieces. The two boards differ in table shape: ruby has four
variant rows with the destinations fifth, sapphire has three plus a
destination row and a row of spacers. Ruby repeats the first variant in
groups 0 and 2, so 57 distinct pieces cover the 72 slots.

Piece 2 of groups 0 and 1 is never copied on either board -- their
destination overlaps the next group's piece 0 -- so there is no DMA size
to take. Those 13 are extracted at their full declared span instead.
Everything else uses the size from DrawRubyCoinRewardMeter and
DrawSapphireCoinRewardMeter.

Palettes are bank 9 of gRubyBoardPalette and bank 11 of
gSapphireBoardPalette, both tilemap-attested. The two boards do not
match here: ruby's coin arrows are green with white numerals, sapphire's
are light blue with white numerals, which is what the field scans show.
Sapphire bank 0 is also blue and scores within 3 RGB units of bank 11
against the scan, but it is navy where the board art is clearly light
blue, so the render decided it rather than the distance.

Left alone: graphics/stage/ruby/ruby_coin_arrows.png, an earlier
extraction of the whole 0xC00 block as one 256x24 sheet, still
referenced by a commented-out incbin above the first symbol. It matches
baserom exactly but cannot be wired up without collapsing the 30
individual symbols, which is presumably why it was left commented. It is
now redundant and could be removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 23:31:17 -04:00
Cyphgirl b7a58383ea Extract the get arrows for both boards
60 pieces out of baserom.gba, in colour. ROM still matches
(make compare).

Same shape as the evo arrows: three letter groups spelling GET, each
with a set of variants for the lit progression, each variant three
pieces going to three VRAM destinations one tilemap row apart. Group
sizes differ per piece (0x40 or 0x60) and were taken from the
DmaCopy16 calls in DrawRubyCatchArrowProgress and
DrawSapphireCatchArrowProgress. Groups 0 and 2 repeat their first
variant, so 60 distinct pieces cover the 72 table slots.

30 symbols span more ROM than is copied; the remainder is .space where
blank (22) and a baserom incbin otherwise (8).

Palettes are bank 8 of gRubyBoardPalette and bank 11 of
gSapphireBoardPalette. Both are referenced by their board tilemap at
these tiles and both render the arrows red-orange with a white letter,
matching the Ruby and Sapphire Field scans. Index layouts differ per
board again: ruby draws the body at index 5, sapphire at index 7.

Note these are the "Catch 'em Arrows" in the Bulbapedia legends; the
symbol and function names disagree with each other already
(gRubyGetArrowTilePtrs drawn by DrawRubyCatchArrowProgress) and were
left alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 23:23:35 -04:00
Cyphgirl 4c6f2013a4 Extract catch, hatch, mart and bumper arrows for both boards
78 arrow rows out of baserom.gba, in colour. ROM still matches
(make compare).

Each arrow is four rows that go to four separate VRAM destinations one
tilemap row apart, with an off and an on state (mart has four states:
mart off/on and evo off/on, since the ramp is shared). Row widths are
not uniform -- they run 2 to 5 tiles -- so the copied size per row was
taken from the DmaCopy16 calls in ruby_board_indicators.c and
sapphire_board_indicators.c rather than assumed from the symbol spans.

22 symbols span more ROM than the game copies. Only the copied part is
extracted; the remainder stays as a baserom incbin, or .space where it
is blank (6 of them).

gSapphireBumperArrow_082E0860 and _082E08E0 are skipped: they are
already .space, and both the table comment and the fact that
AnimateSapphireBumperArrowPalette only copies rows 1-3 confirm row 0 is
unused.

Palette is bank 11 of gRubyBoardPalette / gSapphireBoardPalette. That
bank is the only one on either board that is both yellow at the arrow
body index and actually referenced by the board tilemap at these tiles.
The two boards do not share index layouts -- ruby draws the body at
index 10 with outline 15, sapphire at index 4 with outline 11 -- so each
was decoded separately. Checked against the Ruby and Sapphire Field
scans on Bulbapedia, where all four arrow types on both boards are
yellow with a dark outline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 23:18:37 -04:00
Retnuhytnuob 01b02dc2ef spheal tilemap 2026-07-28 20:59:46 -05:00
Retnuhytnuob eb56664fb2 sapphire tiles 2026-07-28 16:13:35 -05:00
Retnuhytnuob 99c486a41d ruby tilemap 2026-07-28 15:57:50 -05:00
Retnuhytnuob 1cbf96eb77 kecleon, kyogre, rayquaza 2026-07-28 15:37:05 -05:00
Retnuhytnuob bfa32b4177 flipper collision data 2026-07-22 23:04:49 -05:00
Retnuhytnuob 136bff1e6a groudon board tilemaps 2026-07-22 22:24:45 -05:00
Retnuhytnuob f1b7c73450 dusclops board tiles 2026-07-22 21:53:57 -05:00
Retnuhytnuob 1d633482cf big checkpoint 2026-06-01 02:31:46 -05:00
Retnuhytnuob 4d87510ada board collision var names 2026-05-31 21:20:50 -05:00
Retnuhytnuob 622d7678c5 collision checkpoint 2026-05-31 01:15:45 -05:00
Retnuhytnuob dd7efbf409 Add collision view/edit tool 2026-05-21 04:25:05 -05:00
Retnuhytnuob c75a93182f Renaming of board related src files, Prize state naming, board/bonus board state/substate naming (#214)
* name prize array, start on main board state

* next board state const refs

* main/groudon board states

* spriteGroup Const initialize

* undo SG_ consts for now - Later PR

* sprite group active bool checkpoint

* sprite group active conditions

* center screen display

* board substate initial enum tagging

* board intro mode; bonus board substates

* folderization; partial

* catch tile naming

* remaining board folderization (2 intended exceptions)

* center catch substates

* Finished Catchem substates

* finish jirachi substate mode

* baby mon substates

* clean up center screen in-file naming

* defolderization, evo/travel substates

* pendingModeChangeFlags

* Kecleon board state naming

* spheal board states

* fix unknown enum name

* kyogre initial state enum

* kyogre const checkpoint

* kyogre state enums finished

* data extraction for roulette wheel sets, state for special capturestate

* rayquaza states, roulette prize option dump
2026-05-09 08:06:24 -05:00
Retnuhytnuob 1b72dfe8ad Datadump rom1: 0x08118680 - 0x0825DE70 graphics/simple arrays (#213)
* dump u8 * pointers

* through 08137DA4

* checkpoint 08138014

* travel paint

* sapphire travel paint

* painter palette rename

* ruby evo arrows

* prenaming sapphire evo arrows

* sapphire evo arrows

* raw ruby coin arrow dump

* rename capture ball variants

* indicatorLight naming

* carve pointer array at 0x086AFCB0

* dump/carve gFieldBoardConfigs 0x086AFD70

* dumped gKecleonSpriteYSortData:: @ 0x086AEA70

* dump spheal flight path data

* merged the 2 SphealFrameset Arrays

* naming of spheal/sealeo frameset data

* dump gOptionsSpriteSets and potential data block after the Pokepinagb string

* add comment with original name of raw fu the 2 raw functions

* add comment with original name of the 2 raw functions

* folderized board lights

* split Ruby Light file, name ruby light unknowns

* Sapphire board lights

* class standardization: board collision data set

* board data folderization

* 4 banner graphics, unk naming

* added m4a_tables.c

* move comment point for m4a_tables

* agb_sram rodata

* incorporate remainder of m4a_tables file.

* Move AgbLibSramVersion into src/agb_sram.c

* Dump se fragment

* Don't hardcode gDeltaEncodingTable address

* Don't hardcode AgbMain in crt0.s

* Cleanup gAreaPortraitIndexes name

* More name cleanup

---------

Co-authored-by: Marcus Huderle <huderlem@gmail.com>
2026-04-14 17:39:37 -05:00