diff --git a/Makefile b/Makefile index 86351e37..be34b92b 100644 --- a/Makefile +++ b/Makefile @@ -86,34 +86,8 @@ endif # Clear the default suffixes .SUFFIXES: - -# Don't delete generated art and audio between builds. These are intermediates: -# nothing names them as a target, they are only pulled in as scaninc-discovered -# prerequisites of the object rules, so make would remove them after each build. -# -# This list is deliberately scoped rather than a bare `.SECONDARY:`. A bare one -# marks *every* target secondary, including $(OBJS), and make does not rebuild a -# missing secondary file when whatever depends on it still looks up to date -- -# so deleting an object and re-running `make compare` relinked a stale ELF and -# printed OK without rebuilding anything. Objects must stay non-secondary for a -# missing .o to force a rebuild. -# -# .PRECIOUS with patterns would be terser but it defeats .DELETE_ON_ERROR, which -# would leave a half-written .4bpp in place after a failed conversion and -# silently produce a wrong ROM. -GFX_PNG_SRCS := $(shell find graphics -type f -name '*.png') -GFX_PAL_SRCS := $(shell find graphics -type f -name '*.pal') -SND_WAV_SRCS := $(shell find sound -type f -name '*.wav') - -SECONDARY_ASSETS := $(GFX_PNG_SRCS:.png=.4bpp) $(GFX_PNG_SRCS:.png=.8bpp) \ - $(GFX_PNG_SRCS:.png=.1bpp) $(GFX_PNG_SRCS:.png=.gbapal) \ - $(GFX_PAL_SRCS:.pal=.gbapal) -SECONDARY_ASSETS += $(addsuffix .lz,$(SECONDARY_ASSETS)) \ - $(addsuffix .rl,$(SECONDARY_ASSETS)) \ - $(SND_WAV_SRCS:.wav=.bin) - -.SECONDARY: $(SECONDARY_ASSETS) - +# Don't delete intermediate files +.SECONDARY: # Delete files that weren't built properly .DELETE_ON_ERROR: diff --git a/data/board_data/VRAM_LAYOUT.md b/data/board_data/VRAM_LAYOUT.md deleted file mode 100644 index 519331b3..00000000 --- a/data/board_data/VRAM_LAYOUT.md +++ /dev/null @@ -1,172 +0,0 @@ -# Main-field board BG VRAM layout - -How the scrolling pinball board is assembled in VRAM. Worked out for FIELD_RUBY; -the other main fields use the same shape with their own assets. - -Without this, the board tile sheets cannot be read as pictures: the tilemaps -index a window that is streamed at runtime, so any static interpretation of -char base 2 produces a recognisable-but-corrupted board. - -## BG configuration - -From `SetupDisplayRegistersForField` (`src/all_board_pinball_game_main.c:178`), -FIELD_RUBY. Screen base N is `VRAM + N*0x800`, char base N is `VRAM + N*0x4000`. - -| BG | screen base | char base | size | priority | tilemap source | -|-----|-------------|-----------|-------|----------|-------------------------------------------| -| BG0 | 4 | 1 | 32x64 | 0 | built at runtime in `gBG0TilemapBuffer` | -| BG1 | 6 | 2 | 32x64 | 1 | `gRubyBoardBGTilemap` | -| BG2 | 2 | 1 | 32x64 | 2 | `gRubyBoardBG1Tilemap` (ramp) | -| BG3 | 0 | 2 | 32x64 | 3 | `gRubyBoardBG0Tilemap` | - -Note the labels do not match the layers: `gRubyBoardBG0Tilemap` is DMA'd to -screen base 0, which is **BG3**, and `gRubyBoardBG1Tilemap` to screen base 2, -which is **BG2**. - -## Char base 1 (BG0, BG2) - -`loadFieldBoardGraphics` decompresses `gRubyBoardCompressedTiles1` (the bgramp -sheet) and copies its first 0x4000 bytes -- 512 tiles -- over the whole -charblock. BG2's tilemap only reaches id 223, well inside that. - -## Char base 2 (BG1, BG3) -- the part that matters - -Two regions with completely different lifetimes: - - tiles 0-703 streamed ring window, rewritten as the board scrolls - tiles 704-1023 gRubyBoardBGTiles, static overlay (rails, ramps, SHOP sign) - -`loadFieldBoardGraphics` appears to place bgtop into tiles 0-703 with two -DmaCopy16 calls, but those are immediately superseded: the caller in -`src/save_and_restore_game.c:213` runs a streaming loop afterwards. - -### The ring - - for (i = 0; i < 22; i++) { - var0 = i + scroll; - var1 = (i + 10 + scroll) % 22; - src = var0 < 32 ? &gBoardGfxBuffer[var0 * 0x400] // bgtop - : &gBoardBGTileBufferAlt[(var0-32) * 0x400]; // bgbottom - DmaCopy16(3, src, (void *)0x6008000 + var1 * 0x400, 0x400); - } - -A chunk is `0x400` bytes = 32 tiles = exactly one row of the 32-tile-wide board. -The source is a virtual 64-chunk strip: bgtop supplies chunks 0-31, bgbottom -chunks 32-63. 22 of those 64 rows are resident at a time. - -Substituting `i = C - scroll` for chunk `C` gives slot `(C + 10) % 22`: **a chunk -always lands in the same slot regardless of scroll position.** That invariant is -what makes the sheets statically reconstructible. - -## Reconstructing the board - -Because chunk == tilemap row, BG3's tilemap row `R` always draws chunk `R`: - - tile pixels for (row R, column c) - = (bgtop ++ bgbottom)[ R*32 + (tilemap_id % 32) ] - -with the entry's own flip bits and palette bank applied as usual. - -Verified against `ruby_board_bg0_tilemap.bin`: 57 of 64 rows have every tile id -inside slot `(R+10) % 22` exactly as predicted. The 7 exceptions are rows 56-63, -which are filler -- every entry is 0. So the board proper is rows 0-55, i.e. -256x448 pixels. - -## Palette - -`loadFieldBoardGraphics` copies `BG_PLTT_SIZE` (0x200) from `gRubyBoardPalette`, -which is only 0x180 long, so banks 12-15 come from whatever follows it in ROM. -The tilemaps do use bank 13, so a faithful render needs the full 0x200 read. - -## Which boards this applies to - -The streaming loop is gated on `gMain.selectedField < MAIN_FIELD_COUNT`, and -`MAIN_FIELD_COUNT` is 2 (`include/constants/fields.h:6`). So **only Ruby and -Sapphire scroll**, and they share an identical BG configuration -- the model -above transfers to Sapphire unchanged. - -The six bonus boards are static: no ring, no scroll, char base 2 is simply what -`loadFieldBoardGraphics` leaves there. Dusclops is representative: - - LZ77UnCompWram(gBonusFieldCompressedBaseTiles, gBoardGfxBuffer); - DmaCopy16(3, gBoardGfxBuffer, (void *)0x06004000, 0x4000); // char base 1, shared - LZ77UnCompWram(gDusclopsBoardCompressedBGTiles, gBoardGfxBuffer); - DmaCopy16(3, gBoardGfxBuffer, (void *)0x06008000, 0x8000); // char base 2, 1024 tiles - DmaCopy16(3, gDusclopsBoardBG0Tilemap, (void *)0x06000000, 0x800); // BG3 - DmaCopy16(3, gDusclopsBoardBG1Tilemap, (void *)0x06000800, 0x800); // BG2 - DmaCopy16(3, gDusclopsBoardBG2Tilemap, (void *)0x06001000, 0x800); // BG1 - -All three of its BG layers read char base 2, and each tilemap is a plain 32x32 -screen, so those boards fold with `detilemap` directly -- no ring reconstruction. -Note char base 1 holds `gBonusFieldCompressedBaseTiles`, which is shared by every -bonus board. - -Per-field BG configuration (screen base / char base / size): - -| field | BG0 | BG1 | BG2 | BG3 | -|----------|------------|------------|------------|------------| -| RUBY | 4/1 32x64 | 6/2 32x64 | 2/1 32x64 | 0/2 32x64 | -| SAPPHIRE | 4/1 32x64 | 6/2 32x64 | 2/1 32x64 | 0/2 32x64 | -| DUSCLOPS | 4/1 32x64 | 2/2 32x32 | 1/2 32x32 | 0/2 32x32 | -| KECLEON | 4/1 32x64 | 2/2 32x32 | 1/2 32x32 | 0/2 32x32 | -| KYOGRE | 4/1 32x64 | -- | 1/2 32x32 | 0/2 32x32 | -| GROUDON | 4/1 32x64 | -- | 1/2 32x32 | 0/2 32x32 | -| RAYQUAZA | 4/1 32x64 | 6/2 32x64 | 2/2 32x64 | 0/2 32x64 | -| SPHEAL | 4/1 32x64 | 2/2 32x64 | 1/2 32x32 | 0/2 32x32 | - -## OBJ tiles and the OBJ palette - -`loadFieldBoardGraphics` ends every field with one 0x8000 DMA of the board's -`intro_sprite` sheet to 0x06010000, so OAM tile number 0 is tile 0 of that -sheet and the tile ranges quoted in the sprite-set data index it directly. -The sheets are shorter than the 1024 tiles the region holds; the tail is left -over from whatever ran before. - -Much of the region is scratch, streamed over frame by frame from a table of -interchangeable art. The art checked in at those offsets is only what the sheet -happens to ship with, and its palette arrives with the replacement tiles rather -than sitting in the board's own set: - -| tiles | written by | palette | -|-------|------------|---------| -| 0x65 | `all_board_portrait_display.c` (portrait slot 0) | into the bank `gPortraitPaletteSlots[0]` names, 13 | -| 0x7d | `all_board_portrait_display.c` (portrait slot 1) | `gLocationPalettes[n]`, into bank 12 | -| 0x95 | `main_board_pichu_entity.c`, `gMonHatchSpriteGroup5_Gfx` | mon hatch group palette | -| 0xb6 | `main_board_evolution_mode.c`, `gEvoItemTilesGfxPtrs[n]` | `gEvoItemPalettes[n]`, into bank 15 | -| 0x107 | `main_board_launcher_and_cutscenes.c`, `gSpoinkEntity_Gfx` | bank 11, see below | -| 0x115 | `main_board_to_be_split.c`, `gRubyBoardHatchCave_Gfx` | bank 11, see below | -| 0x1e8 | `UpdateRubyEvolutionShopSprite`, `gRubyBoardShop_Gfx` | `gShopPalette`, into bank 2 | -| 0x295 | `DrawOneUpBannerSprite`, `gOneUpBannerSprite_Gfx` | `gOneUpSpritePalette`, into bank 12 | - -Which entry of each table the sheet ships is recoverable by searching the ROM -for the segment's own tiles: tile 0x7d is `gLocationPortraitGfx[0]` and tile -0x95 is `wurmple_hatch` frame 0, both byte-identical, and tile 0xb6 is -`gPickupIcon1_Gfx`. Tile 0x65 is a Geodude portrait close to but not identical -to the extracted one. - -OBJ bank 11 is never its resting ROM value on the main fields: `UpdateSpoinkAnimation` -runs every frame and rewrites it from `gFieldPaletteVariants[field][set * 2 + (camera is low)]`, -six banks per field -- three brightness sets crossed with which half of the -board is on screen. Set 0 is what plays, so the upper half of the board is -variant 0 (ruby) / 6 (sapphire) and the lower half 1 / 7. Spoink is the plunger -at the bottom; the egg, hatch cave and hatch machine sit at the top. Ruby's -resting value for the bank happens to equal its own upper-half variant. - -The OBJ palette itself is one 512-byte DMA: - - DmaCopy16(3, gBoardConfig.fieldLayout.objPaletteSets[0], (void *)OBJ_PLTT, OBJ_PLTT_SIZE); - -`objPaletteSets[1]` and `[2]` are the same palette at two dimmer levels, used -by the banner fades in `all_board_banners.c`; set 0 is the one on screen during -play. Every board's set 0 is a full 512 bytes except ruby's, which is 0x120 -- -the DMA runs on past it into `gBonusStageObjPal`, so ruby's OBJ banks 0-8 come -from `ruby_board_palset_0.gbapal` and banks 9-15 from -`graphics/stage/main/bonus_stage_obj.gbapal`. The bonus boards copy that same -label's first bank to 0x05000320, which is OBJ bank 9 there too. - -Sapphire leaves banks 12-15 at zero and loads them when the sprites that use -them appear; ruby, by the overlap above, keeps a usable copy in ROM. - -`tools/scripts/objpalette.py` derives which bank each segment of a sheet is -drawn with from the OAM data in `data/rom_2.s`, records it in the gfx config as -`"palette"`/`"palbank"`, and recolours the segment PNGs to match. diff --git a/graphics/intro/scene1torchic/bg1_dividers_screen.png b/graphics/intro/scene1torchic/bg1_dividers_screen.png deleted file mode 100644 index 3d8ddda9..00000000 Binary files a/graphics/intro/scene1torchic/bg1_dividers_screen.png and /dev/null differ diff --git a/graphics/intro/scene1torchic/bg2_dividers_screen.png b/graphics/intro/scene1torchic/bg2_dividers_screen.png deleted file mode 100644 index 517f5efd..00000000 Binary files a/graphics/intro/scene1torchic/bg2_dividers_screen.png and /dev/null differ diff --git a/graphics/intro/scene1torchic/bg3_text_screen.png b/graphics/intro/scene1torchic/bg3_text_screen.png deleted file mode 100644 index 7219cd97..00000000 Binary files a/graphics/intro/scene1torchic/bg3_text_screen.png and /dev/null differ diff --git a/graphics/intro/scene1torchic/tiles.png b/graphics/intro/scene1torchic/tiles.png new file mode 100644 index 00000000..ad0574e0 Binary files /dev/null and b/graphics/intro/scene1torchic/tiles.png differ diff --git a/graphics/intro/scene2pikas/bg0_pichu_screen.png b/graphics/intro/scene2pikas/bg0_pichu_screen.png deleted file mode 100644 index 405a77ae..00000000 Binary files a/graphics/intro/scene2pikas/bg0_pichu_screen.png and /dev/null differ diff --git a/graphics/intro/scene2pikas/bg1_pikachu_screen.png b/graphics/intro/scene2pikas/bg1_pikachu_screen.png deleted file mode 100644 index c0911b04..00000000 Binary files a/graphics/intro/scene2pikas/bg1_pikachu_screen.png and /dev/null differ diff --git a/graphics/intro/scene2pikas/bg3_text_screen.png b/graphics/intro/scene2pikas/bg3_text_screen.png deleted file mode 100644 index 14f8a9e3..00000000 Binary files a/graphics/intro/scene2pikas/bg3_text_screen.png and /dev/null differ diff --git a/graphics/intro/scene2pikas/tiles.png b/graphics/intro/scene2pikas/tiles.png new file mode 100644 index 00000000..71876887 Binary files /dev/null and b/graphics/intro/scene2pikas/tiles.png differ diff --git a/graphics/intro/scene3treecko/bg0_treecko_screen.png b/graphics/intro/scene3treecko/bg0_treecko_screen.png deleted file mode 100644 index 26685d7e..00000000 Binary files a/graphics/intro/scene3treecko/bg0_treecko_screen.png and /dev/null differ diff --git a/graphics/intro/scene3treecko/bg1_ball_screen.png b/graphics/intro/scene3treecko/bg1_ball_screen.png deleted file mode 100644 index 2976c2b3..00000000 Binary files a/graphics/intro/scene3treecko/bg1_ball_screen.png and /dev/null differ diff --git a/graphics/intro/scene3treecko/bg1_ballandflipper_screen.png b/graphics/intro/scene3treecko/bg1_ballandflipper_screen.png deleted file mode 100644 index 5a46fbd4..00000000 Binary files a/graphics/intro/scene3treecko/bg1_ballandflipper_screen.png and /dev/null differ diff --git a/graphics/intro/scene3treecko/bg1_ballflipperhit_screen.png b/graphics/intro/scene3treecko/bg1_ballflipperhit_screen.png deleted file mode 100644 index f068d024..00000000 Binary files a/graphics/intro/scene3treecko/bg1_ballflipperhit_screen.png and /dev/null differ diff --git a/graphics/intro/scene3treecko/bg2_screen.png b/graphics/intro/scene3treecko/bg2_screen.png deleted file mode 100644 index 252b69a6..00000000 Binary files a/graphics/intro/scene3treecko/bg2_screen.png and /dev/null differ diff --git a/graphics/intro/scene3treecko/bg3_text_screen.png b/graphics/intro/scene3treecko/bg3_text_screen.png deleted file mode 100644 index dfbb84f4..00000000 Binary files a/graphics/intro/scene3treecko/bg3_text_screen.png and /dev/null differ diff --git a/graphics/intro/scene3treecko/tiles.png b/graphics/intro/scene3treecko/tiles.png new file mode 100644 index 00000000..2e9b4b80 Binary files /dev/null and b/graphics/intro/scene3treecko/tiles.png differ diff --git a/graphics/intro/scene3treecko/tiles_unmapped.png b/graphics/intro/scene3treecko/tiles_unmapped.png deleted file mode 100644 index ccc4b926..00000000 Binary files a/graphics/intro/scene3treecko/tiles_unmapped.png and /dev/null differ diff --git a/graphics/intro/scene4plussleminun/bg0_minun_screen.png b/graphics/intro/scene4plussleminun/bg0_minun_screen.png deleted file mode 100644 index b3aa04f7..00000000 Binary files a/graphics/intro/scene4plussleminun/bg0_minun_screen.png and /dev/null differ diff --git a/graphics/intro/scene4plussleminun/bg1_plussle_screen.png b/graphics/intro/scene4plussleminun/bg1_plussle_screen.png deleted file mode 100644 index d1f80a3d..00000000 Binary files a/graphics/intro/scene4plussleminun/bg1_plussle_screen.png and /dev/null differ diff --git a/graphics/intro/scene4plussleminun/bg3_text_screen.png b/graphics/intro/scene4plussleminun/bg3_text_screen.png deleted file mode 100644 index 3ed09012..00000000 Binary files a/graphics/intro/scene4plussleminun/bg3_text_screen.png and /dev/null differ diff --git a/graphics/intro/scene4plussleminun/intro_gfx.json b/graphics/intro/scene4plussleminun/intro_gfx.json new file mode 100644 index 00000000..3b7cd985 --- /dev/null +++ b/graphics/intro/scene4plussleminun/intro_gfx.json @@ -0,0 +1,30 @@ +{ + "kind": "gfx-config", + "version": 1, + "defaults": { + "mwidth": 0, + "mheight": 0, + "oam": false, + "width": 16, + "palette": "" + }, + "files": [ + { + "gfx_filename": "tiles", + "segments":[ + { + "segfile": "text_tiles", + "width":20 + }, + { + "segfile": "space_tiles" + }, + { + "segfile": "mon_tiles", + "width":24 + } + ], + "align":1 + } + ] +} \ No newline at end of file diff --git a/graphics/intro/scene4plussleminun/mon_tiles.png b/graphics/intro/scene4plussleminun/mon_tiles.png new file mode 100644 index 00000000..08457729 Binary files /dev/null and b/graphics/intro/scene4plussleminun/mon_tiles.png differ diff --git a/graphics/intro/scene4plussleminun/space_tiles.png b/graphics/intro/scene4plussleminun/space_tiles.png new file mode 100644 index 00000000..4cef91ff Binary files /dev/null and b/graphics/intro/scene4plussleminun/space_tiles.png differ diff --git a/graphics/intro/scene4plussleminun/text_tiles.png b/graphics/intro/scene4plussleminun/text_tiles.png new file mode 100644 index 00000000..0a8ff71e Binary files /dev/null and b/graphics/intro/scene4plussleminun/text_tiles.png differ diff --git a/graphics/intro/scene5mudkip/bg0_screen.png b/graphics/intro/scene5mudkip/bg0_screen.png deleted file mode 100644 index fc80e3da..00000000 Binary files a/graphics/intro/scene5mudkip/bg0_screen.png and /dev/null differ diff --git a/graphics/intro/scene5mudkip/bg1_1sections_screen.png b/graphics/intro/scene5mudkip/bg1_1sections_screen.png deleted file mode 100644 index 68bc40dc..00000000 Binary files a/graphics/intro/scene5mudkip/bg1_1sections_screen.png and /dev/null differ diff --git a/graphics/intro/scene5mudkip/bg2_2sections_screen.png b/graphics/intro/scene5mudkip/bg2_2sections_screen.png deleted file mode 100644 index 638b4eba..00000000 Binary files a/graphics/intro/scene5mudkip/bg2_2sections_screen.png and /dev/null differ diff --git a/graphics/intro/scene5mudkip/bg3_text_screen.png b/graphics/intro/scene5mudkip/bg3_text_screen.png deleted file mode 100644 index 6b2b5d06..00000000 Binary files a/graphics/intro/scene5mudkip/bg3_text_screen.png and /dev/null differ diff --git a/graphics/intro/scene5mudkip/text_tiles.png b/graphics/intro/scene5mudkip/text_tiles.png new file mode 100644 index 00000000..454239b6 Binary files /dev/null and b/graphics/intro/scene5mudkip/text_tiles.png differ diff --git a/graphics/intro/scene6chinchou/bg0_chinchou_screen.png b/graphics/intro/scene6chinchou/bg0_chinchou_screen.png deleted file mode 100644 index be9869bf..00000000 Binary files a/graphics/intro/scene6chinchou/bg0_chinchou_screen.png and /dev/null differ diff --git a/graphics/intro/scene6chinchou/bg1_ball_screen.png b/graphics/intro/scene6chinchou/bg1_ball_screen.png deleted file mode 100644 index 7ff7cd5f..00000000 Binary files a/graphics/intro/scene6chinchou/bg1_ball_screen.png and /dev/null differ diff --git a/graphics/intro/scene6chinchou/bg2_chinchou_screen.png b/graphics/intro/scene6chinchou/bg2_chinchou_screen.png deleted file mode 100644 index e610ab4c..00000000 Binary files a/graphics/intro/scene6chinchou/bg2_chinchou_screen.png and /dev/null differ diff --git a/graphics/intro/scene6chinchou/bg3_burst_screen.png b/graphics/intro/scene6chinchou/bg3_burst_screen.png deleted file mode 100644 index ea32b2d1..00000000 Binary files a/graphics/intro/scene6chinchou/bg3_burst_screen.png and /dev/null differ diff --git a/graphics/intro/scene6chinchou/tiles.png b/graphics/intro/scene6chinchou/tiles.png new file mode 100644 index 00000000..9682b1b5 Binary files /dev/null and b/graphics/intro/scene6chinchou/tiles.png differ diff --git a/graphics/intro/scene6chinchou/tiles_unmapped.png b/graphics/intro/scene6chinchou/tiles_unmapped.png deleted file mode 100644 index 1cd628e4..00000000 Binary files a/graphics/intro/scene6chinchou/tiles_unmapped.png and /dev/null differ diff --git a/graphics/intro/scene7parade/bg0_pelipper_screen.png b/graphics/intro/scene7parade/bg0_pelipper_screen.png deleted file mode 100644 index 6672acd1..00000000 Binary files a/graphics/intro/scene7parade/bg0_pelipper_screen.png and /dev/null differ diff --git a/graphics/intro/scene7parade/bg1_spoink_screen.png b/graphics/intro/scene7parade/bg1_spoink_screen.png deleted file mode 100644 index fba1b9b3..00000000 Binary files a/graphics/intro/scene7parade/bg1_spoink_screen.png and /dev/null differ diff --git a/graphics/intro/scene7parade/bg2_makuhita_screen.png b/graphics/intro/scene7parade/bg2_makuhita_screen.png deleted file mode 100644 index 90192fd1..00000000 Binary files a/graphics/intro/scene7parade/bg2_makuhita_screen.png and /dev/null differ diff --git a/graphics/intro/scene7parade/bg3_text_screen.png b/graphics/intro/scene7parade/bg3_text_screen.png deleted file mode 100644 index 86852a98..00000000 Binary files a/graphics/intro/scene7parade/bg3_text_screen.png and /dev/null differ diff --git a/graphics/intro/scene7parade/tiles.png b/graphics/intro/scene7parade/tiles.png new file mode 100644 index 00000000..5dc1c265 Binary files /dev/null and b/graphics/intro/scene7parade/tiles.png differ diff --git a/graphics/intro/scene7parade/tiles_unmapped.png b/graphics/intro/scene7parade/tiles_unmapped.png deleted file mode 100644 index dced7e90..00000000 Binary files a/graphics/intro/scene7parade/tiles_unmapped.png and /dev/null differ diff --git a/graphics/intro/scene8wailmerlaunch/bg2_explosion_screen.png b/graphics/intro/scene8wailmerlaunch/bg2_explosion_screen.png deleted file mode 100644 index f950646a..00000000 Binary files a/graphics/intro/scene8wailmerlaunch/bg2_explosion_screen.png and /dev/null differ diff --git a/graphics/intro/scene8wailmerlaunch/explosion_tiles.png b/graphics/intro/scene8wailmerlaunch/explosion_tiles.png new file mode 100644 index 00000000..cd59dcb8 Binary files /dev/null and b/graphics/intro/scene8wailmerlaunch/explosion_tiles.png differ diff --git a/graphics/intro/scene8wailmerlaunch/tiles_unmapped.png b/graphics/intro/scene8wailmerlaunch/tiles_unmapped.png deleted file mode 100644 index 87b2aab3..00000000 Binary files a/graphics/intro/scene8wailmerlaunch/tiles_unmapped.png and /dev/null differ diff --git a/graphics/intro/scene9ballflight/bg3_clouds_screen.png b/graphics/intro/scene9ballflight/bg3_clouds_screen.png deleted file mode 100644 index c40c7514..00000000 Binary files a/graphics/intro/scene9ballflight/bg3_clouds_screen.png and /dev/null differ diff --git a/graphics/intro/scene9ballflight/cloud_tiles.png b/graphics/intro/scene9ballflight/cloud_tiles.png new file mode 100644 index 00000000..92021010 Binary files /dev/null and b/graphics/intro/scene9ballflight/cloud_tiles.png differ diff --git a/graphics/stage/dusclops/dusclops_board_bg0_screen.png b/graphics/stage/dusclops/dusclops_board_bg0_screen.png deleted file mode 100644 index b74ee9b6..00000000 Binary files a/graphics/stage/dusclops/dusclops_board_bg0_screen.png and /dev/null differ diff --git a/graphics/stage/dusclops/dusclops_board_bg1_screen.png b/graphics/stage/dusclops/dusclops_board_bg1_screen.png deleted file mode 100644 index 2b7129f1..00000000 Binary files a/graphics/stage/dusclops/dusclops_board_bg1_screen.png and /dev/null differ diff --git a/graphics/stage/dusclops/dusclops_board_bg2_screen.png b/graphics/stage/dusclops/dusclops_board_bg2_screen.png deleted file mode 100644 index 23134f69..00000000 Binary files a/graphics/stage/dusclops/dusclops_board_bg2_screen.png and /dev/null differ diff --git a/graphics/stage/dusclops/dusclops_board_bg_tiles.png b/graphics/stage/dusclops/dusclops_board_bg_tiles.png new file mode 100644 index 00000000..463ebd56 Binary files /dev/null and b/graphics/stage/dusclops/dusclops_board_bg_tiles.png differ diff --git a/graphics/stage/groudon/groudon_board_bg0_screen.png b/graphics/stage/groudon/groudon_board_bg0_screen.png deleted file mode 100644 index a360cf2a..00000000 Binary files a/graphics/stage/groudon/groudon_board_bg0_screen.png and /dev/null differ diff --git a/graphics/stage/groudon/groudon_board_bg1_screen.png b/graphics/stage/groudon/groudon_board_bg1_screen.png deleted file mode 100644 index 19af9b75..00000000 Binary files a/graphics/stage/groudon/groudon_board_bg1_screen.png and /dev/null differ diff --git a/graphics/stage/groudon/groudon_board_bg_tiles.png b/graphics/stage/groudon/groudon_board_bg_tiles.png new file mode 100644 index 00000000..27976cb8 Binary files /dev/null and b/graphics/stage/groudon/groudon_board_bg_tiles.png differ diff --git a/graphics/stage/kecleon/kecleon_board_bg0_screen.png b/graphics/stage/kecleon/kecleon_board_bg0_screen.png deleted file mode 100644 index a24467d0..00000000 Binary files a/graphics/stage/kecleon/kecleon_board_bg0_screen.png and /dev/null differ diff --git a/graphics/stage/kecleon/kecleon_board_bg1_screen.png b/graphics/stage/kecleon/kecleon_board_bg1_screen.png deleted file mode 100644 index 0f97e71a..00000000 Binary files a/graphics/stage/kecleon/kecleon_board_bg1_screen.png and /dev/null differ diff --git a/graphics/stage/kecleon/kecleon_board_bg_tiles.png b/graphics/stage/kecleon/kecleon_board_bg_tiles.png new file mode 100644 index 00000000..0e1ed9ea Binary files /dev/null and b/graphics/stage/kecleon/kecleon_board_bg_tiles.png differ diff --git a/graphics/stage/kecleon/kecleon_overlay_0_screen.png b/graphics/stage/kecleon/kecleon_overlay_0_screen.png deleted file mode 100644 index f941ec6f..00000000 Binary files a/graphics/stage/kecleon/kecleon_overlay_0_screen.png and /dev/null differ diff --git a/graphics/stage/kecleon/kecleon_overlay_1_screen.png b/graphics/stage/kecleon/kecleon_overlay_1_screen.png deleted file mode 100644 index beb2261d..00000000 Binary files a/graphics/stage/kecleon/kecleon_overlay_1_screen.png and /dev/null differ diff --git a/graphics/stage/kecleon/kecleon_overlay_2_screen.png b/graphics/stage/kecleon/kecleon_overlay_2_screen.png deleted file mode 100644 index b5ec5b09..00000000 Binary files a/graphics/stage/kecleon/kecleon_overlay_2_screen.png and /dev/null differ diff --git a/graphics/stage/kecleon/kecleon_overlay_3_screen.png b/graphics/stage/kecleon/kecleon_overlay_3_screen.png deleted file mode 100644 index d48345c3..00000000 Binary files a/graphics/stage/kecleon/kecleon_overlay_3_screen.png and /dev/null differ diff --git a/graphics/stage/kyogre/kyogre_board_bg0_screen.png b/graphics/stage/kyogre/kyogre_board_bg0_screen.png deleted file mode 100644 index fa114dea..00000000 Binary files a/graphics/stage/kyogre/kyogre_board_bg0_screen.png and /dev/null differ diff --git a/graphics/stage/kyogre/kyogre_board_bg1_screen.png b/graphics/stage/kyogre/kyogre_board_bg1_screen.png deleted file mode 100644 index aa55021a..00000000 Binary files a/graphics/stage/kyogre/kyogre_board_bg1_screen.png and /dev/null differ diff --git a/graphics/stage/kyogre/kyogre_board_bg_tiles.png b/graphics/stage/kyogre/kyogre_board_bg_tiles.png new file mode 100644 index 00000000..93938ed4 Binary files /dev/null and b/graphics/stage/kyogre/kyogre_board_bg_tiles.png differ diff --git a/graphics/stage/kyogre/kyogre_board_unmapped.png b/graphics/stage/kyogre/kyogre_board_unmapped.png deleted file mode 100644 index fdc34d07..00000000 Binary files a/graphics/stage/kyogre/kyogre_board_unmapped.png and /dev/null differ diff --git a/graphics/stage/rayquaza/rayquaza_board_bg0_screen.png b/graphics/stage/rayquaza/rayquaza_board_bg0_screen.png deleted file mode 100644 index 87e7fd29..00000000 Binary files a/graphics/stage/rayquaza/rayquaza_board_bg0_screen.png and /dev/null differ diff --git a/graphics/stage/rayquaza/rayquaza_board_bg1_screen.png b/graphics/stage/rayquaza/rayquaza_board_bg1_screen.png deleted file mode 100644 index d42fde08..00000000 Binary files a/graphics/stage/rayquaza/rayquaza_board_bg1_screen.png and /dev/null differ diff --git a/graphics/stage/rayquaza/rayquaza_board_bg_tiles.png b/graphics/stage/rayquaza/rayquaza_board_bg_tiles.png new file mode 100644 index 00000000..550bc028 Binary files /dev/null and b/graphics/stage/rayquaza/rayquaza_board_bg_tiles.png differ diff --git a/graphics/stage/rayquaza/rayquaza_board_scroll_screen.png b/graphics/stage/rayquaza/rayquaza_board_scroll_screen.png deleted file mode 100644 index 54cb0fa7..00000000 Binary files a/graphics/stage/rayquaza/rayquaza_board_scroll_screen.png and /dev/null differ diff --git a/graphics/stage/rayquaza/rayquaza_board_unmapped_a.png b/graphics/stage/rayquaza/rayquaza_board_unmapped_a.png deleted file mode 100644 index 62d27634..00000000 Binary files a/graphics/stage/rayquaza/rayquaza_board_unmapped_a.png and /dev/null differ diff --git a/graphics/stage/rayquaza/rayquaza_board_unmapped_b.png b/graphics/stage/rayquaza/rayquaza_board_unmapped_b.png deleted file mode 100644 index 0f060d82..00000000 Binary files a/graphics/stage/rayquaza/rayquaza_board_unmapped_b.png and /dev/null differ diff --git a/graphics/stage/ruby/ruby_board_bg_screen.png b/graphics/stage/ruby/ruby_board_bg_screen.png deleted file mode 100644 index 17e1fba3..00000000 Binary files a/graphics/stage/ruby/ruby_board_bg_screen.png and /dev/null differ diff --git a/graphics/stage/ruby/ruby_board_bg_tiles.png b/graphics/stage/ruby/ruby_board_bg_tiles.png new file mode 100644 index 00000000..3e2d02cf Binary files /dev/null and b/graphics/stage/ruby/ruby_board_bg_tiles.png differ diff --git a/graphics/stage/ruby/ruby_board_bg_unmapped.png b/graphics/stage/ruby/ruby_board_bg_unmapped.png deleted file mode 100644 index d45013ed..00000000 Binary files a/graphics/stage/ruby/ruby_board_bg_unmapped.png and /dev/null differ diff --git a/graphics/stage/ruby/ruby_board_bgbottom_tiles.png b/graphics/stage/ruby/ruby_board_bgbottom_tiles.png new file mode 100644 index 00000000..0b6980ce Binary files /dev/null and b/graphics/stage/ruby/ruby_board_bgbottom_tiles.png differ diff --git a/graphics/stage/ruby/ruby_board_bgramp_tiles.png b/graphics/stage/ruby/ruby_board_bgramp_tiles.png new file mode 100644 index 00000000..16d7253c Binary files /dev/null and b/graphics/stage/ruby/ruby_board_bgramp_tiles.png differ diff --git a/graphics/stage/ruby/ruby_board_bgtop_tiles.png b/graphics/stage/ruby/ruby_board_bgtop_tiles.png new file mode 100644 index 00000000..e6c36a36 Binary files /dev/null and b/graphics/stage/ruby/ruby_board_bgtop_tiles.png differ diff --git a/graphics/stage/ruby/ruby_board_ramp_screen.png b/graphics/stage/ruby/ruby_board_ramp_screen.png deleted file mode 100644 index 4ebf09c7..00000000 Binary files a/graphics/stage/ruby/ruby_board_ramp_screen.png and /dev/null differ diff --git a/graphics/stage/ruby/ruby_board_ramp_unmapped_a.png b/graphics/stage/ruby/ruby_board_ramp_unmapped_a.png deleted file mode 100644 index 0f348edc..00000000 Binary files a/graphics/stage/ruby/ruby_board_ramp_unmapped_a.png and /dev/null differ diff --git a/graphics/stage/ruby/ruby_board_ramp_unmapped_b.png b/graphics/stage/ruby/ruby_board_ramp_unmapped_b.png deleted file mode 100644 index 2656b341..00000000 Binary files a/graphics/stage/ruby/ruby_board_ramp_unmapped_b.png and /dev/null differ diff --git a/graphics/stage/ruby/ruby_board_screen.png b/graphics/stage/ruby/ruby_board_screen.png deleted file mode 100644 index 83166b9b..00000000 Binary files a/graphics/stage/ruby/ruby_board_screen.png and /dev/null differ diff --git a/graphics/stage/sapphire/sapphire_board_bg_screen.png b/graphics/stage/sapphire/sapphire_board_bg_screen.png deleted file mode 100644 index 5ff02c07..00000000 Binary files a/graphics/stage/sapphire/sapphire_board_bg_screen.png and /dev/null differ diff --git a/graphics/stage/sapphire/sapphire_board_bg_tiles.png b/graphics/stage/sapphire/sapphire_board_bg_tiles.png new file mode 100644 index 00000000..56a316db Binary files /dev/null and b/graphics/stage/sapphire/sapphire_board_bg_tiles.png differ diff --git a/graphics/stage/sapphire/sapphire_board_bg_unmapped_a.png b/graphics/stage/sapphire/sapphire_board_bg_unmapped_a.png deleted file mode 100644 index 4f77682b..00000000 Binary files a/graphics/stage/sapphire/sapphire_board_bg_unmapped_a.png and /dev/null differ diff --git a/graphics/stage/sapphire/sapphire_board_bg_unmapped_b.png b/graphics/stage/sapphire/sapphire_board_bg_unmapped_b.png deleted file mode 100644 index 6f758c7e..00000000 Binary files a/graphics/stage/sapphire/sapphire_board_bg_unmapped_b.png and /dev/null differ diff --git a/graphics/stage/sapphire/sapphire_board_bgbottom_tiles.png b/graphics/stage/sapphire/sapphire_board_bgbottom_tiles.png new file mode 100644 index 00000000..0e50f8ba Binary files /dev/null and b/graphics/stage/sapphire/sapphire_board_bgbottom_tiles.png differ diff --git a/graphics/stage/sapphire/sapphire_board_bgramp_tiles.png b/graphics/stage/sapphire/sapphire_board_bgramp_tiles.png new file mode 100644 index 00000000..04b9346b Binary files /dev/null and b/graphics/stage/sapphire/sapphire_board_bgramp_tiles.png differ diff --git a/graphics/stage/sapphire/sapphire_board_bgtop_tiles.png b/graphics/stage/sapphire/sapphire_board_bgtop_tiles.png new file mode 100644 index 00000000..ba064887 Binary files /dev/null and b/graphics/stage/sapphire/sapphire_board_bgtop_tiles.png differ diff --git a/graphics/stage/sapphire/sapphire_board_ramp_screen.png b/graphics/stage/sapphire/sapphire_board_ramp_screen.png deleted file mode 100644 index 074d2206..00000000 Binary files a/graphics/stage/sapphire/sapphire_board_ramp_screen.png and /dev/null differ diff --git a/graphics/stage/sapphire/sapphire_board_ramp_unmapped_a.png b/graphics/stage/sapphire/sapphire_board_ramp_unmapped_a.png deleted file mode 100644 index 9989e509..00000000 Binary files a/graphics/stage/sapphire/sapphire_board_ramp_unmapped_a.png and /dev/null differ diff --git a/graphics/stage/sapphire/sapphire_board_ramp_unmapped_b.png b/graphics/stage/sapphire/sapphire_board_ramp_unmapped_b.png deleted file mode 100644 index e40771b4..00000000 Binary files a/graphics/stage/sapphire/sapphire_board_ramp_unmapped_b.png and /dev/null differ diff --git a/graphics/stage/sapphire/sapphire_board_screen.png b/graphics/stage/sapphire/sapphire_board_screen.png deleted file mode 100644 index c87168d1..00000000 Binary files a/graphics/stage/sapphire/sapphire_board_screen.png and /dev/null differ diff --git a/graphics/stage/spheal/spheal_board_bg_screen.png b/graphics/stage/spheal/spheal_board_bg_screen.png deleted file mode 100644 index 30b0babc..00000000 Binary files a/graphics/stage/spheal/spheal_board_bg_screen.png and /dev/null differ diff --git a/graphics/stage/spheal/spheal_board_bg_tiles.png b/graphics/stage/spheal/spheal_board_bg_tiles.png new file mode 100644 index 00000000..4ff6ceb9 Binary files /dev/null and b/graphics/stage/spheal/spheal_board_bg_tiles.png differ diff --git a/graphics/stage/spheal/spheal_board_bgwater_screen.png b/graphics/stage/spheal/spheal_board_bgwater_screen.png deleted file mode 100644 index 5fd76908..00000000 Binary files a/graphics/stage/spheal/spheal_board_bgwater_screen.png and /dev/null differ diff --git a/graphics/stage/spheal/spheal_board_unmapped.png b/graphics/stage/spheal/spheal_board_unmapped.png deleted file mode 100644 index df5cf92e..00000000 Binary files a/graphics/stage/spheal/spheal_board_unmapped.png and /dev/null differ diff --git a/graphics_rules.mk b/graphics_rules.mk index b69adac6..35b40123 100644 --- a/graphics_rules.mk +++ b/graphics_rules.mk @@ -15,286 +15,3 @@ graphics/options/sprites.4bpp: graphics/options/sprites.png graphics/pokedex/text_glyphs.4bpp: graphics/pokedex/text_glyphs.png $(GFX) $< $@ -num_tiles 513 - -# --- BG sheets rebuilt from assembled screens ------------------------------- -# These tile sheets are deduplicated, so the raw sheet is not editable as an -# image at any width. The checked-in source is the assembled screen instead -# (8bpp, pixel = palette bank * 16 + colour index); detilemap folds the screens -# back down through their tilemaps. See tools/scripts/detilemap.py. - -PYTHON ?= python3 -DETILEMAP := $(PYTHON) tools/scripts/detilemap.py - -S1TORCHIC := graphics/intro/scene1torchic -# bg0_tilemap.bin is not listed: its three screens reference only blank tiles of -# this sheet (its artwork lives in the ball buffer, tiles 704-890), so it adds -# no pixels here. Tiles 320-703 are unreferenced and come out as zeroes. -$(S1TORCHIC)/tiles.4bpp: $(S1TORCHIC)/bg3_text_screen.png $(S1TORCHIC)/bg3_text_tilemap.bin \ - $(S1TORCHIC)/bg1_dividers_screen.png $(S1TORCHIC)/bg1_dividers_tilemap.bin \ - $(S1TORCHIC)/bg2_dividers_screen.png $(S1TORCHIC)/bg2_dividers_tilemap.bin \ - tools/scripts/detilemap.py - $(DETILEMAP) extract -n 704 -o $@ \ - --from $(S1TORCHIC)/bg3_text_screen.png:$(S1TORCHIC)/bg3_text_tilemap.bin:32x32 \ - --from $(S1TORCHIC)/bg1_dividers_screen.png:$(S1TORCHIC)/bg1_dividers_tilemap.bin:32x64 \ - --from $(S1TORCHIC)/bg2_dividers_screen.png:$(S1TORCHIC)/bg2_dividers_tilemap.bin:32x64 - -# Bonus boards are static: no scroll ring, and all three BG layers read char -# base 2, so the sheet folds straight out of its three 32x32 screens. -# See data/board_data/VRAM_LAYOUT.md. -DUSCLOPS := graphics/stage/dusclops -$(DUSCLOPS)/dusclops_board_bg_tiles.4bpp: \ - $(foreach s,bg0 bg1 bg2,$(DUSCLOPS)/dusclops_board_$(s)_screen.png $(DUSCLOPS)/dusclops_board_$(s)_tilemap.bin) \ - tools/scripts/detilemap.py - $(DETILEMAP) extract -n 1024 -o $@ \ - --from $(DUSCLOPS)/dusclops_board_bg0_screen.png:$(DUSCLOPS)/dusclops_board_bg0_tilemap.bin:32x32 \ - --from $(DUSCLOPS)/dusclops_board_bg1_screen.png:$(DUSCLOPS)/dusclops_board_bg1_tilemap.bin:32x32 \ - --from $(DUSCLOPS)/dusclops_board_bg2_screen.png:$(DUSCLOPS)/dusclops_board_bg2_tilemap.bin:32x32 - -KYOGRE := graphics/stage/kyogre -# 32 non-blank tiles (water/bubble art) sit unreferenced in an 8x4 block at rows -# 8-11, cols 4-11 of the 32-wide grid. One 32x4 strip over tiles 256-383 covers -# them; the referenced tiles it also spans agree with the screens. -$(KYOGRE)/kyogre_board_bg_tiles.4bpp: \ - $(foreach s,bg0 bg1,$(KYOGRE)/kyogre_board_$(s)_screen.png $(KYOGRE)/kyogre_board_$(s)_tilemap.bin) \ - $(KYOGRE)/kyogre_board_unmapped.png tools/scripts/detilemap.py - $(DETILEMAP) extract -n 1024 -o $@ \ - --from $(KYOGRE)/kyogre_board_bg0_screen.png:$(KYOGRE)/kyogre_board_bg0_tilemap.bin:32x32 \ - --from $(KYOGRE)/kyogre_board_bg1_screen.png:$(KYOGRE)/kyogre_board_bg1_tilemap.bin:32x32 \ - --extra $(KYOGRE)/kyogre_board_unmapped.png@256 - -KECLEON := graphics/stage/kecleon -# The four kecleon_overlay_* maps are not screens: kecleon_process3.c mines rows -# out of gKecleonBerryOverlayTilemap into gBG0TilemapBuffer as fragments, so they -# render as strips rather than pictures. They are still the only source for tiles -# 640-1023, and all four contribute distinct ones, so all four are needed. -KECLEON_MAPS := kecleon_board_bg0 kecleon_board_bg1 \ - kecleon_overlay_0 kecleon_overlay_1 kecleon_overlay_2 kecleon_overlay_3 -$(KECLEON)/kecleon_board_bg_tiles.4bpp: \ - $(foreach s,$(KECLEON_MAPS),$(KECLEON)/$(s)_screen.png $(KECLEON)/$(s)_tilemap.bin) \ - tools/scripts/detilemap.py - $(DETILEMAP) extract -n 1024 -o $@ \ - $(foreach s,$(KECLEON_MAPS),--from $(KECLEON)/$(s)_screen.png:$(KECLEON)/$(s)_tilemap.bin:32x32) - -SPHEAL := graphics/stage/spheal -# Only two of its layers have a static tilemap; BG1's is built at runtime. -# 32 non-blank tiles sit unreferenced in an 8x4 block at rows 17-20, cols 4-11, -# so one 32x4 strip over tiles 544-671 carries them. -$(SPHEAL)/spheal_board_bg_tiles.4bpp: \ - $(SPHEAL)/spheal_board_bgwater_screen.png $(SPHEAL)/spheal_board_bgwater_tilemap.bin \ - $(SPHEAL)/spheal_board_bg_screen.png $(SPHEAL)/spheal_board_bg_tilemap.bin \ - $(SPHEAL)/spheal_board_unmapped.png tools/scripts/detilemap.py - $(DETILEMAP) extract -n 1024 -o $@ \ - --from $(SPHEAL)/spheal_board_bgwater_screen.png:$(SPHEAL)/spheal_board_bgwater_tilemap.bin:32x32 \ - --from $(SPHEAL)/spheal_board_bg_screen.png:$(SPHEAL)/spheal_board_bg_tilemap.bin:32x32 \ - --extra $(SPHEAL)/spheal_board_unmapped.png@544 - -RAYQUAZA := graphics/stage/rayquaza -# Three 32x64 layers, all on char base 2. Tiles 301 and 335 are non-blank but -# referenced by no tilemap. -RAYQUAZA_MAPS := bg0 bg1 scroll -$(RAYQUAZA)/rayquaza_board_bg_tiles.4bpp: \ - $(foreach s,$(RAYQUAZA_MAPS),$(RAYQUAZA)/rayquaza_board_$(s)_screen.png $(RAYQUAZA)/rayquaza_board_$(s)_tilemap.bin) \ - $(RAYQUAZA)/rayquaza_board_unmapped_a.png $(RAYQUAZA)/rayquaza_board_unmapped_b.png \ - tools/scripts/detilemap.py - $(DETILEMAP) extract -n 1024 -o $@ \ - $(foreach s,$(RAYQUAZA_MAPS),--from $(RAYQUAZA)/rayquaza_board_$(s)_screen.png:$(RAYQUAZA)/rayquaza_board_$(s)_tilemap.bin:32x64) \ - --extra $(RAYQUAZA)/rayquaza_board_unmapped_a.png@301 \ - --extra $(RAYQUAZA)/rayquaza_board_unmapped_b.png@335 - -GROUDON := graphics/stage/groudon -$(GROUDON)/groudon_board_bg_tiles.4bpp: \ - $(foreach s,bg0 bg1,$(GROUDON)/groudon_board_$(s)_screen.png $(GROUDON)/groudon_board_$(s)_tilemap.bin) \ - tools/scripts/detilemap.py - $(DETILEMAP) extract -n 1024 -o $@ \ - --from $(GROUDON)/groudon_board_bg0_screen.png:$(GROUDON)/groudon_board_bg0_tilemap.bin:32x32 \ - --from $(GROUDON)/groudon_board_bg1_screen.png:$(GROUDON)/groudon_board_bg1_tilemap.bin:32x32 - -# Sapphire shares Ruby's BG configuration and scroll ring exactly. -SAPPHIRE := graphics/stage/sapphire -$(SAPPHIRE)/sapphire_board_strip.4bpp: $(SAPPHIRE)/sapphire_board_screen.png \ - $(SAPPHIRE)/sapphire_board_bg0_tilemap.bin \ - tools/scripts/detilemap.py - $(DETILEMAP) extract --row-chunk 32 -n 2048 -o $@ \ - --from $(SAPPHIRE)/sapphire_board_screen.png:$(SAPPHIRE)/sapphire_board_bg0_tilemap.bin:32x64 - -$(SAPPHIRE)/sapphire_board_bgtop_tiles.4bpp: $(SAPPHIRE)/sapphire_board_strip.4bpp - head -c 32768 $< > $@ - -$(SAPPHIRE)/sapphire_board_bgbottom_tiles.4bpp: $(SAPPHIRE)/sapphire_board_strip.4bpp - tail -c 32768 $< > $@ - -# Same BG0 runtime-tilemap region as Ruby: tiles 256-318 and 416-510 are -# unreachable statically. -$(SAPPHIRE)/sapphire_board_bgramp_tiles.4bpp: $(SAPPHIRE)/sapphire_board_ramp_screen.png \ - $(SAPPHIRE)/sapphire_board_bgramp_tilemap.bin \ - $(SAPPHIRE)/sapphire_board_ramp_unmapped_a.png \ - $(SAPPHIRE)/sapphire_board_ramp_unmapped_b.png tools/scripts/detilemap.py - $(DETILEMAP) extract -n 1024 -o $@ \ - --from $(SAPPHIRE)/sapphire_board_ramp_screen.png:$(SAPPHIRE)/sapphire_board_bgramp_tilemap.bin:32x64 \ - --extra $(SAPPHIRE)/sapphire_board_ramp_unmapped_a.png@256 \ - --extra $(SAPPHIRE)/sapphire_board_ramp_unmapped_b.png@416 - -$(SAPPHIRE)/sapphire_board_bg_tiles.4bpp: $(SAPPHIRE)/sapphire_board_bg_screen.png \ - $(SAPPHIRE)/sapphire_board_bg_tilemap.bin \ - $(SAPPHIRE)/sapphire_board_bg_unmapped_a.png \ - $(SAPPHIRE)/sapphire_board_bg_unmapped_b.png tools/scripts/detilemap.py - $(DETILEMAP) extract -n 1024 -o $@ \ - --from $(SAPPHIRE)/sapphire_board_bg_screen.png:$(SAPPHIRE)/sapphire_board_bg_tilemap.bin:32x64 \ - --extra $(SAPPHIRE)/sapphire_board_bg_unmapped_a.png@705 \ - --extra $(SAPPHIRE)/sapphire_board_bg_unmapped_b.png@880 - -RUBY := graphics/stage/ruby - -# The board art itself. Ruby scrolls, so char base 2 holds a 22-slot ring over a -# 64-chunk virtual strip (bgtop then bgbottom) and tilemap row R draws chunk R -- -# hence --row-chunk 32. One screen therefore reconstructs both sheets, which are -# split back apart below. See data/board_data/VRAM_LAYOUT.md. -$(RUBY)/ruby_board_strip.4bpp: $(RUBY)/ruby_board_screen.png $(RUBY)/ruby_board_bg0_tilemap.bin \ - tools/scripts/detilemap.py - $(DETILEMAP) extract --row-chunk 32 -n 2048 -o $@ \ - --from $(RUBY)/ruby_board_screen.png:$(RUBY)/ruby_board_bg0_tilemap.bin:32x64 - -$(RUBY)/ruby_board_bgtop_tiles.4bpp: $(RUBY)/ruby_board_strip.4bpp - head -c 32768 $< > $@ - -$(RUBY)/ruby_board_bgbottom_tiles.4bpp: $(RUBY)/ruby_board_strip.4bpp - tail -c 32768 $< > $@ - -# The ramp sheet sits at char base 1, which BG0 also reads -- but BG0's tilemap -# is built at runtime, so tiles 256-318 and 416-510 are unreachable statically -# and ride along as strips. -$(RUBY)/ruby_board_bgramp_tiles.4bpp: $(RUBY)/ruby_board_ramp_screen.png \ - $(RUBY)/ruby_board_ramp_tilemap.bin \ - $(RUBY)/ruby_board_ramp_unmapped_a.png \ - $(RUBY)/ruby_board_ramp_unmapped_b.png tools/scripts/detilemap.py - $(DETILEMAP) extract -n 1024 -o $@ \ - --from $(RUBY)/ruby_board_ramp_screen.png:$(RUBY)/ruby_board_ramp_tilemap.bin:32x64 \ - --extra $(RUBY)/ruby_board_ramp_unmapped_a.png@256 \ - --extra $(RUBY)/ruby_board_ramp_unmapped_b.png@416 - -# tile 834 is non-blank but referenced by no tilemap. -$(RUBY)/ruby_board_bg_tiles.4bpp: $(RUBY)/ruby_board_bg_screen.png $(RUBY)/ruby_board_bg_tilemap.bin \ - $(RUBY)/ruby_board_bg_unmapped.png tools/scripts/detilemap.py - $(DETILEMAP) extract -n 1024 -o $@ \ - --from $(RUBY)/ruby_board_bg_screen.png:$(RUBY)/ruby_board_bg_tilemap.bin:32x64 \ - --extra $(RUBY)/ruby_board_bg_unmapped.png@834 - -S9BALL := graphics/intro/scene9ballflight -$(S9BALL)/cloud_tiles.4bpp: $(S9BALL)/bg3_clouds_screen.png $(S9BALL)/bg3_clouds_tilemap.bin \ - tools/scripts/detilemap.py - $(DETILEMAP) extract -n 288 -o $@ \ - --from $(S9BALL)/bg3_clouds_screen.png:$(S9BALL)/bg3_clouds_tilemap.bin:32x32 - -S8WAIL := graphics/intro/scene8wailmerlaunch -# tiles 376-394 are non-blank; only 383 of them is reachable through the tilemap, -# so the whole run rides along as a strip (383 agrees with the screen). -$(S8WAIL)/explosion_tiles.4bpp: $(S8WAIL)/bg2_explosion_screen.png $(S8WAIL)/bg2_explosion_tilemap.bin \ - $(S8WAIL)/tiles_unmapped.png tools/scripts/detilemap.py - $(DETILEMAP) extract -n 416 -o $@ \ - --from $(S8WAIL)/bg2_explosion_screen.png:$(S8WAIL)/bg2_explosion_tilemap.bin:32x32 \ - --extra $(S8WAIL)/tiles_unmapped.png@376 - -S7PARADE := graphics/intro/scene7parade -# tile 590 is non-blank but referenced by no tilemap. -S7SCREENS := bg3_text bg0_pelipper bg1_spoink bg2_makuhita -$(S7PARADE)/tiles.4bpp: $(foreach s,$(S7SCREENS),$(S7PARADE)/$(s)_screen.png $(S7PARADE)/$(s)_tilemap.bin) \ - $(S7PARADE)/tiles_unmapped.png tools/scripts/detilemap.py - $(DETILEMAP) extract -n 640 -o $@ \ - --from $(S7PARADE)/bg3_text_screen.png:$(S7PARADE)/bg3_text_tilemap.bin:32x32 \ - --from $(S7PARADE)/bg0_pelipper_screen.png:$(S7PARADE)/bg0_pelipper_tilemap.bin:64x64 \ - --from $(S7PARADE)/bg1_spoink_screen.png:$(S7PARADE)/bg1_spoink_tilemap.bin:64x64 \ - --from $(S7PARADE)/bg2_makuhita_screen.png:$(S7PARADE)/bg2_makuhita_tilemap.bin:64x64 \ - --extra $(S7PARADE)/tiles_unmapped.png@590 - -S6CHIN := graphics/intro/scene6chinchou -# tiles 376-394 are non-blank but referenced by no tilemap, so they ride along -# as a 19-tile strip rather than being zeroed. -S6SCREENS := bg3_burst bg0_chinchou bg2_chinchou bg1_ball -$(S6CHIN)/tiles.4bpp: $(foreach s,$(S6SCREENS),$(S6CHIN)/$(s)_screen.png $(S6CHIN)/$(s)_tilemap.bin) \ - $(S6CHIN)/tiles_unmapped.png tools/scripts/detilemap.py - $(DETILEMAP) extract -n 800 -o $@ \ - --from $(S6CHIN)/bg3_burst_screen.png:$(S6CHIN)/bg3_burst_tilemap.bin:32x32 \ - --from $(S6CHIN)/bg0_chinchou_screen.png:$(S6CHIN)/bg0_chinchou_tilemap.bin:32x32 \ - --from $(S6CHIN)/bg2_chinchou_screen.png:$(S6CHIN)/bg2_chinchou_tilemap.bin:32x32 \ - --from $(S6CHIN)/bg1_ball_screen.png:$(S6CHIN)/bg1_ball_tilemap.bin:64x64 \ - --extra $(S6CHIN)/tiles_unmapped.png@376 - -S5MUDKIP := graphics/intro/scene5mudkip -S5SCREENS := bg3_text bg0 bg1_1sections bg2_2sections -$(S5MUDKIP)/text_tiles.4bpp: $(foreach s,$(S5SCREENS),$(S5MUDKIP)/$(s)_screen.png $(S5MUDKIP)/$(s)_tilemap.bin) \ - tools/scripts/detilemap.py - $(DETILEMAP) extract -n 640 -o $@ \ - --from $(S5MUDKIP)/bg3_text_screen.png:$(S5MUDKIP)/bg3_text_tilemap.bin:32x32 \ - --from $(S5MUDKIP)/bg0_screen.png:$(S5MUDKIP)/bg0_tilemap.bin:32x32 \ - --from $(S5MUDKIP)/bg1_1sections_screen.png:$(S5MUDKIP)/bg1_1sections_tilemap.bin:32x64 \ - --from $(S5MUDKIP)/bg2_2sections_screen.png:$(S5MUDKIP)/bg2_2sections_tilemap.bin:32x64 - -S4PLUSMIN := graphics/intro/scene4plussleminun -# 513 tiles, not 512: the old segmented rule appended one blank tile via -# "align": 1, and tile 512 is that padding. -$(S4PLUSMIN)/tiles.4bpp: $(S4PLUSMIN)/bg3_text_screen.png $(S4PLUSMIN)/bg3_text_tilemap.bin \ - $(S4PLUSMIN)/bg0_minun_screen.png $(S4PLUSMIN)/bg0_minun_tilemap.bin \ - $(S4PLUSMIN)/bg1_plussle_screen.png $(S4PLUSMIN)/bg1_plussle_tilemap.bin \ - tools/scripts/detilemap.py - $(DETILEMAP) extract -n 513 -o $@ \ - --from $(S4PLUSMIN)/bg3_text_screen.png:$(S4PLUSMIN)/bg3_text_tilemap.bin:32x32 \ - --from $(S4PLUSMIN)/bg0_minun_screen.png:$(S4PLUSMIN)/bg0_minun_tilemap.bin:64x64 \ - --from $(S4PLUSMIN)/bg1_plussle_screen.png:$(S4PLUSMIN)/bg1_plussle_tilemap.bin:64x64 - -S3TRECK := graphics/intro/scene3treecko -# tile 303 sits in the gap between bg3's range (ends 302) and bg2's (starts 304) -# and is referenced by no tilemap, so it is carried as a one-tile strip. -S3SCREENS := bg3_text bg2 bg0_treecko bg1_ball bg1_ballandflipper bg1_ballflipperhit -$(S3TRECK)/tiles.4bpp: $(foreach s,$(S3SCREENS),$(S3TRECK)/$(s)_screen.png $(S3TRECK)/$(s)_tilemap.bin) \ - $(S3TRECK)/tiles_unmapped.png tools/scripts/detilemap.py - $(DETILEMAP) extract -n 864 -o $@ \ - --from $(S3TRECK)/bg3_text_screen.png:$(S3TRECK)/bg3_text_tilemap.bin:32x32 \ - --from $(S3TRECK)/bg2_screen.png:$(S3TRECK)/bg2_tilemap.bin:32x64 \ - --from $(S3TRECK)/bg0_treecko_screen.png:$(S3TRECK)/bg0_treecko_tilemap.bin:32x64 \ - --from $(S3TRECK)/bg1_ball_screen.png:$(S3TRECK)/bg1_ball_tilemap.bin:32x32 \ - --from $(S3TRECK)/bg1_ballandflipper_screen.png:$(S3TRECK)/bg1_ballandflipper_tilemap.bin:32x32 \ - --from $(S3TRECK)/bg1_ballflipperhit_screen.png:$(S3TRECK)/bg1_ballflipperhit_tilemap.bin:32x32 \ - --extra $(S3TRECK)/tiles_unmapped.png@303 - -S2PIKAS := graphics/intro/scene2pikas -$(S2PIKAS)/tiles.4bpp: $(S2PIKAS)/bg3_text_screen.png $(S2PIKAS)/bg3_text_tilemap.bin \ - $(S2PIKAS)/bg0_pichu_screen.png $(S2PIKAS)/bg0_pichu_tilemap.bin \ - $(S2PIKAS)/bg1_pikachu_screen.png $(S2PIKAS)/bg1_pikachu_tilemap.bin \ - tools/scripts/detilemap.py - $(DETILEMAP) extract -n 512 -o $@ \ - --from $(S2PIKAS)/bg3_text_screen.png:$(S2PIKAS)/bg3_text_tilemap.bin:32x32 \ - --from $(S2PIKAS)/bg0_pichu_screen.png:$(S2PIKAS)/bg0_pichu_tilemap.bin:64x64 \ - --from $(S2PIKAS)/bg1_pikachu_screen.png:$(S2PIKAS)/bg1_pikachu_tilemap.bin:64x64 - -# Sheets built by detilemap have no .png source, so the Makefile's -# SECONDARY_ASSETS list -- which is derived from png/pal/wav sources -- does not -# cover them or their .lz derivatives, and make deletes those as intermediates -# after every build. .SECONDARY accumulates, so listing them here is enough. -DETILEMAP_SHEETS := \ - $(S1TORCHIC)/tiles.4bpp \ - $(S2PIKAS)/tiles.4bpp \ - $(S3TRECK)/tiles.4bpp \ - $(S4PLUSMIN)/tiles.4bpp \ - $(S5MUDKIP)/text_tiles.4bpp \ - $(S6CHIN)/tiles.4bpp \ - $(S7PARADE)/tiles.4bpp \ - $(S8WAIL)/explosion_tiles.4bpp \ - $(S9BALL)/cloud_tiles.4bpp \ - $(RUBY)/ruby_board_bg_tiles.4bpp \ - $(DUSCLOPS)/dusclops_board_bg_tiles.4bpp \ - $(GROUDON)/groudon_board_bg_tiles.4bpp \ - $(KECLEON)/kecleon_board_bg_tiles.4bpp \ - $(KYOGRE)/kyogre_board_bg_tiles.4bpp \ - $(RUBY)/ruby_board_strip.4bpp \ - $(RUBY)/ruby_board_bgtop_tiles.4bpp \ - $(RUBY)/ruby_board_bgbottom_tiles.4bpp \ - $(RUBY)/ruby_board_bgramp_tiles.4bpp \ - $(SAPPHIRE)/sapphire_board_strip.4bpp \ - $(SAPPHIRE)/sapphire_board_bgtop_tiles.4bpp \ - $(SAPPHIRE)/sapphire_board_bgbottom_tiles.4bpp \ - $(SAPPHIRE)/sapphire_board_bgramp_tiles.4bpp \ - $(SAPPHIRE)/sapphire_board_bg_tiles.4bpp \ - $(RAYQUAZA)/rayquaza_board_bg_tiles.4bpp \ - $(SPHEAL)/spheal_board_bg_tiles.4bpp - -.SECONDARY: $(DETILEMAP_SHEETS) $(addsuffix .lz,$(DETILEMAP_SHEETS)) diff --git a/tools/scripts/detilemap.py b/tools/scripts/detilemap.py deleted file mode 100755 index 9455219d..00000000 --- a/tools/scripts/detilemap.py +++ /dev/null @@ -1,353 +0,0 @@ -#!/usr/bin/env python3 -"""detilemap -- convert between GBA BG tile sheets and assembled screen images. - -A BG tile sheet is stored deduplicated: the tilemap references each unique 8x8 -tile once, so the raw sheet is unreadable as a picture no matter what width it -is exported at. This tool moves the editable form to the *assembled screen*, -where the art is actually visible, and derives the sheet from it. - - assemble tiles.4bpp + tilemap.bin + palette.pal -> screen.png - extract screen.png + tilemap.bin -> tiles.4bpp - -The screen image is an 8bpp indexed PNG carrying the full 256-colour GBA -palette, with each pixel storing bank * 16 + colour_index. That keeps every -per-cell palette bank distinct and makes the conversion lossless in both -directions -- a 4bpp image could only hold one of the banks a screen uses. - -Flips are applied on assemble and undone on extract, so a cell that the tilemap -marks h/v flipped still contributes its unflipped pixels back to the sheet. - -Ruby and Sapphire stream their board through a scroll ring, so a tilemap row R -draws chunk R of a virtual strip rather than indexing the sheet directly. Pass -`--row-chunk N` for those: the tile index becomes `R*N + (id % N)`. See -data/board_data/VRAM_LAYOUT.md. - -Only stdlib is used, so this adds no build dependency. - -Examples --------- - detilemap.py assemble -t tiles.4bpp -m bg3.bin -p scene.pal -s 32x32 \\ - -o screen_bg3.png - - detilemap.py extract -n 704 -o tiles.4bpp \\ - --from screen_bg3.png:bg3.bin \\ - --from screen_bg1.png:bg1.bin - -`extract` takes as many screen/tilemap pairs as the sheet needs: one sheet is -often shared by several BG layers. Tiles that no supplied tilemap references -are emitted as zeroes and reported, so you can see exactly how much of the -sheet was reconstructed rather than assumed. -""" -import argparse -import struct -import sys -import zlib - -SCREEN_SIZES = {"32x32": (32, 32), "64x32": (64, 32), "32x64": (32, 64), "64x64": (64, 64)} - - -# ---------------------------------------------------------------- PNG (8bpp indexed) - -def png_write(path, width, height, pixels, palette): - """pixels: bytes, one byte per pixel. palette: list of (r,g,b), <=256 entries.""" - def chunk(tag, data): - return (struct.pack(">I", len(data)) + tag + data - + struct.pack(">I", zlib.crc32(tag + data) & 0xFFFFFFFF)) - - raw = bytearray() - for y in range(height): - raw.append(0) # filter type 0 (None) - raw += pixels[y * width:(y + 1) * width] - plte = b"".join(bytes(c) for c in palette) - out = (b"\x89PNG\r\n\x1a\n" - + chunk(b"IHDR", struct.pack(">IIBBBBB", width, height, 8, 3, 0, 0, 0)) - + chunk(b"PLTE", plte) - + chunk(b"IDAT", zlib.compress(bytes(raw), 9)) - + chunk(b"IEND", b"")) - with open(path, "wb") as fp: - fp.write(out) - - -def png_read(path): - """Returns (width, height, pixels bytes one-per-pixel, palette list).""" - data = open(path, "rb").read() - if data[:8] != b"\x89PNG\r\n\x1a\n": - raise SystemExit(f"{path}: not a PNG") - pos, idat, plte, ihdr = 8, bytearray(), None, None - while pos < len(data): - ln, tag = struct.unpack(">I4s", data[pos:pos + 8]) - body = data[pos + 8:pos + 8 + ln] - if tag == b"IHDR": - ihdr = struct.unpack(">IIBBBBB", body) - elif tag == b"PLTE": - plte = body - elif tag == b"IDAT": - idat += body - pos += 12 + ln - w, h, bd, ct, comp, filt, inter = ihdr - if ct != 3 or bd not in (1, 2, 4, 8): - raise SystemExit(f"{path}: need an indexed PNG (colour type 3), got type {ct} depth {bd}") - if inter: - raise SystemExit(f"{path}: interlaced PNGs are not supported") - - raw = zlib.decompress(bytes(idat)) - bpp = 1 # bytes per pixel for filtering purposes at bd<=8 - stride = (w * bd + 7) // 8 - rows, prev, pos = [], bytearray(stride), 0 - for _ in range(h): - ftype = raw[pos]; pos += 1 - line = bytearray(raw[pos:pos + stride]); pos += stride - for i in range(stride): - a = line[i - bpp] if i >= bpp else 0 - b = prev[i] - c = prev[i - bpp] if i >= bpp else 0 - if ftype == 1: line[i] = (line[i] + a) & 0xFF - elif ftype == 2: line[i] = (line[i] + b) & 0xFF - elif ftype == 3: line[i] = (line[i] + ((a + b) >> 1)) & 0xFF - elif ftype == 4: - p = a + b - c - pa, pb, pc = abs(p - a), abs(p - b), abs(p - c) - pr = a if (pa <= pb and pa <= pc) else (b if pb <= pc else c) - line[i] = (line[i] + pr) & 0xFF - elif ftype != 0: - raise SystemExit(f"{path}: bad PNG filter type {ftype}") - rows.append(bytes(line)); prev = line - - if bd == 8: - pixels = b"".join(rows) - else: # unpack sub-byte indices - px = bytearray() - mask = (1 << bd) - 1 - for line in rows: - for x in range(w): - byte = line[(x * bd) // 8] - shift = 8 - bd - ((x * bd) % 8) - px.append((byte >> shift) & mask) - pixels = bytes(px) - pal = [tuple(plte[i:i + 3]) for i in range(0, len(plte or b""), 3)] - return w, h, pixels, pal - - -# ---------------------------------------------------------------- GBA data - -def read_jasc(path): - lines = open(path, "rb").read().decode().replace("\r\n", "\n").split("\n") - if lines[0] != "JASC-PAL": - raise SystemExit(f"{path}: not a JASC palette") - n = int(lines[2]) - return [tuple(map(int, lines[3 + k].split())) for k in range(n)] - - -def split_map_arg(arg): - """`file.bin` or `file.bin@N` -- N is an entry offset, for files holding - several screens back to back.""" - if "@" in arg: - path, off = arg.rsplit("@", 1) - return path, int(off, 0) - return arg, 0 - - -def read_tilemap(path, cols, rows, offset=0): - """GBA stores >32-wide/tall screens as 32x32 screenblocks in TL,TR,BL,BR order.""" - blob = open(path, "rb").read() - entries = [int.from_bytes(blob[i:i + 2], "little") for i in range(0, len(blob), 2)] - entries = entries[offset:] - need = cols * rows - if len(entries) < need: - raise SystemExit(f"{path}: {len(entries)} entries from offset {offset}, " - f"need {need} for {cols}x{rows}") - cell = {} - blocks_x = cols // 32 - for i, e in enumerate(entries[:need]): - blk, off = divmod(i, 1024) - r = off // 32 + 32 * (blk // blocks_x) - c = off % 32 + 32 * (blk % blocks_x) - cell[(r, c)] = (e & 0x3FF, (e >> 10) & 1, (e >> 11) & 1, e >> 12) - return cell - - -def tile_pixels(sheet, tile): - """Return 64 palette indices for one 4bpp tile, row-major.""" - out = bytearray(64) - base = tile * 32 - for y in range(8): - for x in range(0, 8, 2): - byte = sheet[base + y * 4 + x // 2] - out[y * 8 + x] = byte & 0xF - out[y * 8 + x + 1] = byte >> 4 - return out - - -# ---------------------------------------------------------------- commands - -def cmd_assemble(a): - cols, rows = SCREEN_SIZES[a.size] - sheet = open(a.tiles, "rb").read() - ntiles = len(sheet) // 32 - pal = read_jasc(a.palette) - if len(pal) < 256: - pal = pal + [(0, 0, 0)] * (256 - len(pal)) - map_path, map_off = split_map_arg(a.map) - cells = read_tilemap(map_path, cols, rows, map_off) - - w, h = cols * 8, rows * 8 - px = bytearray(w * h) - missing = set() - for (r, c), (tile, hf, vf, bank) in cells.items(): - if a.row_chunk: - tile = r * a.row_chunk + (tile % a.row_chunk) - if tile >= ntiles: - missing.add(tile) - continue - tp = tile_pixels(sheet, tile) - for y in range(8): - for x in range(8): - sx = 7 - x if hf else x - sy = 7 - y if vf else y - px[(r * 8 + y) * w + c * 8 + x] = bank * 16 + tp[sy * 8 + sx] - png_write(a.output, w, h, bytes(px), pal[:256]) - print(f"assembled {a.output} {w}x{h} ({cols}x{rows} cells, {ntiles} tiles in sheet)") - if missing: - lo, hi = min(missing), max(missing) - print(f" note: {len(missing)} tile ids referenced but outside this sheet ({lo}..{hi}) " - f"-- those cells are left blank") - - -def cmd_extract(a): - sheet = bytearray(a.count * 32) - written = {} - conflicts = [] - - for spec in getattr(a, "from"): - parts = spec.split(":") - if len(parts) == 3 and parts[2] in SCREEN_SIZES: - png_path, map_path, size = parts - elif len(parts) == 2: - png_path, map_path, size = parts[0], parts[1], a.size - else: - raise SystemExit(f"--from needs PNG:TILEMAP[:SIZE], got {spec!r}") - cols, rows = SCREEN_SIZES[size] - w, h, px, _ = png_read(png_path) - if (w, h) != (cols * 8, rows * 8): - raise SystemExit(f"{png_path}: is {w}x{h}, expected {cols*8}x{rows*8} for size {size}") - map_file, map_off = split_map_arg(map_path) - cells = read_tilemap(map_file, cols, rows, map_off) - - for (r, c), (tile, hf, vf, bank) in cells.items(): - if a.row_chunk: - tile = r * a.row_chunk + (tile % a.row_chunk) - if tile >= a.count: - continue - tp = bytearray(64) - ok = True - for y in range(8): - for x in range(8): - v = px[(r * 8 + y) * w + c * 8 + x] - if v >> 4 != bank: - ok = False - sx = 7 - x if hf else x - sy = 7 - y if vf else y - tp[sy * 8 + sx] = v & 0xF - if not ok: - break - if not ok: - conflicts.append(("bank", png_path, r, c, tile, bank)) - continue - packed = bytes((tp[i * 8 + j] | (tp[i * 8 + j + 1] << 4)) - for i in range(8) for j in range(0, 8, 2)) - if tile in written and written[tile] != packed: - conflicts.append(("pixels", png_path, r, c, tile, bank)) - continue - written[tile] = packed - - # Tiles that live in the sheet but that no tilemap references. They cannot - # come from a screen, so they are supplied as a plain linear tile strip. - for spec in getattr(a, "extra", None) or []: - png_path, first = split_map_arg(spec) - if "@" not in spec: - raise SystemExit(f"--extra needs PNG@FIRST_TILE, got {spec!r}") - w, h, px, _ = png_read(png_path) - if w % 8 or h % 8: - raise SystemExit(f"{png_path}: is {w}x{h}, must be a whole number of 8x8 tiles") - per_row = w // 8 - for n in range((w // 8) * (h // 8)): - tile = first + n - if tile >= a.count: - raise SystemExit(f"{png_path}: tile {tile} is past the end of a {a.count}-tile sheet") - tr, tc = divmod(n, per_row) - packed = bytes( - ((px[(tr * 8 + y) * w + tc * 8 + x] & 0xF) - | ((px[(tr * 8 + y) * w + tc * 8 + x + 1] & 0xF) << 4)) - for y in range(8) for x in range(0, 8, 2)) - if tile in written and written[tile] != packed: - conflicts.append(("pixels", png_path, 0, n, tile, 0)) - continue - written[tile] = packed - - for tile, packed in written.items(): - sheet[tile * 32:(tile + 1) * 32] = packed - - if conflicts: - print(f"error: {len(conflicts)} cell(s) did not agree with the tilemap:", file=sys.stderr) - for kind, p, r, c, tile, bank in conflicts[:10]: - if kind == "bank": - print(f" {p} cell ({r},{c}): pixels are not in palette bank {bank}", file=sys.stderr) - else: - print(f" {p} cell ({r},{c}): tile {tile} drawn two different ways", file=sys.stderr) - if len(conflicts) > 10: - print(f" ... and {len(conflicts) - 10} more", file=sys.stderr) - return 1 - - with open(a.output, "wb") as fp: - fp.write(sheet) - blank = a.count - len(written) - print(f"extracted {a.output} {a.count} tiles, {len(written)} reconstructed from screens") - if blank: - ids = sorted(set(range(a.count)) - set(written)) - runs, start = [], ids[0] - for i in range(1, len(ids) + 1): - if i == len(ids) or ids[i] != ids[i - 1] + 1: - runs.append((start, ids[i - 1])); start = ids[i] if i < len(ids) else 0 - pretty = ", ".join(f"{x}" if x == y else f"{x}-{y}" for x, y in runs[:6]) - print(f" note: {blank} tile(s) unreferenced, emitted as zeroes ({pretty}" - f"{', ...' if len(runs) > 6 else ''})") - return 0 - - -def main(): - ap = argparse.ArgumentParser(description=__doc__.split("\n")[0], - formatter_class=argparse.RawDescriptionHelpFormatter) - sub = ap.add_subparsers(dest="cmd", required=True) - - asm = sub.add_parser("assemble", help="tiles + tilemap + palette -> screen PNG") - asm.add_argument("-t", "--tiles", required=True) - asm.add_argument("-m", "--map", required=True) - asm.add_argument("-p", "--palette", required=True) - asm.add_argument("-s", "--size", default="32x32", choices=sorted(SCREEN_SIZES)) - asm.add_argument("-o", "--output", required=True) - asm.add_argument("--row-chunk", type=int, default=0, metavar="N", - help="scroll-ring addressing: tile index is R*N + (id %% N)") - asm.set_defaults(fn=cmd_assemble) - - ext = sub.add_parser("extract", help="screen PNG(s) + tilemap(s) -> tiles") - ext.add_argument("--from", action="append", required=True, metavar="PNG:TILEMAP[:SIZE]", - help="screen image and the tilemap that placed it, with an optional " - "per-pair screen size; repeatable. The tilemap may carry an " - "@ENTRY_OFFSET suffix when one file holds several screens.") - ext.add_argument("--extra", action="append", metavar="PNG@FIRST_TILE", - help="linear tile strip for sheet tiles no tilemap references; " - "written into consecutive slots from FIRST_TILE. Repeatable.") - ext.add_argument("-n", "--count", type=int, required=True, help="tiles in the output sheet") - ext.add_argument("-s", "--size", default="32x32", choices=sorted(SCREEN_SIZES), - help="default screen size for --from pairs that do not give one") - ext.add_argument("-o", "--output", required=True) - ext.add_argument("--row-chunk", type=int, default=0, metavar="N", - help="scroll-ring addressing: tile index is R*N + (id %% N)") - ext.set_defaults(fn=cmd_extract) - - a = ap.parse_args() - return a.fn(a) or 0 - - -if __name__ == "__main__": - sys.exit(main())