38 Commits

Author SHA1 Message Date
Retnuhytnuob 14547ba828 Cleanup AI comments, add usage disclaimer. (#264)
* cleanup data dump comments
* AI usage disclaimer
2026-09-12 22:59:53 -05:00
Retnuhytnuob f3f8ba893d last of the Pals 2026-08-14 03:15:20 -05:00
Retnuhytnuob ef29d1b7a0 additional pal, and char block addressing 2026-08-13 02:44:26 -05:00
Cyphgirl cff0082a52 Extract the rom_1.s tilemap blob and correct two sheet names
Move the 854-byte blob after gSapphireBoardZigzagoonFx_Gfx out of inline
.2byte data into graphics/stage/unknown_085279CC.bin, labelled
gUnknown_085279CC. Both ends are pinned by real references, so its extent
is exact: the 0xC00 gfx copies above it and the pointer to gOptionsBGMList
below. Nothing reaches it -- no pointer in the ROM lands inside it at any
alignment, and .text ends 4.9MB earlier, so PC-relative forms cannot span
the gap either.

Rename bumper_plusle_fx_tail to compressed_numbers and give it its own
label at 0x084ECF6C. It is a narrow digit font holding 0-9 plus a trailing
glyph, not overrun tiles from the Plusle hit bumper sheet it was filed
under -- it sits past the whole mon_portraits block, nowhere near that
sheet's stride.

Rename the capture_screen "unreferenced gap" segment to
capture_screen_absorb_fx_frame_0. The sheet loads at OBJ tile 704, and
shifting gMonCatchBallAbsorbPokemonFxSpriteSet's ball-adjacent entries back
by 21 tiles maps t123/t127/t129 onto t102/t106/t108 -- the two ball
sub-slots are 21 tiles apart, one per absorb frame.

ROM stays byte-identical to baserom.gba throughout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 21:20:20 -04:00
Retnuhytnuob 158ba46210 naming patch 2026-08-08 15:36:15 -05:00
Cyphgirl c15304397d Extract the last of the rom_1.s baserom blobs
The remaining seventeen, in one pass. Nothing in rom_1.s reads baserom.gba any
more; the two lines that still name it are commented out and predate this work.

Seven are ordinary slot sheets and went in the usual way, each with a SLOTS entry
that derives its bank from full coverage: the flying Rayquaza (one 67-tile sprite
of 14 pieces, three sub-tile phases so the shape approximates two of them), the
Kyogre surfacing splash, both Chikorita projectile sheets, the hatch machine
spark, the Linoone side bumper, and the decimal digits. The digits get the same
treatment as the alphabet, one 8x16 glyph per metatile laid ten across, so the
sheet reads as 0123456789.

Four are not graphics. gPokedexInfoWindowTiles is 1024 BG map entries and becomes
a .bin. gPokeballCaptureOamFrames is 40 framesets of six OAM entries and is
listed as raw attributes, three halfwords a line, since nothing has confirmed the
field split. The tail under gSapphireBoardZigzagoonFx_Gfx is 427 halfwords that
no pointer reaches and whose length is not a multiple of the tile size, so it is
listed flat too. gDefaultCustomButtonConfigTileIds is ten bytes and becomes
.byte.

Three are palette tails past the end of an indexed set, and they exposed
something: *.gbapal is gitignored here, because every palette in the tree is a
build product of a .png that carries it. A palette with no art has nothing to be
generated from, so a bare .gbapal would have been untracked and deleted by the
next make clean. They take the .gbapal.bin suffix instead, which is what
gb_player.gbapal.bin already does for the same reason.

Verified past the usual gates: make clean, rebuild every tool, rebuild the ROM
from the checked-in sources alone -- pokepinballrs.gba: OK. That is what would
have caught the palette mistake, and is worth doing whenever an extraction adds a
file type the build generates.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 10:17:50 -04:00
Cyphgirl d510ebd0df Extract the catch tile burst start sheet from baserom
gCatchTile_BurstStart_Gfx is 8 frames of the lightning strike drawn by
gCatchTile_RevealOamFramesets, and it is the last of the burst family. Six frames
have their pieces on one sub-tile phase and consume the slice in order, so they
take shapes; frame 4 has a piece 4px off and takes an approximate one.

I had this filed as blocked, on the grounds that frame 6 contains an overlap no
layout can express. That was half right and it cost the other seven frames. The
overlap is real -- a 16x16 at t204 and a 16x32 at t207 both claim t207, so that
frame's tiles are not one run and it stays a strip -- but it says nothing about
the rest of the sheet, and treating the whole thing as blocked was the error.

The frames were worth generating rather than hand-computing: 6 shapes, up to
78 cells each. The helper takes a frameset's piece list and either returns a
shape or explains why not. It rejects on three grounds, and the third only turned
up here: mixed sub-tile phases, two pieces landing on one grid cell, and pieces
that do not consume the tile slice in order. That last check is what correctly
sends frame 6 to a strip -- a grid-overlap test alone accepts it, because the two
pieces do not collide on screen, only in the tile numbers they read.

t219 is unused and t240..255 are unreferenced. Palette from
gCatchTile_BurstStart_Pal into OBJ bank 14, so no vote.

Rebuilt sheet matches the ROM blob before and after recolouring, check passes,
make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 10:05:08 -04:00
Cyphgirl 91f1f7dfe1 Extract the catch tile burst stage 2 sheet from baserom
gCatchTile_BurstStage2_Gfx is 16 sprites of 16x16, a uniform 2x2 grid with
nothing irregular anywhere in it, so one -oam entry covers the whole sheet.

The framesets are what make it look harder than it is.
gCatchTile_BurstRevealOamFramesets0 draws six 16x16 pieces at a time into a 3x2
block of cells, reusing the same few sprites in different arrangements, so the
sheet is a bank of quarters the animation picks from rather than a run of whole
frames. Reading the tile numbers as frame boundaries is what made this look like
an irregular composite earlier; reading them as cell contents makes the grid
obvious. Only t0..35 are ever named, and t36..63 are unreferenced but still
inside the copy.

Palette from gCatchTile_BurstStage2_Pal into OBJ bank 14 in the copy right after,
so no vote and no overrides.

Rebuilt sheet matches the ROM blob before and after recolouring, check passes,
make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 10:00:53 -04:00
Cyphgirl 81bab75500 Extract both pond bumpers from baserom
gRubyFlashingTiles_Secondary is the Chinchou pond bumper. It and gLotadBumperTiles
are the two branches of one loop in ruby_process3_entities_2.c, which picks a
bumper by field variant and streams 0x100 into tile 372 + 8i for the three of
them. gRubyBumpersSpriteSet draws each as a single 16x32, twice side by side, so
both sheets are 2 tiles wide and 11 frames tall.

Taken as a pair for the same reason as the ball spawn glows: one DMA site, one
sprite set, one geometry, so the second sheet costs nothing once the first is
understood. Both palettes are settled rather than voted -- each branch loads its
own into OBJ bank 9 in the copy right after the tiles, and both gbapals were
already extracted under matching names.

The renders confirm the naming: Chinchou comes out blue with yellow antennae,
Lotad green with its pad. That is the sixth label in this series describing
something other than its contents, and as usual the sprite set and the palette
label both had it right.

Both rebuilt sheets match the ROM blob before and after recolouring, check
passes, make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:52:40 -04:00
Cyphgirl dd98b58c23 Extract the ball upgrade fx frames from baserom
gBallShadowTileGraphics is not a shadow. The group all_board_process7.c streams
these into is FIELD_SG_BALL_UPGRADE_FX, gBallUpgradeFxSpriteSet draws them as a
single 32x32 over tile 247, and the palette is bank 1, the ball's own. 6 frames
of 0x200, 4 tiles wide, no OAM packing. gBallShadowTileIndices and
ballShadowTimer in ruby_board_indicators.c carry the same wrong name.

The colours are what caught it. Bank 1 rendered the frames as pink sparkles and
expanding rings, which is wrong for anything called a shadow, so it looked like
another palette mistake. It was the label that was wrong: reading up to where
spriteGroup is assigned named the group outright, and the art is the sparkle that
plays when the ball upgrades. Fifth mislabelled symbol in this series, and the
first found by disbelieving a render rather than by resolving the slot.

Rebuilt sheet matches the ROM blob before and after recolouring, check passes,
make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:48:36 -04:00
Cyphgirl 6d2cffda06 Extract the flipper frames from baserom
gFlipperTileGraphics is 6 angles of 0x200, copied into tile 0 for the left
flipper and tile 16 for the right. Each is a single 32x32 from
gBonusBoardLeft/RightFlipperSpriteSet in palette bank 0, so the sheet is 4 tiles
wide with no OAM packing and no shape.

These are the same two slots the shared bonus-board block fills at t0..31 on every
board's intro sheet, which is why bank 0 is already the answer ruby and kyogre
record for their own copies of the flipper.

Rebuilt sheet matches the ROM blob before and after recolouring, check passes,
make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:45:00 -04:00
Cyphgirl ef693dd40c Colour the launcher Spoink from the field variants, not the resting bank
The sheet went in with bonus_stage_obj bank 2, which is what the vote returns for
OBJ bank 11 and is wrong: that is the yellow-green set, and Spoink is grey with a
pink crest.

Bank 11 on the main field does not come from the board's own palette set.
field_variants supplies it, three brightness sets crossed with the half of the
board on screen, and Spoink is the plunger at the bottom, so it takes the
lower-half variant. Ruby's intro sheet already carries exactly this binding for
its own copy of the sprite, with a comment explaining the split -- the answer was
in the tree and the vote talked me out of looking for it.

The pixels never changed, only the PLTE chunk, so the sheet still rebuilds byte
for byte and make compare still passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:41:50 -04:00
Cyphgirl 30c880b502 Extract the launcher Spoink frames from baserom
gSpoinkEntity_Gfx is 7 frames of 0x1C0 over tile 263.
gSpoinkLauncherSpriteSet draws a 16x32 with an 8x32 beside it and a 16x8 below,
14 tiles, in palette bank 11, which the vote finds on its own.

The shape carries one approximation and it is worth naming. The body pair sits on
the tile grid, but the lower strip is offset 4 pixels -- half a tile -- so it
cannot share that grid, and the shape places it at the nearest column instead.
Everything else is exact and the sheet still rebuilds byte for byte, since the
shape only decides where tiles land in the PNG.

Elsewhere a phase mismatch like this has meant falling back to plain strips, and
that would have been the consistent choice. It is the wrong one here: a strip
slices the 16x32 body into bands and the sprite becomes unreadable, whereas the
shape shows seven legible Spoinks for the cost of a 4px lie about one strip.
Ruby's intro sheet takes the third option and splits the sprite at that seam, as
spoink and spoink_tail, which does not survive 7 frames interleaving.

Rebuilt sheet matches the ROM blob before and after recolouring, check passes,
make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:38:02 -04:00
Cyphgirl d56ac1f452 Extract the capture mode sheet from baserom
gCaptureModeTilesGfx is the two egg deliveries, over the tile-704 overlay slot
with gCaptureModePalette going to OBJ bank 14 in the copy above it. Six segments.

The two animations interleave rather than sit side by side.
gRubyAerodactylEggDeliverySpriteSet holds t0..30, gRubyTotodileEggDeliverySpriteSet
holds t28..34 and t56..77, and the two overlap at t28..30, so no cut separates
them and t0..34 stays one segment.

The Totodile egg at t72..77 draws in bank 11 while everything around it is bank
14, which is why it is split out rather than left inside the body segment: one
palette over the whole sheet would have coloured it wrong. The derive vote found
both banks on its own and agreed with the paletteNum fields in the sprite set,
which is the first time on a 704-slot sheet it has been trustworthy without
cross-checking.

t35..55 and t78..100 are unreferenced. t101 is past the copy, a solid colour-1
tile, and takes an override since nothing names it.

Rebuilt sheet matches the ROM blob before and after recolouring, check passes,
make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:31:41 -04:00
Cyphgirl 4b073789bc Extract the alphabet tiles from baserom
gAlphabetTilesGfx is 52 entries of 0x40, A-Z then a-z, each letter an 8x16 pair
of tiles. The Pokemon name display copies one letter at a time into tile 704 + 2i
and gLegendaryCatchNameBannerSpriteSet draws the ten slots in palette bank 1,
which the vote finds on its own from full coverage of the two-tile slot.

Laid out as 1x2 metatiles 26 across, so the sheet reads as uppercase over
lowercase the way the font is actually ordered. That is the only arrangement that
shows what this is -- a plain strip renders it as an unreadable ribbon, and the
letters are two tiles tall, not two wide.

gSpaceTileGfx below it is already the blank the same loop substitutes for a
space, so the pair now reads together in the source.

Rebuilt sheet matches the ROM blob before and after recolouring, check passes,
make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:27:49 -04:00
Cyphgirl 58ca11a0ce Extract the egg mode sheet from baserom
gEggModeTilesGfx goes over the tile-704 overlay slot with gEggModePalette to OBJ
bank 14 in the copy above it. gEggFloatOamFramesets draws three frames, each a
32x32 with a 16x8 under it and a 16x32 and 8x32 beside it, and each drawn at
three y offsets a pixel apart for the float bob.

Only the first frame has every piece on one sub-tile phase, so it is the one that
takes a shape -- 31 tiles in a 7x6 with three spacer runs. The other two put a
single piece one pixel off phase and stay strips. That also settles their widths:
31 is prime, so the shape is what makes that frame presentable at all.

The label is 0x1020 but the copy is 0x1000, and the layout inside is unusual
enough to be worth spelling out. t88..127 are blank yet still inside the copy, so
they are .space rather than a sheet of empty tiles, and t128 sits past the copy
and is not blank, so it gets its own small file after the .space. The label is
therefore two incbins around a gap.

Assembled sheet plus gap plus cap matches the ROM blob exactly, before and after
recolouring. check passes, make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:20:56 -04:00
Cyphgirl 1c9506ca2d Extract the catch mon appear fx from baserom
gCatchMonAppearFx_Gfx is the puff the caught mon appears out of, over the
tile-704 overlay slot. gCatchMonRevealOamFramesets draws it in seven framesets: a
16x16 spark, then six puff frames built from a 32x32 with a 32x8 under it and an
8x32 beside it.

The five later frames add an 8x8 corner and fill a 5x5 block exactly, so they
need nothing but -oam. The first frame is the same three pieces without that
corner, so it takes a shape with one spacer. What makes both work is that the
pieces sit at x=4 and x=36, y=-4 and y=28 -- none of them a multiple of 8, but
all sharing the same sub-tile phase, so their offsets from each other are whole
tiles. Absolute alignment is the wrong thing to test; relative alignment is what
decides whether a shape exists.

Palette came free: gCatchMonAppearFx_Pal is DMA'd to OBJ bank 14 in the copy
right below the sheet's, so one SLOT_PALETTES entry covers all four segments and
the 704 slot's crowd of bank 14 sheets never gets a vote.

t153..159 are unreferenced. Rebuilt sheet matches the ROM blob before and after
recolouring, check passes, make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:17:11 -04:00
Cyphgirl 5a0b1330b6 Extract both ball spawn glow sheets from baserom
gBallSpawnGlowTiles_Type1 and _Type2 are 11 and 9 frames of 0x200, streamed a
frame at a time over tile 57 by all_board_process7.c. That slot belongs to
gBonusBoardBallRespawnFxSpriteSet, a single 32x32, so both sheets are 4 tiles
wide with no OAM packing and no shape. Type 1 is a swirl collapsing into a ring,
type 2 a smaller sparkle dissipating.

Taking them as a pair was worth it: same slot, same geometry, same palette
question, so the second cost almost nothing once the first was understood.

Both needed an explicit palette. Tile 57 is one of the lowest slots and half the
game's screens put something there, so the vote lands on whichever set covers
most of it -- here gPikaChargingSpinnerSpriteSet with 9 of 16 tiles, in bank 3.
The set that actually reads the slot covers all 16 in bank 1, which is where the
board keeps gBallPalettes.

Both rebuilt sheets match the ROM blob before and after recolouring, check
passes, make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:13:44 -04:00
Cyphgirl fc462c4942 Extract the Rayquaza tornado frames from baserom
gRayquazaMinionOrbFrames is not orbs. rayquaza_process3.c streams it into tile
245 and gRaquazaTornadoSpriteSet reads that slot: a 32x32 over a 32x8 in palette
bank 12, which tiles into a clean 4x5 block. Nine frames, one segment, and the
render is a funnel spinning up.

Fourth mislabelled symbol of the series, and the sprite set named the art
correctly every time. Worth noting the sets spell it Raquaza while the graphics
labels spell it Rayquaza, so a prefix search for one misses the other --
objpalette already carries both spellings for this reason.

Rebuilt sheet matches the ROM blob before and after recolouring, the slot derives
bank 12 on full coverage with no override, check passes, make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:10:30 -04:00
Cyphgirl aefbead510 Extract the Kyogre vortex minion frames from baserom
gKyogreWhirlpoolMinionSprites is 11 frames of 0x200, streamed into whichever of
the two 16-tile slots at t169 and t185 the entity holds. gKyogreWhirlpool0/1SpriteSet
draw each as a single 32x32 in palette bank 6, so the sheet needs no OAM packing
at all -- 4 tiles wide, 46 rows. The 8 tiles after the eleventh frame are past the
end of the animation and no slot copy reaches them.

This label was already half extracted. graphics/stage/gunk_084FF90C.png and its
config entry existed from an earlier pass, but they were wrong: rebuilding that
PNG gives 6144 bytes against the blob's 5888, because it was cut as 4x4 metatiles
12 wide, which is 192 tiles rather than 184. The incbin was never swapped, which
is presumably why it went unnoticed. Both are replaced here.

Rebuilt sheet matches the ROM blob before and after recolouring, the slot derives
bank 6 on full coverage, check passes, make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:04:59 -04:00
Cyphgirl f364af2cee Extract the ruby Chikorita frames from baserom
gRubyFlashingDecorationTiles is not decorations. ruby_trigger_targets.c streams
it into tile 313 for SG_RUBY_CHIKORITA, and gRubyChikoritaSpriteSet is what draws
it: a 32x32 over a 32x16 in palette bank 3. That tiles into a clean 4x6 block
with nothing left over, so the whole sheet is one segment, 8 frames stacked.
The render is eight frames of a Chikorita, which settles it.

That is the third mislabelled symbol in this series, after the Kyogre whirlpool
that is a freeze trap and the Groudon background that is effect art. The pattern
is the same each time: the label describes where the art was thought to go, the
DmaCopy16 destination and the sprite set that reads that slot describe what it
actually is.

Rebuilt sheet matches the ROM blob before and after recolouring, the slot derives
bank 3 on full coverage with no override needed, check passes, make compare
passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:01:26 -04:00
Cyphgirl f89366614e Extract the catch tile burst stage 4 sheet from baserom
gCatchTile_BurstStage4_Gfx is 9 frames drawn by gCatchTile_BurstRevealOamFramesets1
over the tile-704 overlay slot. This is the burst family's easy case: every tile
group is contiguous and none of them overlap, so the frames cut cleanly at the
boundaries the framesets give. t158..191 are unreferenced.

Three frames have all their pieces on multiples of 8 and take oam-shapes, two of
them sharing one shape since their geometry is identical -- a 16x32 beside a
16x16 in a 4x4 with a 2x2 hole. Frames 4 and 5 are plain 6x4 blocks of a 32x32
plus a 16x32. The rest put one piece at an offset that is not a multiple of 8,
frame 3 at x=9 and frame 6 at y=-5, so they stay strips.

The palette is settled rather than voted: gCatchTile_BurstStage4_Pal is DMA'd to
OBJ bank 14 beside the sheet, so one SLOT_PALETTES entry covers all ten segments
and the 704 slot's foreign sprite sets never get a say.

Rebuilt sheet matches the ROM blob before and after recolouring, check passes,
make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 08:58:45 -04:00
Cyphgirl f44fdf5d6f Extract the capture cutscene sheet from baserom
gCaptureScreenTilesGfx goes over the tile-704 overlay slot from three call sites.
Six segments: the float-up fx at t0..101 from gPokemonFloatOamFramesets, the ball
absorb fx at t127..222 from gMonCatchBallAbsorbPokemonFxSpriteSet, and between
them the two sub-slots at t102..105 and t123..126 that gCaptureBallTilesGfx
streams the ball itself into, a gap and a tail.

This one does not end in a blank tile. The copy is 0x1C00 but the label runs
0x1C20, and that last tile holds real pixels, so it stays in the sheet instead of
becoming .space the way the others did. Worth checking per blob rather than
assuming, since five sheets in a row had a blank there.

Four of the six segments needed palette overrides. The float-up fx votes on full
coverage and is bank 14, but everything from t123 on is the ball and its absorb
fx in bank 1, and a banner sheet sharing the slot outvoted that with 88 tiles of
bank 14. gMonCatchBallAbsorbPokemonFxSpriteSet names bank 1 throughout, and the
ball slots take gBallPalettes, which the board loads into bank 1.

Rebuilt sheet matches the ROM blob before and after recolouring, check passes,
make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 08:55:30 -04:00
Cyphgirl 1f66572d05 Extract the Rayquaza wind attack sheet from baserom
gRayquazaWindBoardGfx goes over the tile-704 overlay slot when the entity enters
its flyby. Five segments, cut where the owning sprite set changes: the flyby
Rayquaza at t0..101 in palette bank 15, the three speedline tiers at t102..151,
t152..185 and t186..207 in bank 12, and an unreferenced tail at t208..223.

The segments are plain strips rather than shaped composites, and that is not
laziness. The flyby entity is 18 pieces whose positions are not multiples of 8 on
either axis -- x offsets land on all eight sub-tile phases -- and rounding them to
a tile grid makes two of them overlap at column 17, so no oam-shape can
reassemble the sprite. The widths only divide the tile counts; they carry no
meaning about layout.

Bank filtering mattered here for a second reason: this slot also holds the sky
background sheet extracted earlier, so gRaquazaIntroCloud* entries land on these
same tiles without belonging to this sheet. The four real segments vote on full
coverage and agree; the tail took an override, since the bank 8 found there is
another mode's sheet.

Rebuilt sheet matches the ROM blob before and after recolouring, check passes,
make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 08:51:11 -04:00
Cyphgirl ab418558d7 Extract the Groudon board effect sheet from baserom
gGroudonBoardBackgroundGfx is not a background. It is the Groudon board's OBJ
effect art, streamed whole over the tile-704 overlay slot, and three animations
share it: gGroudonProjectileAttackOamData at t0..51, gGroudonProjectileOamData
at t52..163 and gGroudonBallGrabOamData at t164..255. Ten segments, cut at the
boundaries those tables give. t50..51 fall inside the debris segment and
t240..254 are the unused tail; only t255 of that tail is ever named.

Four segments are clean rectangles and take -oam: the projectile spawn (3x2),
its six flight frames (3x4), the large burst (6x4) and the ball-grab hold (4x3).
The rest have pieces at offsets that are not multiples of 8 -- the launch fx puts
an 8x16 seven pixels below a 32x8 -- so no tile grid describes their on-screen
layout and they stay plain strips at a width their tile count divides.

The palette needed overrides on five of the ten segments. derive votes over the
whole board sprite-set table, and the 704 slot is shared with other modes, so
foreign sets reached these tiles and won with banks 1 and 8. Every gGroudon*
entry landing in 704..959 names bank 12 and nothing else.

Rebuilt sheet matches the ROM blob before and after recolouring, check passes,
make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 08:46:51 -04:00
Cyphgirl f2b1ebdcd5 Extract the ruby ramp Makuhita frames from baserom
gRubyMakuhitaGfx is 10 frames of 0x300 streamed over tile 428 by ruby_ramp.c.
gRubyMakuhitaSpriteSet draws a frame as a 32x32 at the origin, an 8x32 beside it
and a 32x8 under it offset 8px right, which is a 5x5 block with the bottom-left
tile unused -- so it takes an oam-shape with one spacer rather than -mwidth.

Worth noting the shape is not guessable from round-tripping: a plain 4-wide
render of this sheet also comes back byte-exact and still looks like a Makuhita,
because the 8x32 column is narrow enough to pass at a glance. Only the sprite
set settles it. Ruby's intro sheet reaches the same art as two segments,
makuhita_top and makuhita_feet, which is not an option here -- the frames would
have to interleave.

Rebuilt sheet matches the ROM blob, objpalette derives bank 8 from the slot,
check passes, make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 08:41:12 -04:00
Cyphgirl daf2fc6444 Extract the catch tile burst stage 3 sheet from baserom
gCatchTileParticleOamAttributes was cut in half in rom_2.s -- two halfwords
under the label and the other 0x20 stranded as gUnknown_086B4E42 -- which left
the burst particle geometry underivable. adc7a10 finished that table, and it
reads as six rows: four 64x64 frames at tiles 704, 768, 832 and 896, replayed
832, 768.

So gCatchTile_BurstStage3_Gfx is four 64x64 frames of the catch tile collapsing,
one sprite per frame, which needs no OAM packing at all -- a plain 8-tile-wide
sheet. Its palette comes from gCatchTile_BurstStage3_Pal in the copy beside it,
so it takes a SLOT_PALETTES entry like the reveal sheet.

The same table does not explain the other burst stages: stage 2 and stage 4 are
drawn through gCatchTile_BurstRevealOamFramesets0/1 as irregular composites, and
render as scrambled blocks at width 8. They still need per-frameset shapes.

It does close one loose end on the reveal sheet: the run at rel 68..102 that no
sprite set reached is gHatchAnimOamAttributes -- four 8-tile frames plus three
single-tile specks.

Rebuilt sheet matches the ROM blob, objpalette check passes, make compare passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 08:36:38 -04:00
Cyphgirl 3967addd7f Extract the Kyogre freeze trap frames from baserom
gKyogreWhirlpoolSpriteFrames is 9 frames of 0x3C0 streamed over tile 295 by
KyogreProcess3. The label is a misnomer: the only code that reads it is the
freeze trap, through gKyogrefreezeTrapAnimFrameset and
gKyogreFreezeTrapSpriteSet, and the art is an ice block forming and shattering.
The whirlpool is a different sheet -- the intro sprite segment of that name.

Each frame is 4 OAM pieces, a 32x32 over a 16x32 with a 32x8 and a 16x8
beneath, which tiles into a 6x5 block. Frame 0 is byte-identical to the 30
tiles the intro sheet already carries as iceberg + ice_bits, so the board
starts on frame 0 without a copy.

The rebuilt sheet matches the ROM blob, objpalette check passes, and make
compare still passes. Note that make compare needs tools/preproc rebuilt first
now that .ereadertext exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 08:31:33 -04:00
Cyphgirl ad2db56f71 Extract the four largest rom_1.s OBJ sheets from baserom
gSphealIntroSprites_Gfx, gCatchTile_RevealTilesGfx, gRayquazaSkyBackgroundGfx
and gBoardActionTilesGfx were the four biggest baserom incbins left in rom_1.s,
40096 bytes between them. Each is now a segmented PNG sheet.

Segment boundaries come from the OAM entries in rom_2.s that draw each sheet,
filtered by the palette bank the sheet is loaded with -- the tile-704 overlay
slot is shared by around 160 labels, so the bank is what says which of them
belong to a given sheet. Piece geometry comes from the same entries.

The Rayquaza clouds are irregular composites that -oam cannot describe, so they
get oam-shape jsons with spacer runs for the holes, as the painter and
ball-open sheets already do.

objpalette.py gains the bindings this needed: SLOT_PALETTES for the catch reveal
and evolution banner, whose palettes are DMA'd beside their sheets; SLOTS for
the Rayquaza sky; and overrides for the sky's unreferenced tail and for Spheal's
pause block, which is the one board leaving OBJ bank 9 blank.

Trailing blank tiles stay as .space rather than padding out the PNGs. Every
segment round-trips png -> 4bpp byte-exact, each rebuilt sheet matches the ROM
blob before and after recolouring, and make compare still passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 22:42:48 -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 0329e168d1 Colourize the remaining board, pickup and Pokedex OBJ sheets
Extends the intro_sprite work to every other OBJ sheet whose palette is
recoverable: 136 more segment PNGs across the boards, the evolution pickups
and the Pokedex, all byte-for-byte identical through the build.

A second kind of sheet
- The intro_sprite sheets are loaded whole, so their segments start at OBJ
  tile 0. Everything else is streamed frame by frame over a slot inside that
  sheet while the board runs -- the entities, the shop sign, the banners. Those
  start at the slot's tile instead, and since every frame is drawn through the
  same OAM entry they all share one bank. SLOTS records where each lands, taken
  from the destination of the DmaCopy16 that streams it.
- Segment ranges and the vote are unchanged otherwise, so a slot that coincides
  with an intro_sprite segment lands on the palette already reviewed for it.
  A slot that is only part of a sprite (the charge indicator streams four tiles
  into the middle of an eight-tile one) falls back to the entry covering it.

Tile 704 and OBJ bank 14
- These are the overlay pair. Every mode that wants a banner streams its sheet
  over one and its palette over the other, so the sprite set the vote finds
  there says nothing about the art. Those sheets take the palette loaded beside
  their own copy instead: gRubyPainterPalette and gSapphirePainterPalette for
  the travel painters, gBonusStagePal_Lit for the ball save, gBonusClearTextPal_Lit
  for the end of ball, gTravelPortraitPalette for the roulette fx.
- The evolution pickups work the same way one level down: gEvoItemPalettes runs
  parallel to gEvoItemAppear_GfxList, so each icon sheet takes its own entry.
  Nine of them share one appear-and-sparkle animation and colour it differently;
  a PNG holds one palette, so a shared segment keeps the first sheet's.

Also
- Spheal and the Pokedex join BOARDS. The Pokedex sheet is also what the
  e-reader screen puts in OBJ VRAM, so both sprite-set tables have to be
  consulted to reach all of its segments.
- Sheets built by the plain %.4bpp: %.png rule have no config entry to record
  the answer in; derive adds one carrying only the palette, which
  generate_graphics_rules.sh ignores.
- Palette paths are written relative to the config that holds them, not to the
  board -- graphics/stage/gfx.json holds a sapphire sheet.

Left greyscale: 37 PNGs. Five go to BG VRAM (the text and glyph sheets, the
high score watermark), where the bank lives in the tilemap entries the code
writes rather than in any OAM data, so they need a different derivation. The
rest are sheets with no incbin -- the light and arrow strips, several gunk_*
blobs -- whose place in VRAM is not established yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 15:04:26 -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 581f0a2944 Merge branch 'master' into custom_oam_shape 2026-03-17 01:28:14 -05:00
Marcus Huderle d4b22a7fd6 jq is a dependency right now 2026-03-04 18:14:37 -06:00
Retnuhytnuob 725e0ee12f standardize param name oamshape 2026-02-28 03:17:39 -06:00
Retnuhytnuob f62b50ef73 groudon simplification 2026-02-28 02:11:45 -06:00
Retnuhytnuob 450aa1c2a6 Gbagfx OAM chunk handling, with override support (#143)
* Add tooling support for graphics with 2^n chunking

* update to OAM handling, from 2n sizing

* Update assembly to use the hex, rather than music name, for ease of uese in decompme/m2C

* additional tools/examples

* graphics build, not currently compare matching.

* remove sprites.png

* working conversion in make file!

* stage/main folder done

* stage/misc folder done

* stage/ruby complete

* stage/sapphire updated

* base for remaining kinda-graphics; file name casing

* more name casing

* extract catch sprites

* remove special 6x4 handling from oam slicer; switched hed the 2 images to using non-oam 2x2 chunks. (net same, but allows the 6x4 to process normally when things like the whalmer are found)

* Palette info for the catch sprites, thanks to cyphgirl

* obliterate the old hatch-sprite code

* refactor segments to not have the base file name dependancy

* catch mon 1-9, horizontal layout

* rename the f param in the json for graphics

* casing cleanup?

* .

* possible fix for the out of date segment piece

* Replace graphic_cnvt_attrs.txt strategy with individually-generated makefile rules

* Don't specify tileCount

---------

Co-authored-by: Marcus Huderle <huderlem@gmail.com>
2026-02-08 14:17:19 -06:00