diff --git a/data/rom_1.s b/data/rom_1.s index 7ef4e865..789b0466 100644 --- a/data/rom_1.s +++ b/data/rom_1.s @@ -751,7 +751,7 @@ gBallFlashPalette:: @ 0x08137F14 .incbin "baserom.gba", 0x137F14, 0x100 gCaptureBallTilesGfx:: @ 0x08138014 - .incbin "graphics/stage/main/capture_ball_variants.4bpp" + .incbin "graphics/stage/main/ball_open_to_catch.4bpp" .space 0x20 gDusclopsBonusClear_Gfx:: @ 0x08138834 @@ -1361,8 +1361,8 @@ gKyogreBodySprites_After15:: @ 0x0849B8CC gGroudonBoardBackgroundGfx:: @ 0x0849F1CC .incbin "baserom.gba", 0x49F1CC, 0x2020 -gGroudonBoulderSpriteFrames:: @ 0x084A11EC - .incbin "baserom.gba", 0x4A11EC, 0x5D00 +gGroudonBoardBoulders_Gfx:: @ 0x084A11EC + .incbin "graphics/stage/groudon/boulders.4bpp"; gRayquazaMinionOrbFrames:: @ 0x084A6EEC .incbin "baserom.gba", 0x4A6EEC, 0x1680 diff --git a/graphics/stage/groudon/boulder_shape.json b/graphics/stage/groudon/boulder_shape.json new file mode 100644 index 00000000..036f2780 --- /dev/null +++ b/graphics/stage/groudon/boulder_shape.json @@ -0,0 +1,24 @@ +{ + "kind": "oam-shape", + "version":1, + "pngsize": "5x5", + "pieces": [ + { + "pos":"0,0", + "size": "1x1", + "spacer":true + }, + { + "pos":"1,0", + "size": "4x1" + }, + { + "pos":"0,1", + "size": "1x4" + }, + { + "pos":"1,1", + "size": "4x4" + } + ] +} \ No newline at end of file diff --git a/graphics/stage/groudon/boulders.png b/graphics/stage/groudon/boulders.png new file mode 100644 index 00000000..41d9b4ae Binary files /dev/null and b/graphics/stage/groudon/boulders.png differ diff --git a/graphics/stage/groudon/crystal_shape.json b/graphics/stage/groudon/crystal_shape.json new file mode 100644 index 00000000..e0a15c78 --- /dev/null +++ b/graphics/stage/groudon/crystal_shape.json @@ -0,0 +1,24 @@ +{ + "kind": "oam-shape", + "version":1, + "pngsize": "3x3", + "pieces": [ + { + "pos":"0,0", + "size": "1x2" + }, + { + "pos":"0,2", + "size": "1x1", + "spacer":true + }, + { + "pos":"1,2", + "size": "2x1" + }, + { + "pos":"1,0", + "size": "2x2" + } + ] +} \ No newline at end of file diff --git a/graphics/stage/groudon/groudon_head_shape.json b/graphics/stage/groudon/groudon_head_shape.json new file mode 100644 index 00000000..ed471bfc --- /dev/null +++ b/graphics/stage/groudon/groudon_head_shape.json @@ -0,0 +1,42 @@ +{ + "kind": "oam-shape", + "version":1, + "pngsize": "4x4", + "pieces": [ + { + "pos":"0,0", + "size": "1x1", + "spacer":true + }, + { + "_comment":"Fins", + "pos":"1,0", + "size": "2x1" + }, + { + "pos":"3,0", + "size": "1x1", + "spacer":true + }, + { + "_comment":"Face", + "pos":"0,1", + "size": "4x2" + }, + { + "pos":"0,3", + "size": "1x1", + "spacer":true + }, + { + "_comment":"Chin", + "pos":"1,3", + "size": "2x1" + }, + { + "pos":"3,3", + "size": "1x1", + "spacer":true + } + ] +} \ No newline at end of file diff --git a/graphics/stage/groudon/groudon_stage_gfx.json b/graphics/stage/groudon/groudon_stage_gfx.json index 43b63503..3b6daeca 100644 --- a/graphics/stage/groudon/groudon_stage_gfx.json +++ b/graphics/stage/groudon/groudon_stage_gfx.json @@ -83,80 +83,16 @@ "width": 6 }, { - "segfile": "intro_sprite_rock_1_top", - "width": 4 - }, - { - "segfile": "intro_sprite_rock_1_left", - "width": 1 - }, - { - "segfile": "intro_sprite_rock_1_right", - "width": 4 - }, - { - "segfile": "intro_sprite_rock_2_top", - "width": 4 - }, - { - "segfile": "intro_sprite_rock_2_left", - "width": 1 - }, - { - "segfile": "intro_sprite_rock_2_right", - "width": 4 - }, - { - "segfile": "intro_sprite_rock_3_top", - "width": 4 - }, - { - "segfile": "intro_sprite_rock_3_left", - "width": 1 - }, - { - "segfile": "intro_sprite_rock_3_right", - "width": 4 + "segfile": "intro_sprite_boulders", + "oamshape": "boulder_shape.json" }, { "segfile": "intro_sprite_quarter_circle", "width": 8 }, { - "segfile": "intro_sprite_crystal_1_left", - "width": 1 - }, - { - "segfile": "intro_sprite_crystal_1_bottom", - "width": 2 - }, - { - "segfile": "intro_sprite_crystal_1_top_right", - "width": 2 - }, - { - "segfile": "intro_sprite_crystal_2_left", - "width": 1 - }, - { - "segfile": "intro_sprite_crystal_2_bottom", - "width": 2 - }, - { - "segfile": "intro_sprite_crystal_2_top_right", - "width": 2 - }, - { - "segfile": "intro_sprite_crystal_3_left", - "width": 1 - }, - { - "segfile": "intro_sprite_crystal_3_bottom", - "width": 2 - }, - { - "segfile": "intro_sprite_crystal_3_top_right", - "width": 2 + "segfile": "intro_sprite_crystals", + "oamshape": "crystal_shape.json" }, { "segfile": "intro_sprite_flame_pillars", @@ -171,16 +107,8 @@ "width": 4 }, { - "segfile": "intro_sprite_groudon_fins", - "width": 2 - }, - { - "segfile": "intro_sprite_groudon_face", - "width": 4 - }, - { - "segfile": "intro_sprite_groudon_chin", - "width": 2 + "segfile": "intro_sprite_head_1", + "oamshape": "groudon_head_shape.json" }, { "segfile": "intro_sprite_groudon_body", @@ -195,16 +123,8 @@ "width": 2 }, { - "segfile": "intro_sprite_groudon_fins_2", - "width": 2 - }, - { - "segfile": "intro_sprite_groudon_face_2", - "width": 4 - }, - { - "segfile": "intro_sprite_groudon_chin_2", - "width": 2 + "segfile": "intro_sprite_head_2", + "oamshape": "groudon_head_shape.json" }, { "segfile": "intro_sprite_groudon_leg_2", @@ -243,40 +163,8 @@ "width": 2 }, { - "segfile": "intro_sprite_groudon_fin_3", - "width": 2 - }, - { - "segfile": "intro_sprite_groudon_face_3", - "width": 4 - }, - { - "segfile": "intro_sprite_groudon_chin_3", - "width": 2 - }, - { - "segfile": "intro_sprite_groudon_profile_fin", - "width": 2 - }, - { - "segfile": "intro_sprite_groudon_profile_face", - "width": 4 - }, - { - "segfile": "intro_sprite_groudon_profile_chin", - "width": 2 - }, - { - "segfile": "intro_sprite_groudon_quarter_fin", - "width": 2 - }, - { - "segfile": "intro_sprite_groudon_quarter_face", - "width": 4 - }, - { - "segfile": "intro_sprite_groudon_quarter_chin", - "width": 2 + "segfile": "intro_sprite_face_turn", + "oamshape": "groudon_head_shape.json" }, { "segfile": "intro_sprite_rock_bits_2", @@ -287,28 +175,8 @@ "width": 2 }, { - "segfile": "intro_sprite_groudon_yell_fin", - "width": 2 - }, - { - "segfile": "intro_sprite_groudon_yell_face", - "width": 4 - }, - { - "segfile": "intro_sprite_groudon_yell_chin", - "width": 2 - }, - { - "segfile": "intro_sprite_groudon_yell_fin_2", - "width": 2 - }, - { - "segfile": "intro_sprite_groudon_yell_face_2", - "width": 4 - }, - { - "segfile": "intro_sprite_groudon_yell_chin_2", - "width": 2 + "segfile": "intro_sprite_face_yell", + "oamshape": "groudon_head_shape.json" }, { "segfile": "intro_sprite_groudon_arms", @@ -347,6 +215,10 @@ "width": 7 } ] + }, + { + "gfx_filename": "boulders", + "oamshape": "boulder_shape.json" } ] } diff --git a/graphics/stage/groudon/intro_sprite_boulders.png b/graphics/stage/groudon/intro_sprite_boulders.png new file mode 100644 index 00000000..f7e003e0 Binary files /dev/null and b/graphics/stage/groudon/intro_sprite_boulders.png differ diff --git a/graphics/stage/groudon/intro_sprite_crystal_1_bottom.png b/graphics/stage/groudon/intro_sprite_crystal_1_bottom.png deleted file mode 100644 index 45e3f5eb..00000000 Binary files a/graphics/stage/groudon/intro_sprite_crystal_1_bottom.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_crystal_1_left.png b/graphics/stage/groudon/intro_sprite_crystal_1_left.png deleted file mode 100644 index 41116556..00000000 Binary files a/graphics/stage/groudon/intro_sprite_crystal_1_left.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_crystal_1_top_right.png b/graphics/stage/groudon/intro_sprite_crystal_1_top_right.png deleted file mode 100644 index 542d6bc7..00000000 Binary files a/graphics/stage/groudon/intro_sprite_crystal_1_top_right.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_crystal_2_bottom.png b/graphics/stage/groudon/intro_sprite_crystal_2_bottom.png deleted file mode 100644 index 228066e8..00000000 Binary files a/graphics/stage/groudon/intro_sprite_crystal_2_bottom.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_crystal_2_left.png b/graphics/stage/groudon/intro_sprite_crystal_2_left.png deleted file mode 100644 index c7a8c0d7..00000000 Binary files a/graphics/stage/groudon/intro_sprite_crystal_2_left.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_crystal_2_top_right.png b/graphics/stage/groudon/intro_sprite_crystal_2_top_right.png deleted file mode 100644 index 9bc6117f..00000000 Binary files a/graphics/stage/groudon/intro_sprite_crystal_2_top_right.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_crystal_3_bottom.png b/graphics/stage/groudon/intro_sprite_crystal_3_bottom.png deleted file mode 100644 index 20f18b92..00000000 Binary files a/graphics/stage/groudon/intro_sprite_crystal_3_bottom.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_crystal_3_left.png b/graphics/stage/groudon/intro_sprite_crystal_3_left.png deleted file mode 100644 index 5124ad2a..00000000 Binary files a/graphics/stage/groudon/intro_sprite_crystal_3_left.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_crystal_3_top_right.png b/graphics/stage/groudon/intro_sprite_crystal_3_top_right.png deleted file mode 100644 index 1213a67c..00000000 Binary files a/graphics/stage/groudon/intro_sprite_crystal_3_top_right.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_crystals.png b/graphics/stage/groudon/intro_sprite_crystals.png new file mode 100644 index 00000000..a87f96f9 Binary files /dev/null and b/graphics/stage/groudon/intro_sprite_crystals.png differ diff --git a/graphics/stage/groudon/intro_sprite_face_turn.png b/graphics/stage/groudon/intro_sprite_face_turn.png new file mode 100644 index 00000000..b7c10e5e Binary files /dev/null and b/graphics/stage/groudon/intro_sprite_face_turn.png differ diff --git a/graphics/stage/groudon/intro_sprite_face_yell.png b/graphics/stage/groudon/intro_sprite_face_yell.png new file mode 100644 index 00000000..877a4a1b Binary files /dev/null and b/graphics/stage/groudon/intro_sprite_face_yell.png differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_chin.png b/graphics/stage/groudon/intro_sprite_groudon_chin.png deleted file mode 100644 index c8232e96..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_chin.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_chin_2.png b/graphics/stage/groudon/intro_sprite_groudon_chin_2.png deleted file mode 100644 index 04beedb0..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_chin_2.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_chin_3.png b/graphics/stage/groudon/intro_sprite_groudon_chin_3.png deleted file mode 100644 index 56a81301..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_chin_3.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_face.png b/graphics/stage/groudon/intro_sprite_groudon_face.png deleted file mode 100644 index a91db05a..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_face.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_face_2.png b/graphics/stage/groudon/intro_sprite_groudon_face_2.png deleted file mode 100644 index f573360f..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_face_2.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_face_3.png b/graphics/stage/groudon/intro_sprite_groudon_face_3.png deleted file mode 100644 index da90b087..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_face_3.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_fin_3.png b/graphics/stage/groudon/intro_sprite_groudon_fin_3.png deleted file mode 100644 index a6a3949d..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_fin_3.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_fins.png b/graphics/stage/groudon/intro_sprite_groudon_fins.png deleted file mode 100644 index a6a3949d..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_fins.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_fins_2.png b/graphics/stage/groudon/intro_sprite_groudon_fins_2.png deleted file mode 100644 index a6a3949d..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_fins_2.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_profile_chin.png b/graphics/stage/groudon/intro_sprite_groudon_profile_chin.png deleted file mode 100644 index acd859d6..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_profile_chin.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_profile_face.png b/graphics/stage/groudon/intro_sprite_groudon_profile_face.png deleted file mode 100644 index 82e7c09e..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_profile_face.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_profile_fin.png b/graphics/stage/groudon/intro_sprite_groudon_profile_fin.png deleted file mode 100644 index 495e9f3d..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_profile_fin.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_quarter_chin.png b/graphics/stage/groudon/intro_sprite_groudon_quarter_chin.png deleted file mode 100644 index 21d55c89..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_quarter_chin.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_quarter_face.png b/graphics/stage/groudon/intro_sprite_groudon_quarter_face.png deleted file mode 100644 index 028cfb5f..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_quarter_face.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_quarter_fin.png b/graphics/stage/groudon/intro_sprite_groudon_quarter_fin.png deleted file mode 100644 index 8e888c04..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_quarter_fin.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_yell_chin.png b/graphics/stage/groudon/intro_sprite_groudon_yell_chin.png deleted file mode 100644 index f00630a2..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_yell_chin.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_yell_chin_2.png b/graphics/stage/groudon/intro_sprite_groudon_yell_chin_2.png deleted file mode 100644 index bd97dc2d..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_yell_chin_2.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_yell_face.png b/graphics/stage/groudon/intro_sprite_groudon_yell_face.png deleted file mode 100644 index 49a97468..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_yell_face.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_yell_face_2.png b/graphics/stage/groudon/intro_sprite_groudon_yell_face_2.png deleted file mode 100644 index 240a4248..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_yell_face_2.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_yell_fin.png b/graphics/stage/groudon/intro_sprite_groudon_yell_fin.png deleted file mode 100644 index 99d46f95..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_yell_fin.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_groudon_yell_fin_2.png b/graphics/stage/groudon/intro_sprite_groudon_yell_fin_2.png deleted file mode 100644 index 3b70971c..00000000 Binary files a/graphics/stage/groudon/intro_sprite_groudon_yell_fin_2.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_head_1.png b/graphics/stage/groudon/intro_sprite_head_1.png new file mode 100644 index 00000000..4bcd0cc8 Binary files /dev/null and b/graphics/stage/groudon/intro_sprite_head_1.png differ diff --git a/graphics/stage/groudon/intro_sprite_head_2.png b/graphics/stage/groudon/intro_sprite_head_2.png new file mode 100644 index 00000000..3856b3c5 Binary files /dev/null and b/graphics/stage/groudon/intro_sprite_head_2.png differ diff --git a/graphics/stage/groudon/intro_sprite_rock_1_left.png b/graphics/stage/groudon/intro_sprite_rock_1_left.png deleted file mode 100644 index 4300b31f..00000000 Binary files a/graphics/stage/groudon/intro_sprite_rock_1_left.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_rock_1_right.png b/graphics/stage/groudon/intro_sprite_rock_1_right.png deleted file mode 100644 index a5eb9596..00000000 Binary files a/graphics/stage/groudon/intro_sprite_rock_1_right.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_rock_1_top.png b/graphics/stage/groudon/intro_sprite_rock_1_top.png deleted file mode 100644 index 2e1dbc7b..00000000 Binary files a/graphics/stage/groudon/intro_sprite_rock_1_top.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_rock_2_left.png b/graphics/stage/groudon/intro_sprite_rock_2_left.png deleted file mode 100644 index 4300b31f..00000000 Binary files a/graphics/stage/groudon/intro_sprite_rock_2_left.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_rock_2_right.png b/graphics/stage/groudon/intro_sprite_rock_2_right.png deleted file mode 100644 index a5eb9596..00000000 Binary files a/graphics/stage/groudon/intro_sprite_rock_2_right.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_rock_2_top.png b/graphics/stage/groudon/intro_sprite_rock_2_top.png deleted file mode 100644 index 2e1dbc7b..00000000 Binary files a/graphics/stage/groudon/intro_sprite_rock_2_top.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_rock_3_left.png b/graphics/stage/groudon/intro_sprite_rock_3_left.png deleted file mode 100644 index 4300b31f..00000000 Binary files a/graphics/stage/groudon/intro_sprite_rock_3_left.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_rock_3_right.png b/graphics/stage/groudon/intro_sprite_rock_3_right.png deleted file mode 100644 index a5eb9596..00000000 Binary files a/graphics/stage/groudon/intro_sprite_rock_3_right.png and /dev/null differ diff --git a/graphics/stage/groudon/intro_sprite_rock_3_top.png b/graphics/stage/groudon/intro_sprite_rock_3_top.png deleted file mode 100644 index 2e1dbc7b..00000000 Binary files a/graphics/stage/groudon/intro_sprite_rock_3_top.png and /dev/null differ diff --git a/graphics/stage/main/ball_open_to_catch.json b/graphics/stage/main/ball_open_to_catch.json new file mode 100644 index 00000000..ee0f479d --- /dev/null +++ b/graphics/stage/main/ball_open_to_catch.json @@ -0,0 +1,41 @@ +{ + "kind": "oam-shape", + "version":1, + "pngsize": "3x6", + "pieces": [ + { + "pos":"0,0", + "size": "2x2" + }, + { + "pos":"2,0", + "size": "1x2" + }, + { + "pos":"0,2", + "size": "2x1" + }, + { + "pos":"2,2", + "size": "1x1" + "spacer":true + }, + { + "pos":"0,3", + "size": "2x2" + }, + { + "pos":"2,3", + "size": "1x2" + }, + { + "pos":"0,5", + "size": "1x1" + "spacer":true + }, + { + "pos":"1,5", + "size": "2x1" + } + ] +} \ No newline at end of file diff --git a/graphics/stage/main/ball_open_to_catch.png b/graphics/stage/main/ball_open_to_catch.png new file mode 100644 index 00000000..e6b08153 Binary files /dev/null and b/graphics/stage/main/ball_open_to_catch.png differ diff --git a/graphics/stage/main/main_stage_gfx.json b/graphics/stage/main/main_stage_gfx.json index d2f7813d..a24928aa 100644 --- a/graphics/stage/main/main_stage_gfx.json +++ b/graphics/stage/main/main_stage_gfx.json @@ -1,169 +1,173 @@ -{ - "kind": "gfx-config", - "version": 1, - "defaults": { - "oam": false, - "width": 16, - "palette": "" - }, - "files": [ - { - "gfx_filename": "gunk_084F61EC", - "mwidth":8, - "align":1 - }, - { - "gfx_filename": "gunk_0844AA0C", - "mwidth":6, - "mheight": 6, - "oam":true - }, - { - "gfx_filename": "pika_spinner", - "mwidth":3, - "mheight":3, - "width":12, - "oam":true - }, - { - "gfx_filename": "jirachi_banner", - "mwidth":30, - "mheight":9, - "oam":true - }, - { - "gfx_filename": "travel", - "segments":[ - { - "segfile": "travel_banner", - "mwidth":30, - "mheight":8, - "oam":true - }, - { - "segfile": "travel_bottom_border", - "width":5 - }, - { - "segfile": "travel_pole", - "width":4 - }, - { - "segfile": "travel_arrows", - "mwidth":3, - "mheight":4, - "oam":true - }, - { - "segfile": "travel_pole_base", - "width":2 - } - ] - }, - { - "gfx_filename": "evo_banner", - "mheight":9, - "mwidth":30, - "oam":true - }, - { - "gfx_filename": "game_over_text", - "mheight": 2, - "mwidth": 2, - "width":16 - }, - { - "gfx_filename": "pause_menu_text" - }, - { - "gfx_filename": "latios", - "mheight":8, - "mwidth":8 - }, - { - "gfx_filename": "latios_arm", - "width":2 - }, - { - "gfx_filename": "ball_save", - "segments": [ - { - "segfile":"ball_save_banner", - "mwidth":16, - "mheight":8, - "oam":true - }, - { - "segfile":"ball_save_latias", - "width":8, - "height":8 - }, - { - "segfile":"ball_save_latias_arm", - "width":2, - "height":3 - } - ] - }, - { - "gfx_filename": "end_of_ball", - "segments": [ - { - "segfile":"end_of_ball_left", - "mwidth":8, - "mheight":8 - }, - { - "segfile":"end_of_ball_top_corner", - "mwidth":2, - "mheight":2 - }, - { - "segfile":"end_of_ball_banner", - "mwidth":22, - "mheight":2, - "oam":true - }, - { - "segfile":"end_of_ball_right", - "mwidth":8, - "mheight":8 - }, - { - "segfile":"end_of_ball_space", - "mwidth":24, - "mheight":2, - "oam":true - } - ] - }, - { - "gfx_filename": "bonus_trap", - "mwidth":6, - "mheight":4, - "oam":true - }, - { - "gfx_filename": "pokeball_regular", - "mwidth":2 - }, - { - "gfx_filename": "pokeball_great", - "mwidth":2 - }, - { - "gfx_filename": "pokeball_ultra", - "mwidth":2 - }, - { - "gfx_filename": "pokeball_master", - "mwidth":2 - }, - { - "gfx_filename": "catch_mode_banner", - "oam": true, - "mheight": 9, - "mwidth": 30 - } - ] -} +{ + "kind": "gfx-config", + "version": 1, + "defaults": { + "oam": false, + "width": 16, + "palette": "" + }, + "files": [ + { + "gfx_filename": "gunk_084F61EC", + "mwidth":8, + "align":1 + }, + { + "gfx_filename": "gunk_0844AA0C", + "mwidth":6, + "mheight": 6, + "oam":true + }, + { + "gfx_filename": "pika_spinner", + "mwidth":3, + "mheight":3, + "width":12, + "oam":true + }, + { + "gfx_filename": "jirachi_banner", + "mwidth":30, + "mheight":9, + "oam":true + }, + { + "gfx_filename": "travel", + "segments":[ + { + "segfile": "travel_banner", + "mwidth":30, + "mheight":8, + "oam":true + }, + { + "segfile": "travel_bottom_border", + "width":5 + }, + { + "segfile": "travel_pole", + "width":4 + }, + { + "segfile": "travel_arrows", + "mwidth":3, + "mheight":4, + "oam":true + }, + { + "segfile": "travel_pole_base", + "width":2 + } + ] + }, + { + "gfx_filename": "evo_banner", + "mheight":9, + "mwidth":30, + "oam":true + }, + { + "gfx_filename": "game_over_text", + "mheight": 2, + "mwidth": 2, + "width":16 + }, + { + "gfx_filename": "pause_menu_text" + }, + { + "gfx_filename": "latios", + "mheight":8, + "mwidth":8 + }, + { + "gfx_filename": "latios_arm", + "width":2 + }, + { + "gfx_filename": "ball_save", + "segments": [ + { + "segfile":"ball_save_banner", + "mwidth":16, + "mheight":8, + "oam":true + }, + { + "segfile":"ball_save_latias", + "width":8, + "height":8 + }, + { + "segfile":"ball_save_latias_arm", + "width":2, + "height":3 + } + ] + }, + { + "gfx_filename": "end_of_ball", + "segments": [ + { + "segfile":"end_of_ball_left", + "mwidth":8, + "mheight":8 + }, + { + "segfile":"end_of_ball_top_corner", + "mwidth":2, + "mheight":2 + }, + { + "segfile":"end_of_ball_banner", + "mwidth":22, + "mheight":2, + "oam":true + }, + { + "segfile":"end_of_ball_right", + "mwidth":8, + "mheight":8 + }, + { + "segfile":"end_of_ball_space", + "mwidth":24, + "mheight":2, + "oam":true + } + ] + }, + { + "gfx_filename": "bonus_trap", + "mwidth":6, + "mheight":4, + "oam":true + }, + { + "gfx_filename": "pokeball_regular", + "mwidth":2 + }, + { + "gfx_filename": "pokeball_great", + "mwidth":2 + }, + { + "gfx_filename": "pokeball_ultra", + "mwidth":2 + }, + { + "gfx_filename": "pokeball_master", + "mwidth":2 + }, + { + "gfx_filename": "ball_open_to_catch", + "oamshape": "ball_open_to_catch.json" + }, + { + "gfx_filename": "catch_mode_banner", + "oam": true, + "mheight": 9, + "mwidth": 30 + } + ] +} diff --git a/graphics/stage/ruby/intro_sprite_linoone.png b/graphics/stage/ruby/intro_sprite_linoone.png new file mode 100644 index 00000000..1b27a777 Binary files /dev/null and b/graphics/stage/ruby/intro_sprite_linoone.png differ diff --git a/graphics/stage/ruby/intro_sprite_linoone1.png b/graphics/stage/ruby/intro_sprite_linoone1.png deleted file mode 100644 index 8ea43906..00000000 Binary files a/graphics/stage/ruby/intro_sprite_linoone1.png and /dev/null differ diff --git a/graphics/stage/ruby/intro_sprite_linoone1_arm.png b/graphics/stage/ruby/intro_sprite_linoone1_arm.png deleted file mode 100644 index 6c049ddd..00000000 Binary files a/graphics/stage/ruby/intro_sprite_linoone1_arm.png and /dev/null differ diff --git a/graphics/stage/ruby/intro_sprite_linoone2.png b/graphics/stage/ruby/intro_sprite_linoone2.png deleted file mode 100644 index 8ea43906..00000000 Binary files a/graphics/stage/ruby/intro_sprite_linoone2.png and /dev/null differ diff --git a/graphics/stage/ruby/intro_sprite_linoone2_arm.png b/graphics/stage/ruby/intro_sprite_linoone2_arm.png deleted file mode 100644 index 6c049ddd..00000000 Binary files a/graphics/stage/ruby/intro_sprite_linoone2_arm.png and /dev/null differ diff --git a/graphics/stage/ruby/intro_sprite_sharpedo.png b/graphics/stage/ruby/intro_sprite_sharpedo.png new file mode 100644 index 00000000..1c8d4102 Binary files /dev/null and b/graphics/stage/ruby/intro_sprite_sharpedo.png differ diff --git a/graphics/stage/ruby/intro_sprite_sharpedo_body.png b/graphics/stage/ruby/intro_sprite_sharpedo_body.png deleted file mode 100644 index 9d8d7cf4..00000000 Binary files a/graphics/stage/ruby/intro_sprite_sharpedo_body.png and /dev/null differ diff --git a/graphics/stage/ruby/intro_sprite_sharpedo_side_fin.png b/graphics/stage/ruby/intro_sprite_sharpedo_side_fin.png deleted file mode 100644 index 9f2a9c33..00000000 Binary files a/graphics/stage/ruby/intro_sprite_sharpedo_side_fin.png and /dev/null differ diff --git a/graphics/stage/ruby/intro_sprite_sharpedo_top_fin.png b/graphics/stage/ruby/intro_sprite_sharpedo_top_fin.png deleted file mode 100644 index e2973312..00000000 Binary files a/graphics/stage/ruby/intro_sprite_sharpedo_top_fin.png and /dev/null differ diff --git a/graphics/stage/ruby/linoone_shape.json b/graphics/stage/ruby/linoone_shape.json new file mode 100644 index 00000000..ec7b3785 --- /dev/null +++ b/graphics/stage/ruby/linoone_shape.json @@ -0,0 +1,27 @@ +{ + "kind": "oam-shape", + "version":1, + "pngsize": "3x3", + "pieces": [ + { + "_comment":"Body", + "pos":"0,0", + "size": "2x2", + }, + { + "_comment":"nose", + "pos":"2,0", + "size": "1x2" + }, + { + "_comment":"arm", + "pos":"0,2", + "size": "2x1" + }, + { + "pos":"2,2", + "size": "1x1", + "spacer":true + } + ] +} \ No newline at end of file diff --git a/graphics/stage/ruby/ruby_gfx.json b/graphics/stage/ruby/ruby_gfx.json index b9983a18..7cc348f4 100644 --- a/graphics/stage/ruby/ruby_gfx.json +++ b/graphics/stage/ruby/ruby_gfx.json @@ -18,8 +18,7 @@ }, { "gfx_filename": "sharpedo", - "mwidth":19, - "mheight":3 + "oamshape": "sharpedo_shape.json" }, { "gfx_filename": "shop", @@ -167,38 +166,12 @@ "width":4 }, { - "segfile":"intro_sprite_linoone1", - "oam":true, - "mwidth":3, - "mheight":2, - "width":3 - }, - { - "segfile":"intro_sprite_linoone1_arm", - "width":2 + "segfile": "intro_sprite_linoone", + "oamshape": "linoone_shape.json" }, { - "segfile":"intro_sprite_linoone2", - "oam":true, - "mwidth":3, - "mheight":2, - "width":3 - }, - { - "segfile":"intro_sprite_linoone2_arm", - "width":2 - }, - { - "segfile":"intro_sprite_sharpedo_top_fin", - "width":1 - }, - { - "segfile":"intro_sprite_sharpedo_body", - "width":4 - }, - { - "segfile":"intro_sprite_sharpedo_side_fin", - "width":1 + "segfile": "intro_sprite_sharpedo", + "oamshape": "sharpedo_shape.json" }, { "segfile":"intro_sprite_chinchou", diff --git a/graphics/stage/ruby/sharpedo.png b/graphics/stage/ruby/sharpedo.png index f626f07d..6619b25e 100644 Binary files a/graphics/stage/ruby/sharpedo.png and b/graphics/stage/ruby/sharpedo.png differ diff --git a/graphics/stage/ruby/sharpedo_shape.json b/graphics/stage/ruby/sharpedo_shape.json new file mode 100644 index 00000000..040720d0 --- /dev/null +++ b/graphics/stage/ruby/sharpedo_shape.json @@ -0,0 +1,42 @@ +{ + "kind": "oam-shape", + "version":1, + "pngsize": "5x6", + "pieces": [ + { + "pos":"0,0", + "size": "1x2", + "spacer":true + }, + { + "_comment":"Fin", + "pos":"1,0", + "size": "1x2" + }, + { + "pos":"2,0", + "size": "3x2", + "spacer":true + }, + { + "_comment":"body", + "pos":"0,2", + "size": "4x4" + }, + { + "pos":"4,2", + "size": "1x1", + "spacer":true + }, + { + "_comment":"flipper", + "pos":"4,3", + "size": "1x1" + }, + { + "pos":"4,4", + "size": "1x2", + "spacer":true + } + ] +} \ No newline at end of file diff --git a/src/board_process3_groudon.c b/src/board_process3_groudon.c index 94c9097f..668f242f 100644 --- a/src/board_process3_groudon.c +++ b/src/board_process3_groudon.c @@ -7,7 +7,7 @@ extern const u8 gGroudonBonusClear_Gfx[]; extern const u8 gGroudonLavaPaletteCycleData[]; extern const u8 gGroudonBoardBackgroundGfx[]; -extern const s8 gGroudonBoulderSpriteFrames[][0x300]; +extern const s8 gGroudonBoardBoulders_Gfx[][0x300]; extern struct SongHeader se_unk_118; extern struct SongHeader se_unk_11b; extern struct SongHeader se_unk_11c; @@ -1332,7 +1332,7 @@ void UpdateGroudonFieldEntities(void) } var0 = gCurrentPinballGame->boulderSpriteFrame[i]; - DmaCopy16(3, gGroudonBoulderSpriteFrames[var0], (void *)0x06010FA0 + i * 0x300, 0x300); + DmaCopy16(3, gGroudonBoardBoulders_Gfx[var0], (void *)0x06010FA0 + i * 0x300, 0x300); group->baseX = (gCurrentPinballGame->boulderGroundPosition[i].x / 10) + i - gCurrentPinballGame->cameraXOffset; group->baseY = (gCurrentPinballGame->boulderFallHeight[i] / 10) + (gCurrentPinballGame->boulderGroundPosition[i].y / 10) - gCurrentPinballGame->cameraYOffset; diff --git a/tools/gbagfx/gfx.c b/tools/gbagfx/gfx.c index 127bbe94..c5d4f766 100755 --- a/tools/gbagfx/gfx.c +++ b/tools/gbagfx/gfx.c @@ -80,7 +80,7 @@ static void ConvertFromTiles1Bpp(unsigned char *src, unsigned char *dest, int nu } } -static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors, bool oamMap) +static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors, bool oamMap, int tilesHeight, OamSequence *oamSeq) { int subTileX = 0; int subTileY = 0; @@ -90,10 +90,21 @@ static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int nu TileCoord tileMappingCoord[MAX_OAM_TILE_SIDE_LENGTH_SQUARED]; int tileCount = 0; + struct OamSegment *oamSegments = NULL; if (oamMap) { - tileCount = getOamTileIndex(metatileWidth, metatileHeight, tileMappingCoord); + if (oamSeq) + tileCount = getOamTileIndexEx(metatileWidth, metatileHeight, tileMappingCoord, oamSeq->segments, oamSeq->numSegments, &oamSegments); + else + tileCount = getOamTileIndexEx(metatileWidth, metatileHeight, tileMappingCoord, NULL, 0, &oamSegments); + } + + /* If using OAM mapping, start placement at the first mapped coordinate so + the first data tile goes to the first non-spacer slot instead of (0,0). */ + if (oamMap && tileCount > 0) { + subTileX = tileMappingCoord[0].x; + subTileY = tileMappingCoord[0].y; } for (int i = 0; i < numTiles; i++) { @@ -121,6 +132,44 @@ static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int nu AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight); } } + + // If there are spacer segments, fill those tile regions across all metatiles with a 2x2 checkerboard + if (oamMap && oamSegments) { + int metatilesHigh = tilesHeight / metatileHeight; + + // For each spacer segment, repeat across every metatile and fill its tiles + struct OamSegment *seg = oamSegments; + while (seg) { + if (seg->spacer) { + for (int my = 0; my < metatilesHigh; ++my) { + for (int mx = 0; mx < metatilesWide; ++mx) { + for (int ty = 0; ty < seg->height; ++ty) { + for (int tx = 0; tx < seg->width; ++tx) { + int baseTileX = mx * metatileWidth + seg->offX + tx; + int baseTileY = my * metatileHeight + seg->offY + ty; + for (int j = 0; j < 8; j++) { + int destY = (baseTileY) * 8 + j; + for (int k = 0; k < 4; k++) { + int destX = baseTileX * 4 + k; + // compute two pixels per byte: left at (px = k*2), right at px+1 + int px0 = k * 2; + int py = j; + unsigned char left = (((px0 / 2) + (py / 2)) & 1) ? 0xF : 0x0; + unsigned char right = ((((px0+1) / 2) + (py / 2)) & 1) ? 0xF : 0x0; + dest[destY * ((metatilesWide * metatileWidth) * 4) + destX] = (left << 4) | right; + } + } + } + } + } + } + } + seg = seg->next; + } + + free_oam_segments(oamSegments); + oamSegments = NULL; + } } static void ConvertFromTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors) @@ -211,47 +260,105 @@ void Convert4BppImageWithPaletteMap(struct Image *image) image->bitDepth = 8; } -static void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors, bool oamMap) +static void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, int tilesHeight, bool invertColors, bool oamMap, OamSequence *oamSeq) { int subTileX = 0; int subTileY = 0; int metatileX = 0; int metatileY = 0; int pitch = (metatilesWide * metatileWidth) * 4; - - TileCoord tileMappingCoord[MAX_OAM_TILE_SIDE_LENGTH_SQUARED]; - int tileCount = 0; - - if (oamMap) - { - tileCount = getOamTileIndex(metatileWidth, metatileHeight, tileMappingCoord); - } - - for (int i = 0; i < numTiles; i++) { - for (int j = 0; j < 8; j++) { - int srcY = (metatileY * metatileHeight + subTileY) * 8 + j; - - for (int k = 0; k < 4; k++) { - int srcX = (metatileX * metatileWidth + subTileX) * 4 + k; - unsigned char srcPixelPair = src[srcY * pitch + srcX]; - unsigned char leftPixel = srcPixelPair >> 4; - unsigned char rightPixel = srcPixelPair & 0xF; - - if (invertColors) { - leftPixel = 15 - leftPixel; - rightPixel = 15 - rightPixel; + (void)tilesHeight; // may be unused in some builds + + if (!oamMap) { + // Fallback to standard ordering + for (int i = 0; i < numTiles; i++) { + for (int j = 0; j < 8; j++) { + int srcY = (metatileY * metatileHeight + subTileY) * 8 + j; + + for (int k = 0; k < 4; k++) { + int srcX = (metatileX * metatileWidth + subTileX) * 4 + k; + unsigned char srcPixelPair = src[srcY * pitch + srcX]; + unsigned char leftPixel = srcPixelPair >> 4; + unsigned char rightPixel = srcPixelPair & 0xF; + + if (invertColors) { + leftPixel = 15 - leftPixel; + rightPixel = 15 - rightPixel; + } + + *dest++ = (rightPixel << 4) | leftPixel; } + } - *dest++ = (rightPixel << 4) | leftPixel; + AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight); + } + } else { + // Build full ordering across all metatiles using the segment sequence + struct OamSegment *segments = NULL; + TileCoord dummy[MAX_OAM_TILE_SIDE_LENGTH_SQUARED]; + // getOamTileIndexEx returns the compressed mapping for one metatile and the segments list + if (oamSeq) + getOamTileIndexEx(metatileWidth, metatileHeight, dummy, oamSeq->segments, oamSeq->numSegments, &segments); + else + getOamTileIndexEx(metatileWidth, metatileHeight, dummy, NULL, 0, &segments); + + int metatilesHigh = tilesHeight / metatileHeight; + int totalTiles = numTiles; + + TileCoord *fullOrder = malloc(sizeof(TileCoord) * totalTiles); + bool *fullSpacer = malloc(sizeof(bool) * totalTiles); + int idx = 0; + for (int my = 0; my < metatilesHigh; ++my) { + for (int mx = 0; mx < metatilesWide; ++mx) { + struct OamSegment *seg = segments; + while (seg) { + for (int ty = 0; ty < seg->height; ++ty) { + for (int tx = 0; tx < seg->width; ++tx) { + fullOrder[idx].x = mx * metatileWidth + seg->offX + tx; + fullOrder[idx].y = my * metatileHeight + seg->offY + ty; + fullSpacer[idx] = seg->spacer; + ++idx; + } + } + seg = seg->next; + } } } - if (oamMap) { - AdvanceOamMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, tileMappingCoord, tileCount, i); - } else { - AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight); + // write out tiles in full order, but skip spacer slots when writing + // so the output tile stream only contains actual data tiles. Leave the + // remainder of the destination buffer zeroed so the caller can detect + // trailing zero padding. + unsigned char *destPtr = dest; + for (int p = 0; p < totalTiles; ++p) { + if (fullSpacer[p]) continue; + + int baseTileX = fullOrder[p].x; + int baseTileY = fullOrder[p].y; + for (int j = 0; j < 8; j++) { + int srcY = baseTileY * 8 + j; + for (int k = 0; k < 4; k++) { + int srcX = baseTileX * 4 + k; + unsigned char srcPixelPair = src[srcY * pitch + srcX]; + unsigned char leftPixel = srcPixelPair >> 4; + unsigned char rightPixel = srcPixelPair & 0xF; + + if (invertColors) { + leftPixel = 15 - leftPixel; + rightPixel = 15 - rightPixel; + } + + *destPtr++ = (rightPixel << 4) | leftPixel; + } + } } + + free(fullOrder); + free(fullSpacer); + free_oam_segments(segments); } + + return; } static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors) @@ -434,7 +541,7 @@ static unsigned char *DecodeTilemap(unsigned char *tiles, struct Tilemap *tilema return decoded; } -void ReadTileImage(char *path, int tilesWidth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool oamMap) +void ReadTileImage(char *path, int tilesWidth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool oamMap, char *oamSequenceFilePath) { int tileSize = image->bitDepth * 8; @@ -453,13 +560,45 @@ void ReadTileImage(char *path, int tilesWidth, int metatileWidth, int metatileHe } } + // load custom oam sequence if provided (needed for validation) + OamSequence *oamSeq = NULL; + if (oamSequenceFilePath != NULL) { + int cnt = oam_sequence_load_from_file(oamSequenceFilePath, &oamSeq); + if (cnt < 0) + FATAL_ERROR("Failed to read oam sequence file: %s\n", oamSequenceFilePath); + metatileHeight = oamSeq->png_height; + metatileWidth = oamSeq->png_width; + + //Allow passing this in; default it otherwise + if (tilesWidth == 1){ + tilesWidth = metatileWidth; + } + } + int tilesHeight = (numTiles + tilesWidth - 1) / tilesWidth; - if (tilesWidth % metatileWidth != 0) - FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)\n", tilesWidth, metatileWidth); + // If an OAM shape is provided, validate by per-metatile tilecount + if (oamSeq && oamSeq->tilecount > 0) { + if (numTiles % oamSeq->tilecount != 0) + FATAL_ERROR("The number of tiles (%d) is not a multiple of the OAM shape tile count (%d)\n", numTiles, oamSeq->tilecount); - if (tilesHeight % metatileHeight != 0) - FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)\n", tilesHeight, metatileHeight); + if (tilesWidth % metatileWidth != 0) + FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)\n", tilesWidth, metatileWidth); + + int totalMetatiles = numTiles / oamSeq->tilecount; + int metatilesWide = tilesWidth / metatileWidth; + if (totalMetatiles % metatilesWide != 0) + FATAL_ERROR("Tile data contains %d metatiles which is not divisible by metatiles-wide (%d)\n", totalMetatiles, metatilesWide); + + int metatilesHigh = totalMetatiles / metatilesWide; + tilesHeight = metatilesHigh * metatileHeight; + } else { + if (tilesWidth % metatileWidth != 0) + FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)\n", tilesWidth, metatileWidth); + + if (tilesHeight % metatileHeight != 0) + FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)\n", tilesHeight, metatileHeight); + } image->width = tilesWidth * 8; image->height = tilesHeight * 8; @@ -475,7 +614,7 @@ void ReadTileImage(char *path, int tilesWidth, int metatileWidth, int metatileHe ConvertFromTiles1Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors); break; case 4: - ConvertFromTiles4Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors, oamMap); + ConvertFromTiles4Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors, oamMap, tilesHeight, oamSeq); break; case 8: ConvertFromTiles8Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors); @@ -483,9 +622,10 @@ void ReadTileImage(char *path, int tilesWidth, int metatileWidth, int metatileHe } free(buffer); + if (oamSeq) oam_sequence_free(oamSeq); } -void WriteTileImage(char *path, enum NumTilesMode numTilesMode, int numTiles, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool oamMap) +void WriteTileImage(char *path, enum NumTilesMode numTilesMode, int numTiles, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool oamMap, char *oamSequenceFilePath) { int tileSize = image->bitDepth * 8; @@ -498,13 +638,48 @@ void WriteTileImage(char *path, enum NumTilesMode numTilesMode, int numTiles, in int tilesWidth = image->width / 8; int tilesHeight = image->height / 8; - if (tilesWidth % metatileWidth != 0) - FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)\n", tilesWidth, metatileWidth); - - if (tilesHeight % metatileHeight != 0) - FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)\n", tilesHeight, metatileHeight); + // load sequence if provided (so we can validate using per-metatile tilecount) + OamSequence *oamSeq = NULL; + if (oamSequenceFilePath != NULL) { + int cnt = oam_sequence_load_from_file(oamSequenceFilePath, &oamSeq); + if (cnt < 0) + FATAL_ERROR("Failed to read oam sequence file: %s\n", oamSequenceFilePath); + metatileHeight = oamSeq->png_height; + metatileWidth = oamSeq->png_width; + } int maxNumTiles = tilesWidth * tilesHeight; + if (oamSeq && oamSeq->png_tiles > 0) { + if (maxNumTiles % oamSeq->png_tiles != 0) + FATAL_ERROR("The image contains %d tiles which is not a multiple of the OAM shape pngsize (%d)\n", maxNumTiles, oamSeq->png_tiles); + + if (tilesWidth % metatileWidth != 0) + FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)\n", tilesWidth, metatileWidth); + + int totalMetatiles = maxNumTiles / oamSeq->png_tiles; + int metatilesWide = tilesWidth / metatileWidth; + if (totalMetatiles % metatilesWide != 0) + FATAL_ERROR("Image supplies %d metatiles which is not divisible by metatiles-wide (%d)\n", totalMetatiles, metatilesWide); + } else if (oamSeq && oamSeq->tilecount > 0) { + if (maxNumTiles % oamSeq->tilecount != 0) + FATAL_ERROR("The image contains %d tiles which is not a multiple of the OAM shape tile count (%d)\n", maxNumTiles, oamSeq->tilecount); + + if (tilesWidth % metatileWidth != 0) + FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)\n", tilesWidth, metatileWidth); + + int totalMetatiles = maxNumTiles / oamSeq->tilecount; + int metatilesWide = tilesWidth / metatileWidth; + if (totalMetatiles % metatilesWide != 0) + FATAL_ERROR("Image supplies %d metatiles which is not divisible by metatiles-wide (%d)\n", totalMetatiles, metatilesWide); + } else { + if (tilesWidth % metatileWidth != 0) + FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)\n", tilesWidth, metatileWidth); + + if (tilesHeight % metatileHeight != 0) + FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)\n", tilesHeight, metatileHeight); + } + + if (numTiles == 0) numTiles = maxNumTiles; @@ -513,6 +688,13 @@ void WriteTileImage(char *path, enum NumTilesMode numTilesMode, int numTiles, in int bufferSize = numTiles * tileSize; int maxBufferSize = maxNumTiles * tileSize; + + // Caused by differences in the input and output size, due to 'spacer' tiles. + if (oamSeq && oamSeq->png_tiles > 0 && oamSeq->tilecount > 0) { + bufferSize = numTiles * tileSize / oamSeq->png_tiles * oamSeq->tilecount ; + maxBufferSize = maxBufferSize / oamSeq->png_tiles * oamSeq->tilecount ; + } + unsigned char *buffer = malloc(maxBufferSize); if (buffer == NULL) @@ -520,12 +702,12 @@ void WriteTileImage(char *path, enum NumTilesMode numTilesMode, int numTiles, in int metatilesWide = tilesWidth / metatileWidth; - switch (image->bitDepth) { + switch (image->bitDepth) { case 1: ConvertToTiles1Bpp(image->pixels, buffer, maxNumTiles, metatilesWide, metatileWidth, metatileHeight, invertColors); break; case 4: - ConvertToTiles4Bpp(image->pixels, buffer, maxNumTiles, metatilesWide, metatileWidth, metatileHeight, invertColors, oamMap); + ConvertToTiles4Bpp(image->pixels, buffer, maxNumTiles, metatilesWide, metatileWidth, metatileHeight, tilesHeight, invertColors, oamMap, oamSeq); break; case 8: ConvertToTiles8Bpp(image->pixels, buffer, maxNumTiles, metatilesWide, metatileWidth, metatileHeight, invertColors); @@ -554,6 +736,8 @@ void WriteTileImage(char *path, enum NumTilesMode numTilesMode, int numTiles, in WriteWholeFile(path, buffer, zeroPadded ? bufferSize : maxBufferSize); free(buffer); + + if (oamSeq) oam_sequence_free(oamSeq); } void ReadPlainImage(char *path, int dataWidth, struct Image *image, bool invertColors) diff --git a/tools/gbagfx/gfx.h b/tools/gbagfx/gfx.h index 358532d0..5d88d1e3 100755 --- a/tools/gbagfx/gfx.h +++ b/tools/gbagfx/gfx.h @@ -60,8 +60,8 @@ typedef struct { #define MAX_OAM_TILE_SIDE_LENGTH 32 #define MAX_OAM_TILE_SIDE_LENGTH_SQUARED MAX_OAM_TILE_SIDE_LENGTH * MAX_OAM_TILE_SIDE_LENGTH -void ReadTileImage(char *path, int tilesWidth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool optomized_2n_map); -void WriteTileImage(char *path, enum NumTilesMode numTilesMode, int numTiles, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool optomized_2n_map); +void ReadTileImage(char *path, int tilesWidth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool optomized_2n_map, char *oamSequenceFilePath); +void WriteTileImage(char *path, enum NumTilesMode numTilesMode, int numTiles, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool optomized_2n_map, char *oamSequenceFilePath); void ReadPlainImage(char *path, int dataWidth, struct Image *image, bool invertColors); void WritePlainImage(char *path, int dataWidth, struct Image *image, bool invertColors); void FreeImage(struct Image *image); diff --git a/tools/gbagfx/main.c b/tools/gbagfx/main.c index 258cfbff..d0d0ccca 100755 --- a/tools/gbagfx/main.c +++ b/tools/gbagfx/main.c @@ -68,7 +68,7 @@ void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions * image.tilemap.size = fileSize; } - ReadTileImage(inputPath, options->width, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette, options->oamSprite); + ReadTileImage(inputPath, options->width, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette, options->oamSprite, options->oamSequenceFilePath); if (image.paletteMap != NULL && image.bitDepth == 4) { Convert4BppImageWithPaletteMap(&image); @@ -97,7 +97,7 @@ void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions * ReadPng(inputPath, &image); if (options->isTiled) - WriteTileImage(outputPath, options->numTilesMode, options->numTiles, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette, options->oamSprite); + WriteTileImage(outputPath, options->numTilesMode, options->numTiles, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette, options->oamSprite, options->oamSequenceFilePath); else WritePlainImage(outputPath, options->dataWidth, &image, !image.hasPalette); @@ -120,6 +120,7 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a options.isTiled = true; options.dataWidth = 1; options.oamSprite = false; + options.oamSequenceFilePath = NULL; for (int i = 3; i < argc; i++) { @@ -189,6 +190,14 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a else if (strcmp(option, "-oam") == 0) { options.oamSprite = 1; } + else if (strcmp(option, "-oamshape") == 0) { + if (i + 1 >= argc) + FATAL_ERROR("No oam shape file path following \"-oam-shape\".\n"); + + i++; + options.oamSprite = 1; + options.oamSequenceFilePath = argv[i]; + } else if (strcmp(option, "-tilemap") == 0) { if (i + 1 >= argc) @@ -222,7 +231,7 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a } } - if (options.oamSprite){ + if (options.oamSprite && options.oamSequenceFilePath == NULL){ if (options.metatileHeight == 1 && options.metatileWidth == 1){ FATAL_ERROR("Must specify metatile dimensions when using oam chunk mapping.\n"); } @@ -248,6 +257,7 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a options.isTiled = true; options.dataWidth = 1; options.oamSprite = false; + options.oamSequenceFilePath = NULL; for (int i = 3; i < argc; i++) { @@ -301,6 +311,14 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a else if (strcmp(option, "-oam") == 0) { options.oamSprite = 1; } + else if (strcmp(option, "-oamshape") == 0) { + if (i + 1 >= argc) + FATAL_ERROR("No oam sequence file path following \"-oamshape\".\n"); + + i++; + options.oamSprite = 1; + options.oamSequenceFilePath = argv[i]; + } else if (strcmp(option, "-plain") == 0) { options.isTiled = false; @@ -323,7 +341,7 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a } } - if (options.oamSprite){ + if (options.oamSprite && options.oamSequenceFilePath == NULL){ if (options.metatileHeight == 1 && options.metatileWidth == 1){ FATAL_ERROR("Must specify metatile dimensions when using oam mapping.\n"); } diff --git a/tools/gbagfx/oam_slices/oam_slicer.c b/tools/gbagfx/oam_slices/oam_slicer.c index c1fed5f5..19ce6c63 100644 --- a/tools/gbagfx/oam_slices/oam_slicer.c +++ b/tools/gbagfx/oam_slices/oam_slicer.c @@ -1,10 +1,175 @@ #include #include #include +#include #include "../gfx.h" #include "oam_slicer.h" +#include "../util.h" +// Load oam sequence file. +// JSON matching the schema provided by the user. +// Returns number of segments, 0 if none, or -1 on IO/error. +int oam_sequence_load_from_file(const char *path, OamSequence **outSeq) { + if (path == NULL) return 0; + int fileSize; + unsigned char *buf = ReadWholeFile((char*)path, &fileSize); + if (buf == NULL) return -1; + + // skip leading whitespace + char *p = (char*)buf; + char *end = (char*)buf + fileSize; + while (p < end && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) ++p; + + // If JSON (starts with '{'), parse JSON-ish format for our schema + if (p < end && *p == '{') { + int tilecount = -1; + + // find "tilecount" + char *tok = strstr(p, "\"tilecount\""); + if (tok) { + char *col = strchr(tok, ':'); + if (col) tilecount = atoi(col + 1); + } + + // find "pngsize" + tok = strstr(p, "\"pngsize\""); + int png_w = -1, png_h = -1; + int png_tiles = 0; + if (tok) { + char *col = strchr(tok, ':'); + if (col) { + char *quote1 = strchr(col, '"'); + if (quote1) { + char *quote2 = strchr(quote1 + 1, '"'); + if (quote2) { + int a, b; + if (sscanf(quote1 + 1, "%d x %d", &a, &b) == 2 || sscanf(quote1 + 1, "%dx%d", &a, &b) == 2) { + png_w = a; + png_h = b; + png_tiles = png_w * png_h; + } + } + } + } + } + + // find pieces array + tok = strstr(p, "\"pieces\""); + if (!tok) { + free(buf); + *outSeq = NULL; + return -1; + } + + char *arrstart = strchr(tok, '['); + if (!arrstart) { free(buf); *outSeq = NULL; return -1; } + + int maxSegments = 0; + for (char *q = arrstart; q < end; ++q) if (*q == '{') ++maxSegments; + if (maxSegments == 0) { free(buf); *outSeq = NULL; return 0; } + + struct OamOverrideSegment *arr = malloc(sizeof(struct OamOverrideSegment) * maxSegments); + int idx = 0; + + char *q = arrstart; + while (q < end) { + char *obj = strchr(q, '{'); + if (!obj) break; + char *objend = strchr(obj, '}'); + if (!objend) break; + + int offX = 0, offY = 0, w = 0, h = 0; + int spacer = 0; + char *posTok = strstr(obj, "\"pos\""); + if (posTok && posTok < objend) { + char *col = strchr(posTok, ':'); + if (col) { + char *q1 = strchr(col, '"'); + if (q1 && q1 < objend) { + char *q2 = strchr(q1 + 1, '"'); + if (q2 && q2 < objend) { + sscanf(q1 + 1, "%d,%d", &offX, &offY); + } + } + } + } + + char *sizeTok = strstr(obj, "\"size\""); + if (sizeTok && sizeTok < objend) { + char *col = strchr(sizeTok, ':'); + if (col) { + char *q1 = strchr(col, '"'); + if (q1 && q1 < objend) { + char *q2 = strchr(q1 + 1, '"'); + if (q2 && q2 < objend) { + if (sscanf(q1 + 1, "%d x %d", &w, &h) != 2) { + sscanf(q1 + 1, "%dx%d", &w, &h); + } + } + } + } + } + + char *spTok = strstr(obj, "\"spacer\""); + if (spTok && spTok < objend) { + char *col = strchr(spTok, ':'); + if (col) { + if (strstr(col, "true") != NULL) spacer = 1; + else spacer = atoi(col + 1); + } + } + + if (!spacer) { + if (!is_valid_oam_shape(w, h)) { + free(arr); + free(buf); + *outSeq = NULL; + return -1; + } + } + + arr[idx].offX = (unsigned short)offX; + arr[idx].offY = (unsigned short)offY; + arr[idx].width = (unsigned short)w; + arr[idx].height = (unsigned short)h; + arr[idx].spacer = spacer ? true : false; + ++idx; + + q = objend + 1; + } + + if (tilecount < 0) { + int sum = 0; + for (int i = 0; i < idx; ++i) if (!arr[i].spacer) sum += arr[i].width * arr[i].height; + tilecount = sum; + } + + OamSequence *seq = malloc(sizeof(OamSequence)); + seq->segments = arr; + seq->numSegments = idx; + seq->tilecount = tilecount; + seq->png_tiles = png_tiles; + seq->png_width = png_w; + seq->png_height = png_h; + + free(buf); + if (idx == 0) { free(arr); free(seq); *outSeq = NULL; return 0; } + *outSeq = seq; + return idx; + } + + free(buf); + *outSeq = NULL; + return -1; +} + +void oam_sequence_free(OamSequence *seq) { + if (!seq) return; + if (seq->segments) free(seq->segments); + free(seq); +} + // Define the available OAM shapes. // Ordered by the width, then height. const struct OamShape oamShapes[] = { @@ -14,14 +179,22 @@ const struct OamShape oamShapes[] = { {1, 4, OAM_SIZE_1, OAM_V_RECTANGLE}, {1, 2, OAM_SIZE_0, OAM_V_RECTANGLE}, {1, 1, OAM_SIZE_0, OAM_SQUARE}, }; -// Define manual overrides for specific sprite dimentions, which were observed using a different sequencing -// than the standard algorithm would produce. +bool is_valid_oam_shape(int width, int height) { + for (int i = 0; i < sizeof(oamShapes)/sizeof(oamShapes[0]); ++i) { + if (oamShapes[i].width == width && oamShapes[i].height == height) + return true; + } + return false; +} + +// Define manual overrides for specific sprite dimentions, which were observed using a different sequencing +// than the standard algorithm would produce. const struct OamOverride oamShapeOverrides[] = { { 13, 8, 5, (const struct OamOverrideSegment[]) { //2 8x4 segments, rather than a single 8x8 - {0, 0, 8, 4}, {0, 4, 8, 4}, {8, 0, 4, 8}, {12, 0, 1, 4}, {12, 4, 1, 4} + {0, 0, 8, 4, false}, {0, 4, 8, 4, false}, {8, 0, 4, 8, false}, {12, 0, 1, 4, false}, {12, 4, 1, 4, false} }}, { 6, 3, 4, (const struct OamOverrideSegment[]) { //ordering of middle 2 segments - {0,0,4,2}, {0,2,4,1}, {4,0,2,2}, {4,2,2,1} + {0,0,4,2,false}, {0,2,4,1,false}, {4,0,2,2,false}, {4,2,2,1,false} }}, }; @@ -30,13 +203,14 @@ static bool check_oam_shape_fits(int x, int y, int width, int height, int shapeW return (x + shapeW <= width && y + shapeH <= height); } -// Helper to allocate new segment -static struct OamSegment *new_oam_segment(int x, int y, int w, int h) { +// Helper to allocate new segment (spacer marks a region that has no tile data) +static struct OamSegment *new_oam_segment(int x, int y, int w, int h, bool spacer) { struct OamSegment *seg = malloc(sizeof(struct OamSegment)); seg->offX = x; seg->offY = y; seg->width = w; seg->height = h; + seg->spacer = spacer; seg->next = NULL; return seg; } @@ -48,7 +222,7 @@ static struct OamSegment *check_oam_manual_override(int width, int height) { struct OamSegment *head = NULL, **tail = &head; for (int j = 0; j < ovr->numSegments; ++j) { const struct OamOverrideSegment *seg = &ovr->segments[j]; - *tail = new_oam_segment(seg->offX, seg->offY, seg->width, seg->height); + *tail = new_oam_segment(seg->offX, seg->offY, seg->width, seg->height, seg->spacer); tail = &((*tail)->next); } return head; @@ -90,7 +264,7 @@ static OamSegment *slice_sprite_to_oam(int width, int height) { used[y + dy][x + dx] = true; // Add segment - *tail = new_oam_segment(x, y, shape.width, shape.height); + *tail = new_oam_segment(x, y, shape.width, shape.height, false); tail = &((*tail)->next); break; } @@ -102,6 +276,48 @@ static OamSegment *slice_sprite_to_oam(int width, int height) { return head; } +int getOamTileIndexEx(int width, int height, TileCoord *tileCoordinates, const struct OamOverrideSegment *sequence, int numSequenceSegments, struct OamSegment **outSegments) { + int tileIndex = 0; + + struct OamSegment *segments = NULL; + + if (sequence != NULL && numSequenceSegments > 0) { + struct OamSegment *head = NULL, **tail = &head; + for (int j = 0; j < numSequenceSegments; ++j) { + const struct OamOverrideSegment *seg = &sequence[j]; + *tail = new_oam_segment(seg->offX, seg->offY, seg->width, seg->height, seg->spacer); + tail = &((*tail)->next); + } + segments = head; + } else { + segments = check_oam_manual_override(width, height); + if (segments == NULL) { + segments = slice_sprite_to_oam(width, height); + } + } + + struct OamSegment *segment = segments; + while (segment) { + if (!segment->spacer) { + for (int y = 0; y < segment->height; ++y) { + for (int x = 0; x < segment->width; ++x) { + tileCoordinates[tileIndex].x = segment->offX + x; + tileCoordinates[tileIndex].y = segment->offY + y; + ++tileIndex; + } + } + } + segment = segment->next; + } + + if (outSegments) + *outSegments = segments; + else + free_oam_segments(segments); + + return tileIndex; +} + void print_oam_segments(struct OamSegment *segList) { printf("OAM Segments: "); while (segList) { @@ -113,29 +329,8 @@ void print_oam_segments(struct OamSegment *segList) { } int getOamTileIndex(int width, int height, TileCoord *tileCoordinates) { - int tileIndex = 0; - int tileCount = width * height; - - struct OamSegment *segments; - segments = check_oam_manual_override(width, height); - if (segments == NULL) { - segments = slice_sprite_to_oam(width, height); - } - - struct OamSegment *segment = segments; - while (segment) { - for (int y = 0; y < segment->height; ++y) { - for (int x = 0; x < segment->width; ++x) { - tileCoordinates[tileIndex].x = segment->offX + x; - tileCoordinates[tileIndex].y = segment->offY + y; - ++tileIndex; - } - } - segment = segment->next; - } - - free_oam_segments(segments); - return tileCount; + // legacy wrapper: no custom sequence provided + return getOamTileIndexEx(width, height, tileCoordinates, NULL, 0, NULL); } void free_oam_segments(struct OamSegment *segList) { diff --git a/tools/gbagfx/oam_slices/oam_slicer.h b/tools/gbagfx/oam_slices/oam_slicer.h index a173cb30..42fd6369 100644 --- a/tools/gbagfx/oam_slices/oam_slicer.h +++ b/tools/gbagfx/oam_slices/oam_slicer.h @@ -1,6 +1,7 @@ #ifndef GUARD_OAM_SLICER_H #define GUARD_OAM_SLICER_H #include "../gfx.h" +#include enum OamShapeEnum { OAM_SQUARE = 0, @@ -27,6 +28,7 @@ typedef struct OamOverrideSegment { unsigned short offY; unsigned short width; unsigned short height; + bool spacer; } OamOverrideSegment; typedef struct OamOverride { @@ -42,11 +44,39 @@ typedef struct OamSegment { short offY; unsigned short width; // in tiles unsigned short height; // in tiles + bool spacer; // indicates this segment represents a spacer (no tile data) } OamSegment; +// Represents a parsed OAM sequence file. Contains an array of override +// segments (as declared above), the number of segments, and metadata +// parsed from the file (tilecount and pngsize in tiles). +typedef struct OamSequence { + struct OamOverrideSegment *segments; + int numSegments; + int tilecount; // sum of non-spacer tiles (or -1 if not provided) + int png_tiles; // pngsize in tiles (w*h) if provided, 0 otherwise + int png_height; + int png_width; +} OamSequence; + +// Load an OAM sequence from a JSON file. Returns number of segments (>0), +// 0 if none, or -1 on IO/error. On success *outSeq will be a heap-allocated +// OamSequence that must be freed with oam_sequence_free(). +int oam_sequence_load_from_file(const char *path, OamSequence **outSeq); +void oam_sequence_free(OamSequence *seq); + void print_oam_segments(struct OamSegment *segList); void free_oam_segments(struct OamSegment *segList); int getOamTileIndex(int width, int height, TileCoord *tileCoordinates); +// Extended variant: provide an explicit sequence of segments (optional). +// If sequence==NULL, behaves like getOamTileIndex. +// Extended variant: fills `tileCoordinates` with the full ordering (including spacer positions). +// If `sequence==NULL` behaves like getOamTileIndex. If `outSpacerMask` is non-NULL it will be filled +// with `width*height` boolean entries marking which positions are spacers. +int getOamTileIndexEx(int width, int height, TileCoord *tileCoordinates, const OamOverrideSegment *sequence, int numSequenceSegments, struct OamSegment **outSegments); + +// Validate whether a WxH tile piece is a valid OAM shape +bool is_valid_oam_shape(int width, int height); void PrintTileCoords(TileCoord *tileSequence, int tile_count); #endif // GUARD_OAM_SLICER_H \ No newline at end of file diff --git a/tools/gbagfx/options.h b/tools/gbagfx/options.h index 66b86f27..1b6a0d6b 100755 --- a/tools/gbagfx/options.h +++ b/tools/gbagfx/options.h @@ -20,6 +20,7 @@ struct GbaToPngOptions { bool isTiled; int dataWidth; bool oamSprite; + char *oamSequenceFilePath; }; struct PngToGbaOptions { @@ -33,6 +34,7 @@ struct PngToGbaOptions { bool isTiled; int dataWidth; bool oamSprite; + char *oamSequenceFilePath; }; #endif // OPTIONS_H diff --git a/tools/scripts/generate_graphics_rules.sh b/tools/scripts/generate_graphics_rules.sh index 054e372c..9840528e 100755 --- a/tools/scripts/generate_graphics_rules.sh +++ b/tools/scripts/generate_graphics_rules.sh @@ -35,9 +35,10 @@ emit_rules() { ((.mheight // $d.mheight // 0) | tostring), ((.oam // $d.oam // false) | tostring), ((.align // 0) | tostring), + ((.oamshape // $d.oamshape // "null") | tostring), (if .segments then (.segments | tojson) else "null" end) ] | @tsv - ' "$json" | while IFS=$'\t' read -r gfx_filename mwidth mheight oam align segments; do + ' "$json" | while IFS=$'\t' read -r gfx_filename mwidth mheight oam align oamshape segments; do local stem="graphics/${dir#graphics/}/${gfx_filename}" local target="${stem}.4bpp" @@ -53,7 +54,7 @@ emit_rules() { # Emit a $(GFX) call for each segment local first=1 - printf '%s' "$segments" | jq -r '.[] | [.segfile, (.mwidth // 0 | tostring), (.mheight // 0 | tostring), (.oam // false | tostring)] | @tsv' | while IFS=$'\t' read -r segfile seg_mw seg_mh seg_oam; do + printf '%s' "$segments" | jq -r '.[] | [.segfile, (.mwidth // 0 | tostring), (.mheight // 0 | tostring), (.oam // false | tostring), (.oamshape // "null" | tostring)] | @tsv' | while IFS=$'\t' read -r segfile seg_mw seg_mh seg_oam seg_oamshape; do local seg_png="${dir}/${segfile}.png" local seg_4bpp="${stem}_${segfile}.4bpp" local flags="" @@ -66,6 +67,9 @@ emit_rules() { if [ "$seg_oam" = "true" ]; then flags="$flags -oam" fi + if [ -n "$seg_oamshape" ] && [ "$seg_oamshape" != "null" ]; then + flags="$flags -oamshape $dir/$seg_oamshape" + fi printf '\t$(GFX) %s %s%s; \\\n' "$seg_png" "$seg_4bpp" "$flags" done @@ -94,6 +98,9 @@ emit_rules() { if [ "$oam" = "true" ]; then flags="$flags -oam" fi + if [ -n "$oamshape" ] && [ "$oamshape" != "null" ]; then + flags="$flags -oamshape $dir/$oamshape" + fi # Skip if no special flags and no alignment — the generic # pattern rule in the Makefile handles this case.