diff --git a/.gitignore b/.gitignore index 349671fabe..3301686d06 100644 --- a/.gitignore +++ b/.gitignore @@ -18,8 +18,10 @@ settings.mk /build/ /docs/doxygen/ /include/ld_addrs.h +/include/sprite/ # Assets /bin /img /msg +/sprite diff --git a/.vscode/settings.json b/.vscode/settings.json index c2b3c50402..7a8a3ed59a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,9 +10,15 @@ "-D_LANGUAGE_C", "-DSCRIPT(...)={}", ], + "clang-tidy.blacklist": [ + "include/PR" + ], "git.ignoreLimitWarning": true, "search.exclude": { "build": true, "docs/doxygen": true, }, + "python.autoComplete.extraPaths": [ + "./tools" + ], } diff --git a/Makefile b/Makefile index cb5ffe729d..bb21f0a9c3 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,8 @@ LD_SCRIPT := $(TARGET).ld LD_MAP := $(BUILD_DIR)/$(TARGET).map ASSETS_BIN := $(BUILD_DIR)/bin/assets/assets.bin MSG_BIN := $(BUILD_DIR)/msg.bin -GENERATED_HEADERS := include/ld_addrs.h +NPC_BIN := $(BUILD_DIR)/sprite/npc.bin +GENERATED_HEADERS := include/ld_addrs.h include/sprite ### Tools ### @@ -103,14 +104,14 @@ submodules: git submodule update --init --recursive split: - rm -rf bin img - $(SPLAT) --modes bin Yay0 PaperMarioMapFS PaperMarioMessages img + rm -rf bin msg img sprite + $(SPLAT) --modes ld bin Yay0 PaperMarioMapFS PaperMarioMessages img PaperMarioNpcSprites split-%: - $(SPLAT) --modes $* --verbose + $(SPLAT) --modes ld $* --verbose split-all: - rm -rf bin img + rm -rf bin msg img sprite $(SPLAT) --modes all test: $(ROM) @@ -140,7 +141,7 @@ $(BUILD_DIR)/%.c.o: %.c $(MDEPS) | $(GENERATED_HEADERS) $(CPP) $(CPPFLAGS) -o - $(CPPMFLAGS) $< | iconv --from UTF-8 --to SHIFT-JIS | $(CC) $(CFLAGS) -o - | $(OLD_AS) $(OLDASFLAGS) -o $@ - # Compile C files (with DSL macros) -$(foreach cfile, $(DSL_C_FILES), $(BUILD_DIR)/$(cfile).o): $(BUILD_DIR)/%.c.o: %.c $(MDEPS) tools/compile_dsl_macros.py | include/ld_addrs.h +$(foreach cfile, $(DSL_C_FILES), $(BUILD_DIR)/$(cfile).o): $(BUILD_DIR)/%.c.o: %.c $(MDEPS) tools/compile_dsl_macros.py | $(GENERATED_HEADERS) @mkdir -p $(shell dirname $@) $(CPP) $(CPPFLAGS) -o - $< $(CPPMFLAGS) | $(PYTHON) tools/compile_dsl_macros.py | iconv --from UTF-8 --to SHIFT-JIS | $(CC) $(CFLAGS) -o - | $(OLD_AS) $(OLDASFLAGS) -o $@ - @@ -202,11 +203,28 @@ $(MSG_BIN): $(MESSAGES) @echo "building $@" @$(PYTHON) tools/compile_messages.py $@ /dev/null $(MESSAGES) $(MSG_BIN:.bin=.o): $(MSG_BIN) - @mkdir -p $(shell dirname $@) $(LD) -r -b binary -o $@ $< -$(LD_SCRIPT): $(SPLAT_YAML) +# Sprites +$(foreach npc, $(NPC_SPRITES), $(eval $(BUILD_DIR)/sprite/npc/$(npc):: $(shell find sprite/npc/$(npc) -type f))) # dependencies +NPC_DIRS := $(foreach npc, $(NPC_SPRITES), sprite/npc/$(npc)) +NPC_YAY0 := $(foreach npc, $(NPC_SPRITES), $(BUILD_DIR)/sprite/npc/$(npc).Yay0) +$(BUILD_DIR)/sprite/npc/%:: sprite/npc/% tools/compile_npc_sprite.py @mkdir -p $(shell dirname $@) + $(PYTHON) tools/compile_npc_sprite.py $@ $< +$(NPC_BIN): $(NPC_YAY0) tools/compile_npc_sprites.py + @mkdir -p $(shell dirname $@) + @echo "building $@" + @$(PYTHON) tools/compile_npc_sprites.py $@ $(NPC_YAY0) +$(NPC_BIN:.bin=.o): $(NPC_BIN) + $(LD) -r -b binary -o $@ $< +include/sprite/npc/%.h: sprite/npc/%/SpriteSheet.xml tools/gen_sprite_animations_h.py + @mkdir -p $(shell dirname $@) + @echo "building $@" + @$(PYTHON) tools/gen_sprite_animations_h.py $@ sprite/npc/$* $(NPC_DIRS) +include/sprite: $(foreach dir, $(NPC_DIRS), include/$(dir).h) + +$(LD_SCRIPT): $(SPLAT_YAML) $(SPLAT) --modes ld $(BUILD_DIR)/$(LD_SCRIPT): $(LD_SCRIPT) @@ -230,7 +248,7 @@ include/ld_addrs.h: $(BUILD_DIR)/$(LD_SCRIPT) ### Make Settings ### -.PHONY: clean test setup submodules split $(ROM) +.PHONY: clean test setup submodules split $(ROM) include/sprite .DELETE_ON_ERROR: .SECONDARY: .PRECIOUS: $(ROM) %.Yay0 diff --git a/include/enums.h b/include/enums.h index 070b02f311..c88b6e3aee 100644 --- a/include/enums.h +++ b/include/enums.h @@ -25,241 +25,6 @@ typedef s8 PartnerId; #define PartnerId_GOOMBARIA 0x0A #define PartnerId_TWINK 0x0B -typedef UNK_TYPE SpriteId; -#define SpriteId_WORLD_GOOMBARIO 0x00000001 -#define SpriteId_WORLD_KOOPER 0x00000002 -#define SpriteId_WORLD_BOMBETTE 0x00000003 -#define SpriteId_WORLD_PARAKARRY 0x00000004 -#define SpriteId_WORLD_BOW 0x00000005 -#define SpriteId_WORLD_WATT 0x00000006 -#define SpriteId_WORLD_SUSHI 0x00000007 -#define SpriteId_WORLD_LAKILESTER 0x00000008 -#define SpriteId_BATTLE_GOOMBARIO 0x00000009 -#define SpriteId_BATTLE_KOOPER 0x0000000A -#define SpriteId_BATTLE_BOMBETTE 0x0000000B -#define SpriteId_BATTLE_PARAKARRY 0x0000000C -#define SpriteId_BATTLE_BOW 0x0000000D -#define SpriteId_BATTLE_WATT 0x0000000E -#define SpriteId_BATTLE_SUSHI 0x0000000F -#define SpriteId_BATTLE_LAKILESTER 0x00000010 -#define SpriteId_KOOPER_WITHOUT_SHELL 0x00000011 -#define SpriteId_WORLD_ELDSTAR 0x00000012 -#define SpriteId_WORLD_MAMAR 0x00000013 -#define SpriteId_WORLD_SKOLAR 0x00000014 -#define SpriteId_WORLD_MUSKULAR 0x00000015 -#define SpriteId_WORLD_MISSTAR 0x00000016 -#define SpriteId_WORLD_KLEVAR 0x00000017 -#define SpriteId_WORLD_KALMAR 0x00000018 -#define SpriteId_BATTLE_ELDSTAR 0x00000019 -#define SpriteId_BATTLE_MAMAR 0x0000001A -#define SpriteId_BATTLE_SKOLAR 0x0000001B -#define SpriteId_BATTLE_MUSKULAR 0x0000001C -#define SpriteId_BATTLE_MISSTAR 0x0000001D -#define SpriteId_BATTLE_KLEVAR 0x0000001E -#define SpriteId_BATTLE_KALMAR 0x0000001F -#define SpriteId_TWINK 0x00000020 -#define SpriteId_JR_TROOPA 0x00000021 -#define SpriteId_SPIKED_JR_TROOPA 0x00000022 -#define SpriteId_SPIKED_PARA_JR_TROOPA 0x00000023 -#define SpriteId_MAGE_JR_TROOPA 0x00000024 -#define SpriteId_PARA_JR_TROOPA 0x00000025 -#define SpriteId_GOOMBA 0x00000026 -#define SpriteId_SPIKED_GOOMBA 0x00000027 -#define SpriteId_PARAGOOMBA 0x00000028 -#define SpriteId_KOOPA_TROOPA 0x00000029 -#define SpriteId_PARA_TROOPA 0x0000002A -#define SpriteId_FUZZY 0x0000002B -#define SpriteId_BOMBOMB 0x0000002C -#define SpriteId_BULLET_BILL 0x0000002D -#define SpriteId_BILL_BLASTER 0x0000002E -#define SpriteId_MONTY_MOLE 0x0000002F -#define SpriteId_CLEFT 0x00000030 -#define SpriteId_POKEY 0x00000031 -#define SpriteId_BANDIT 0x00000032 -#define SpriteId_BUZZY_BEETLE 0x00000033 -#define SpriteId_SWOOPER 0x00000034 -#define SpriteId_STONE_CHOMP 0x00000035 -#define SpriteId_PUTRID_PIRANHA 0x00000036 -#define SpriteId_PIRANHA_PLANT 0x00000037 -#define SpriteId_SENTINEL 0x00000038 -#define SpriteId_WORLD_CLUBBA 0x00000039 -#define SpriteId_BATTLE_CLUBBA 0x0000003A -#define SpriteId_SHY_GUY 0x0000003B -#define SpriteId_GROOVE_GUY 0x0000003C -#define SpriteId_SKY_GUY 0x0000003D -#define SpriteId_PYRO_GUY 0x0000003E -#define SpriteId_SPY_GUY 0x0000003F -#define SpriteId_MEDI_GUY 0x00000040 -#define SpriteId_FUZZIPEDE 0x00000041 -#define SpriteId_JUNGLE_GUY 0x00000042 -#define SpriteId_HEART_PLANT 0x00000043 -#define SpriteId_HURT_PLANT 0x00000044 -#define SpriteId_M_BUSH 0x00000045 -#define SpriteId_BUBBLE 0x00000046 -#define SpriteId_KENT_C_KOOPA 0x00000047 -#define SpriteId_DAYZEE 0x00000048 -#define SpriteId_LAKITU 0x00000049 -#define SpriteId_SPINY 0x0000004A -#define SpriteId_BZZAP 0x0000004B -#define SpriteId_RUFF_PUFF 0x0000004C -#define SpriteId_SPIKE_TOP 0x0000004D -#define SpriteId_DUPLIGHOST 0x0000004E -#define SpriteId_ALBINO_DINO 0x0000004F -#define SpriteId_BLOOPER 0x00000050 -#define SpriteId_BABY_BLOOPER 0x00000051 -#define SpriteId_GULPIT 0x00000052 -#define SpriteId_DRY_BONES 0x00000053 -#define SpriteId_THROWN_BONE 0x00000054 -#define SpriteId_BONY_BEETLE 0x00000055 -#define SpriteId_MAGIKOOPA 0x00000056 -#define SpriteId_FLYING_MAGIKOOPA 0x00000057 -#define SpriteId_WORLD_KOOPATROL 0x00000058 -#define SpriteId_KOOPATROL 0x00000059 -#define SpriteId_HAMMER_BROS 0x0000005A -#define SpriteId_BUSH_BASIC 0x0000005B -#define SpriteId_BUSH_BLOCKY 0x0000005C -#define SpriteId_BUSH_DRY 0x0000005D -#define SpriteId_BUSH_LEAFY 0x0000005E -#define SpriteId_BUSH_MATTED 0x0000005F -#define SpriteId_WORLD_KAMMY 0x00000060 -#define SpriteId_BATTLE_KAMMY 0x00000061 -#define SpriteId_GOOMBA_BROS 0x00000062 -#define SpriteId_GOOMBA_KING 0x00000063 -#define SpriteId_SPIKY_GOOMNUT 0x00000064 -#define SpriteId_DARK_TOAD 0x00000065 -#define SpriteId_KOOPA_BROS 0x00000066 -#define SpriteId_BUZZAR 0x00000067 -#define SpriteId_TUTANKOOPA 0x00000068 -#define SpriteId_CHAIN_CHOMP 0x00000069 -#define SpriteId_WORLD_TUBBA 0x0000006A -#define SpriteId_BATTLE_TUBBA 0x0000006B -#define SpriteId_TUBBAS_HEART 0x0000006C -#define SpriteId_BIG_LANTERN_GHOST 0x0000006D -#define SpriteId_SHY_SQUAD_GUY 0x0000006E -#define SpriteId_MARSHAL_GUY 0x0000006F -#define SpriteId_STILT_GUY 0x00000070 -#define SpriteId_STILT_GUY_UNFOLD 0x00000071 -#define SpriteId_SHY_STACK_GUY 0x00000072 -#define SpriteId_SHY_STACK_UNFOLD 0x00000073 -#define SpriteId_SHY_STACK_DAMAGE 0x00000074 -#define SpriteId_SHY_STACK_ROCK 0x00000075 -#define SpriteId_GENERAL_GUY 0x00000076 -#define SpriteId_GENERAL_GUY_BOMB 0x00000077 -#define SpriteId_TANK_GUY 0x00000078 -#define SpriteId_LAVA_PIRANHA_HEAD 0x00000079 -#define SpriteId_PETIT_PIRANHA 0x0000007A -#define SpriteId_LAVA_BUD 0x0000007B -#define SpriteId_HUFF_N_PUFF 0x0000007C -#define SpriteId_TUFF_PUFF 0x0000007D -#define SpriteId_MONSTAR 0x0000007E -#define SpriteId_CRYSTAL_KING 0x0000007F -#define SpriteId_WORLD_BOWSER 0x00000080 -#define SpriteId_BATTLE_BOWSER 0x00000081 -#define SpriteId_LUIGI 0x00000082 -#define SpriteId_TOAD 0x00000083 -#define SpriteId_THREE_SISTERS 0x00000084 -#define SpriteId_VANNA_T 0x00000085 -#define SpriteId_TOAD_KID 0x00000086 -#define SpriteId_TOAD_GUARD 0x00000087 -#define SpriteId_HARRY_T 0x00000088 -#define SpriteId_TOAD_MINISTER 0x00000089 -#define SpriteId_POSTMASTER 0x0000008A -#define SpriteId_CONDUCTOR_TOAD 0x0000008B -#define SpriteId_TRAIN_STATION_TOAD 0x0000008C -#define SpriteId_FISHMAEL 0x0000008D -#define SpriteId_ARTIST_TOAD 0x0000008E -#define SpriteId_KOOPA 0x0000008F -#define SpriteId_KOOPA_WITHOUT_SHELL 0x00000090 -#define SpriteId_WORLD_BOMBOMB 0x00000091 -#define SpriteId_WHACKA 0x00000092 -#define SpriteId_DRYITE 0x00000093 -#define SpriteId_MOUSER 0x00000094 -#define SpriteId_BOO 0x00000095 -#define SpriteId_YOSHI 0x00000096 -#define SpriteId_YOSHI_KID 0x00000097 -#define SpriteId_RAVEN 0x00000098 -#define SpriteId_BUBULB 0x00000099 -#define SpriteId_PENGUIN 0x0000009A -#define SpriteId_SHIVER_TOAD 0x0000009B -#define SpriteId_ALT_BANDIT 0x0000009C -#define SpriteId_GOOMPAPA 0x0000009D -#define SpriteId_GOOMBARIA 0x0000009E -#define SpriteId_GOOMAMA 0x0000009F -#define SpriteId_GOOMPA 0x000000A0 -#define SpriteId_GOOMMA 0x000000A1 -#define SpriteId_THE_MASTER 0x000000A2 -#define SpriteId_CHAN 0x000000A3 -#define SpriteId_LEE 0x000000A4 -#define SpriteId_MERLON 0x000000A5 -#define SpriteId_CHET_RIPPO 0x000000A6 -#define SpriteId_ROWF 0x000000A7 -#define SpriteId_MINH_T 0x000000A8 -#define SpriteId_RUSS_T 0x000000A9 -#define SpriteId_TAYCE_T 0x000000AA -#define SpriteId_FICE_T 0x000000AB -#define SpriteId_BARTENDER 0x000000AC -#define SpriteId_CHANTERELLE 0x000000AD -#define SpriteId_RIP_CHEATO 0x000000AE -#define SpriteId_CHUCK_QUIZMO 0x000000AF -#define SpriteId_MERLUVLEE 0x000000B0 -#define SpriteId_MERLAR 0x000000B1 -#define SpriteId_MERLOW 0x000000B2 -#define SpriteId_STAR_KID 0x000000B3 -#define SpriteId_KOLORADO_WIFE 0x000000B4 -#define SpriteId_KOOPA_KOOT 0x000000B5 -#define SpriteId_KOLORADO 0x000000B6 -#define SpriteId_BATTLE_KOLORADO 0x000000B7 -#define SpriteId_ARCHEOLOGIST 0x000000B8 -#define SpriteId_NOMADIMOUSE 0x000000B9 -#define SpriteId_WORLD_MERLEE 0x000000BA -#define SpriteId_BATTLE_MERLEE 0x000000BB -#define SpriteId_DISGUISED_MOUSTAFA 0x000000BC -#define SpriteId_MOUSTAFA 0x000000BD -#define SpriteId_OAKLIE 0x000000BE -#define SpriteId_BOOTLER 0x000000BF -#define SpriteId_YAKKEY 0x000000C0 -#define SpriteId_GOURMET_GUY 0x000000C1 -#define SpriteId_VILLAGE_LEADER 0x000000C2 -#define SpriteId_LEADERS_FRIEND 0x000000C3 -#define SpriteId_RAFAEL_RAVEN 0x000000C4 -#define SpriteId_TOLIELUP 0x000000C5 -#define SpriteId_GATE_FLOWER 0x000000C6 -#define SpriteId_PETUNIA 0x000000C7 -#define SpriteId_POSIE 0x000000C8 -#define SpriteId_LILY 0x000000C9 -#define SpriteId_ROSIE 0x000000CA -#define SpriteId_SUN 0x000000CB -#define SpriteId_LAKILULU 0x000000CC -#define SpriteId_NINJI 0x000000CD -#define SpriteId_MAYOR_PENGUIN 0x000000CE -#define SpriteId_MAYOR_PENGUIN_WIFE 0x000000CF -#define SpriteId_PENGUIN_PATROL 0x000000D0 -#define SpriteId_HERRINGWAY 0x000000D1 -#define SpriteId_MERLE 0x000000D2 -#define SpriteId_STAR_ROD 0x000000D3 -#define SpriteId_FIRE 0x000000D4 -#define SpriteId_COIN 0x000000D5 -#define SpriteId_PARADE_PEACH 0x000000D6 -#define SpriteId_PARADE_KOOPAS 0x000000D7 -#define SpriteId_PARADE_BURNT_BOWSER 0x000000D8 -#define SpriteId_PARADE_LUIGI 0x000000D9 -#define SpriteId_PARADE_PARTNERS 0x000000DA -#define SpriteId_PARADE_YOSHIS 0x000000DB -#define SpriteId_PARADA_KOLORADOS 0x000000DC -#define SpriteId_PARADE_CHICKS 0x000000DD -#define SpriteId_PARADE_ICE_SHOW 0x000000DE -#define SpriteId_PARADE_TOADS 0x000000DF -#define SpriteId_PARADE_BATONS 0x000000E0 -#define SpriteId_PARADE_DRUMS 0x000000E1 -#define SpriteId_PARADE_FLAGS 0x000000E2 -#define SpriteId_PARADE_HORNS 0x000000E3 -#define SpriteId_PARADE_TUBBA_BALLOON 0x000000E4 -#define SpriteId_PARADE_WIZARDS 0x000000E5 -#define SpriteId_PARADE_MARIO 0x000000E6 -#define SpriteId_PARADE_SHY_GUYS 0x000000E7 -#define SpriteId_PARADE_TWINK 0x000000E8 -#define SpriteId_LEAF 0x000000E9 - typedef s32 SongID; #define Song_TOAD_TOWN 0x00000000 #define Song_NORMAL_BATTLE 0x00000002 diff --git a/include/map.h b/include/map.h index d2345082fd..1278918d5b 100644 --- a/include/map.h +++ b/include/map.h @@ -164,8 +164,6 @@ typedef struct StatDrop { { F16(100), F16(30), attempts, F16(40) }, \ } -#define ANIMATION(sprite, palette, anim) ((sprite << 16) + (palette << 8) + anim) - #define OVERRIDE_MOVEMENT_SPEED(speed) (speed * 32767) #define NO_OVERRIDE_MOVEMENT_SPEED OVERRIDE_MOVEMENT_SPEED(-1) @@ -186,9 +184,9 @@ typedef struct StaticNPC { /* 0x0DC */ s16 maxCoinBonus; /* 0x0DE */ char unk_DE[2]; /* 0x0E0 */ s32 movement[48]; // TODO: type - /* 0x1A0 */ s32 animations[16]; + /* 0x1A0 */ NpcAnimID animations[16]; /* 0x1E0 */ char unk_1E0[8]; - /* 0x1E8 */ UNK_PTR extraAnimations; + /* 0x1E8 */ NpcAnimID* extraAnimations; /* 0x1EC */ MessageID tattle; } StaticNpc; // size = 0x1F0 diff --git a/include/types.h b/include/types.h index 298e297e4d..9721241a66 100644 --- a/include/types.h +++ b/include/types.h @@ -13,4 +13,7 @@ typedef s32 FormationID; #define FORMATION_ID(section, stage, index) ((section << 16) + (stage << 8) + index) +typedef s32 NpcAnimID; +#define NPC_ANIM(sprite, palette, anim) ((_NPC_SPRITE_##sprite << 16) + (_NPC_PALETTE_##sprite##_##palette << 8) + _NPC_ANIM_##sprite##_##anim) + #endif diff --git a/sources.mk b/sources.mk index 2d3efdad4e..488dd9a935 100644 --- a/sources.mk +++ b/sources.mk @@ -58,5 +58,7 @@ ASSETS := \ MESSAGES := $(shell find msg -type f -name "*.msg") +NPC_SPRITES := world_goombario world_kooper world_bombette world_parakarry world_bow world_watt world_sushi world_lakilester battle_goombario battle_kooper battle_bombette battle_parakarry battle_bow battle_watt battle_sushi battle_lakilester kooper_without_shell world_eldstar world_mamar world_skolar world_muskular world_misstar world_klevar world_kalmar battle_eldstar battle_mamar battle_skolar battle_muskular battle_misstar battle_klevar battle_kalmar twink jr_troopa spiked_jr_troopa spiked_para_jr_troopa mage_jr_troopa para_jr_troopa goomba spiked_goomba paragoomba koopa_troopa para_troopa fuzzy bob_omb bullet_bill bill_blaster monty_mole cleft pokey battle_bandit buzzy_beetle swooper stone_chomp putrid_piranha piranha_plant sentinel world_clubba battle_clubba shy_guy groove_guy sky_guy pyro_guy spy_guy medi_guy fuzzipede jungle_guy heart_plant hurt_plant m_bush bubble kent_c_koopa dayzee lakitu spiny bzzap ruff_puff spike_top duplighost albino_dino blooper baby_blooper gulpit dry_bones thrown_bone bony_beetle magikoopa flying_magikoopa world_koopatrol koopatrol hammer_bros bush_basic bush_blocky bush_dry bush_leafy bush_matted world_kammy battle_kammy goomba_bros goomba_king spiky_goomnut dark_toad koopa_bros buzzar tutankoopa chain_chomp world_tubba battle_tubba tubbas_heart big_lantern_ghost shy_squad_guy marshal_guy stilt_guy stilt_guy_unfold shy_stack_guy shy_stack_unfold shy_stack_damage shy_stack_rock general_guy general_guy_bomb tank_guy lava_piranha_head petit_piranha lava_bud huff_n_puff tuff_puff monstar crystal_king world_bowser battle_bowser luigi toad three_sisters vanna_t toad_kid toad_guard harry_t toad_minister postmaster conductor_toad train_station_toad fishmael artist_toad koopa koopa_without_shell world_bob_omb whacka dryite mouser boo yoshi yoshi_kid raven bubulb penguin shiver_toad world_bandit goompapa goombaria goomama goompa goomma the_master chan lee merlon chet_rippo rowf minh_t russ_t tayce_t fice_t bartender chanterelle rip_cheato chuck_quizmo merluvlee merlar merlow star_kid kolorado_wife koopa_koot kolorado battle_kolorado archeologist nomadimouse world_merlee battle_merlee disguised_moustafa moustafa oaklie bootler yakkey gourmet_guy village_leader leaders_friend rafael_raven tolielup gate_flower petunia posie lily rosie sun lakilulu ninji mayor_penguin mayor_penguin_wife penguin_patrol herringway merle star_rod fire coin parade_peach parade_koopas parade_burnt_bowser parade_luigi parade_partners parade_yoshis parada_kolorados parade_chicks parade_ice_show parade_toads parade_batons parade_drums parade_flags parade_horns parade_tubba_balloon parade_wizards parade_mario parade_shy_guys parade_twink leaf + # Image settings $(BUILD_DIR)/img/battle/text_action_command_ratings.ia4.png: IMG_FLAGS = --flip-y diff --git a/src/world/area_kmr/kmr_12/events.c b/src/world/area_kmr/kmr_12/events.c index dd6e0e3b3e..44e6095e23 100644 --- a/src/world/area_kmr/kmr_12/events.c +++ b/src/world/area_kmr/kmr_12/events.c @@ -1,4 +1,5 @@ #include "kmr_12.h" +#include "sprite/npc/goomba.h" Script M(ExitWest) = EXIT_WALK_SCRIPT(60, 0, "kmr_07", 1); Script M(ExitEast) = EXIT_WALK_SCRIPT(60, 1, "kmr_11", 0); @@ -85,7 +86,7 @@ Script M(GoombaIdle) = SCRIPT({ sleep 1 SetSelfVar(0, FALSE) - SetNpcAnimation(NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 13)) + SetNpcAnimation(NpcId_SELF, NPC_ANIM(goomba, normal, fake_mushroom)) // TODO: work out why palette 0 is used here EnableNpcShadow(NpcId_SELF, FALSE) SetSelfEnemyFlagBits(0x00000020, TRUE) @@ -104,15 +105,15 @@ Script M(GoombaIdle) = SCRIPT({ SetNpcRotation(NpcId_SELF, 0, SI_VAR(0), 0) sleep 1 } - SetNpcAnimation(NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 0)) + SetNpcAnimation(NpcId_SELF, NPC_ANIM(goomba, normal, still)) loop 9 { SI_VAR(0) += 10.0 SetNpcRotation(NpcId_SELF, 0, SI_VAR(0), 0) sleep 1 } - SetNpcAnimation(NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 7)) + SetNpcAnimation(NpcId_SELF, NPC_ANIM(goomba, normal, dizzy)) sleep 20 - SetNpcAnimation(NpcId_SELF, ANIMATION(SpriteId_GOOMBA, 0, 1)) + SetNpcAnimation(NpcId_SELF, NPC_ANIM(goomba, normal, idle)) PlaySoundAtNpc(NpcId_SELF, 248, 0) func_802CFE2C(NpcId_SELF, 8192) func_802CFD30(NpcId_SELF, 5, 6, 1, 1, 0) @@ -166,22 +167,22 @@ StaticNpc M(goombaNpc) = { /* flying? */ TRUE, }, .animations = { - ANIMATION(SpriteId_GOOMBA, 0, 1), - ANIMATION(SpriteId_GOOMBA, 0, 2), - ANIMATION(SpriteId_GOOMBA, 0, 3), - ANIMATION(SpriteId_GOOMBA, 0, 3), - ANIMATION(SpriteId_GOOMBA, 0, 1), - ANIMATION(SpriteId_GOOMBA, 0, 1), - ANIMATION(SpriteId_GOOMBA, 0, 5), - ANIMATION(SpriteId_GOOMBA, 0, 5), - ANIMATION(SpriteId_GOOMBA, 0, 3), - ANIMATION(SpriteId_GOOMBA, 0, 3), - ANIMATION(SpriteId_GOOMBA, 0, 3), - ANIMATION(SpriteId_GOOMBA, 0, 3), - ANIMATION(SpriteId_GOOMBA, 0, 3), - ANIMATION(SpriteId_GOOMBA, 0, 3), - ANIMATION(SpriteId_GOOMBA, 0, 3), - ANIMATION(SpriteId_GOOMBA, 0, 3), + NPC_ANIM(goomba, normal, idle), + NPC_ANIM(goomba, normal, walk), + NPC_ANIM(goomba, normal, run), + NPC_ANIM(goomba, normal, run), + NPC_ANIM(goomba, normal, idle), + NPC_ANIM(goomba, normal, idle), + NPC_ANIM(goomba, normal, pain), + NPC_ANIM(goomba, normal, pain), + NPC_ANIM(goomba, normal, run), + NPC_ANIM(goomba, normal, run), + NPC_ANIM(goomba, normal, run), + NPC_ANIM(goomba, normal, run), + NPC_ANIM(goomba, normal, run), + NPC_ANIM(goomba, normal, run), + NPC_ANIM(goomba, normal, run), + NPC_ANIM(goomba, normal, run), }, }; diff --git a/tools/Makefile b/tools/Makefile index d3edc74d33..9e40ad25ca 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -2,7 +2,7 @@ default: all all: Yay0compress -Yay0compress: +Yay0compress: gcc Yay0compress.c -O3 -o Yay0compress clean: diff --git a/tools/compile_npc_sprite.py b/tools/compile_npc_sprite.py new file mode 100755 index 0000000000..6a8f8d42f2 --- /dev/null +++ b/tools/compile_npc_sprite.py @@ -0,0 +1,115 @@ +#! /usr/bin/python3 + +from sys import argv, path +from pathlib import Path +import os +path.append(os.path.join(os.path.dirname(__file__), "n64splat")) +from splat_ext.PaperMarioNpcSprites import Sprite +from convert_image import pack_color, iter_in_groups + +if __name__ == "__main__": + if len(argv) != 3: + print("usage: compile_npc_sprite.py [OUTBIN] [DIR]") + exit(1) + + _, outfile, sprite_dir = argv + + try: + sprite = Sprite.from_dir(Path(sprite_dir)) + except AssertionError as e: + print("error:", e) + exit(1) + + with open(outfile, "wb") as f: + f.seek(0x10) # leave space for header + + # leave space for animation offset list + f.seek((len(sprite.animations) + 1) * 4, 1) + animation_offsets = [] + + # write animations + for i, components in enumerate(sprite.animations): + animation_offsets.append(f.tell()) + + # leave space for component offset list + f.seek((len(components) + 1) * 4, 1) + component_offsets = [] + + for comp in components: + offset = f.tell() + + for command in comp.commands: + f.write(command.to_bytes(2, byteorder="big")) + + f.seek(f.tell() % 4, 1) + component_offsets.append(f.tell()) + + f.write(offset.to_bytes(4, byteorder="big")) + f.write((len(comp.commands) * 2).to_bytes(2, byteorder="big")) + f.write(comp.x.to_bytes(2, byteorder="big")) + f.write(comp.y.to_bytes(2, byteorder="big")) + f.write(comp.z.to_bytes(2, byteorder="big")) + + next_anim = f.tell() + + # write component offset list + f.seek(animation_offsets[i]) + component_offsets.append(-1) + for offset in component_offsets: + f.write(offset.to_bytes(4, byteorder="big", signed=True)) + + f.seek(next_anim) + + # palettes start 8-byte aligned + if (f.tell() & 7) == 4: + f.seek(4, 1) + + # write palettes + palette_offsets = [] + for i, palette in enumerate(sprite.palettes): + palette_offsets.append(f.tell()) + for rgba in palette: + if rgba[3] not in (0, 0xFF): + print("error: translucent pixels not allowed in palette {sprite.palette_names[i]}") + exit(1) + + color = pack_color(*rgba) + f.write(color.to_bytes(2, byteorder="big")) + + # write images/rasters + image_offsets = [] + for image in sprite.images: + offset = f.tell() + + for a, b in iter_in_groups(image.raster, 2): + byte = (a << 4) | b + f.write(byte.to_bytes(1, byteorder="big")) + + image_offsets.append(f.tell()) + + f.write(offset.to_bytes(4, byteorder="big")) + f.write(bytes([image.width, image.height, image.palette_index, 0xFF])) + + # write image offset list + image_offset_list_offset = f.tell() + image_offsets.append(-1) + for offset in image_offsets: + f.write(offset.to_bytes(4, byteorder="big", signed=True)) + + # write palette offset list + palette_offset_list_offset = f.tell() + palette_offsets.append(-1) + for offset in palette_offsets: + f.write(offset.to_bytes(4, byteorder="big", signed=True)) + + # write header + f.seek(0) + f.write(image_offset_list_offset.to_bytes(4, byteorder="big")) + f.write(palette_offset_list_offset.to_bytes(4, byteorder="big")) + f.write(sprite.max_components.to_bytes(4, byteorder="big")) + f.write(sprite.num_variations.to_bytes(4, byteorder="big")) + + # write animation offset list + animation_offsets.append(-1) + for offset in animation_offsets: + f.write(offset.to_bytes(4, byteorder="big", signed=True)) diff --git a/tools/compile_npc_sprites.py b/tools/compile_npc_sprites.py new file mode 100755 index 0000000000..19bb8a82f8 --- /dev/null +++ b/tools/compile_npc_sprites.py @@ -0,0 +1,30 @@ +#! /usr/bin/python3 + +from sys import argv + +if __name__ == "__main__": + if len(argv) < 4: + print("usage: compile_npc_sprites.py [OUTBIN] [INFILES]") + exit(1) + + _, outfile, *infiles = argv + + with open(outfile, "wb") as f: + offsets = [] + + f.seek(4 * (len(infiles) + 1)) + + for filename in infiles: + with open(filename, "rb") as spritef: + offsets.append(f.tell()) + f.write(spritef.read()) + + # align + while f.tell() % 8 != 0: + f.write(bytes([0])) + + offsets.append(f.tell()) + + f.seek(0) + for offset in offsets: + f.write(offset.to_bytes(4, byteorder="big")) diff --git a/tools/gen_sprite_animations_h.py b/tools/gen_sprite_animations_h.py new file mode 100755 index 0000000000..0174bc5640 --- /dev/null +++ b/tools/gen_sprite_animations_h.py @@ -0,0 +1,44 @@ +#! /usr/bin/python3 + +from sys import argv, path +from pathlib import Path +import os +path.append(os.path.join(os.path.dirname(__file__), "n64splat")) +from splat_ext.PaperMarioNpcSprites import Sprite + +if __name__ == "__main__": + if len(argv) < 4: + print("usage: gen_sprite_animations_h.py [OUT] [DIR] [ALLDIRS]") + exit(1) + + _, outfile, sprite_dir, *alldirs = argv + + with open(outfile, "w") as f: + # get sprite index + s = alldirs.index(sprite_dir) + 1 + assert s >= 1 + + sprite_dir = Path(sprite_dir) + + sprite = Sprite.from_dir(sprite_dir, read_images=False) + sprite_name = sprite_dir.stem + + f.write(f"#ifndef _SPRITE_{sprite_name.upper()}_H_\n") + f.write(f"#define _SPRITE_{sprite_name.upper()}_H_\n") + f.write("\n") + f.write('#include "types.h"\n') + f.write("\n") + + f.write(f"#define _NPC_SPRITE_{sprite_name} 0x{s:02X}\n") + + seen_palette_names = set() + for p, palette_name in enumerate(sprite.palette_names): + if palette_name not in seen_palette_names: + seen_palette_names.add(palette_name) + f.write(f"#define _NPC_PALETTE_{sprite_name}_{palette_name} 0x{p:02X}\n") + + for a, name in enumerate(sprite.animation_names): + f.write(f"#define _NPC_ANIM_{sprite_name}_{name} 0x{a:02X}\n") + + f.write("\n") + f.write("#endif\n") diff --git a/tools/n64splat b/tools/n64splat index 24aa8a9d28..686be7cf19 160000 --- a/tools/n64splat +++ b/tools/n64splat @@ -1 +1 @@ -Subproject commit 24aa8a9d284cbc170d691f20874171b018deb53e +Subproject commit 686be7cf19780b5dd619e0f51bb071f721b05b9a diff --git a/tools/sort_symbol_addrs.py b/tools/sort_symbol_addrs.py old mode 100644 new mode 100755 diff --git a/tools/splat.yaml b/tools/splat.yaml index ad373ac6f8..fa08fd955e 100644 --- a/tools/splat.yaml +++ b/tools/splat.yaml @@ -6869,400 +6869,497 @@ segments: vram: 0x802B7000 files: - [0xE21870, c] - - [0xE21EB0, bin] - - [0xE225B0, bin] - - [0xF007C0, bin, Battle_Fanfare_02] # BGM start - - [0xF02160, bin, Hey_You_03] - - [0xF03740, bin, The_Goomba_King_s_Decree_07] - - [0xF043F0, bin, Attack_of_the_Koopa_Bros_08] - - [0xF073C0, bin, Trojan_Bowser_09] - - [0xF08D40, bin, Chomp_Attack_0A] - - [0xF09600, bin, Ghost_Gulping_0B] - - [0xF0A550, bin, Keeping_Pace_0C] - - [0xF0BAE0, bin, Go_Mario_Go_0D] - - [0xF0DEC0, bin, Huffin_and_Puffin_0E] - - [0xF0FD20, bin, Freeze_0F] - - [0xF110D0, bin, Winning_a_Battle_8B] - - [0xF116C0, bin, Winning_a_Battle_and_Level_Up_8E] - - [0xF12320, bin, Jr_Troopa_Battle_04] - - [0xF13C20, bin, Final_Bowser_Battle_interlude_05] - - [0xF15F40, bin, Master_Battle_2C] - - [0xF16F80, bin, Game_Over_87] - - [0xF171D0, bin, Resting_at_the_Toad_House_88] - - [0xF17370, bin, Running_around_the_Heart_Pillar_in_Ch1_84] - - [0xF17570, bin, Tutankoopa_s_Warning_45] - - [0xF18940, bin, Kammy_Koopa_s_Theme_46] - - [0xF193D0, bin, Jr_Troopa_s_Theme_47] - - [0xF19BC0, bin, Goomba_King_s_Theme_50] - - [0xF1A6F0, bin, Koopa_Bros_Defeated_51] - - [0xF1ABD0, bin, Koopa_Bros_Theme_52] - - [0xF1C810, bin, Tutankoopa_s_Warning_2_53] - - [0xF1DBF0, bin, Tutankoopa_s_Theme_54] - - [0xF1F2E0, bin, Tubba_Blubba_s_Theme_55] - - [0xF20FF0, bin, General_Guy_s_Theme_56] - - [0xF21780, bin, Lava_Piranha_s_Theme_57] - - [0xF22A00, bin, Huff_N_Puff_s_Theme_58] - - [0xF23A00, bin, Crystal_King_s_Theme_59] - - [0xF24810, bin, Blooper_s_Theme_5A] - - [0xF25240, bin, Midboss_Theme_5B] - - [0xF26260, bin, Monstar_s_Theme_5C] - - [0xF27840, bin, Moustafa_s_Theme_86] - - [0xF27E20, bin, Fuzzy_Searching_Minigame_85] - - [0xF28E20, bin, Phonograph_in_Mansion_44] - - [0xF29AC0, bin, Toad_Town_00] - - [0xF2E130, bin, Bill_Blaster_Theme_48] - - [0xF2EF90, bin, Monty_Mole_Theme_in_Flower_Fields_49] - - [0xF30590, bin, Shy_Guys_in_Toad_Town_4A] - - [0xF318B0, bin, Whale_s_Problem_4C] - - [0xF32220, bin, Toad_Town_Sewers_4B] - - [0xF33060, bin, Unused_Theme_4D] - - [0xF33AA0, bin, Mario_s_House_Prologue_3E] - - [0xF33F10, bin, Peach_s_Party_3F] - - [0xF354E0, bin, Goomba_Village_01] - - [0xF35ED0, bin, Pleasant_Path_11] - - [0xF36690, bin, Fuzzy_s_Took_My_Shell_12] - - [0xF379E0, bin, Koopa_Village_13] - - [0xF38570, bin, Koopa_Bros_Fortress_14] - - [0xF39160, bin, Dry_Dry_Ruins_18] - - [0xF3A0D0, bin, Dry_Dry_Ruins_Mystery_19] - - [0xF3A450, bin, Mt_Rugged_16] - - [0xF3AF20, bin, Dry_Dry_Desert_Oasis_17] - - [0xF3C130, bin, Dry_Dry_Outpost_15] - - [0xF3CCC0, bin, Forever_Forest_1A] - - [0xF3E130, bin, Boo_s_Mansion_1B] - - [0xF3F3E0, bin, Bow_s_Theme_1C] - - [0xF40F00, bin, Gusty_Gulch_Adventure_1D] - - [0xF42F30, bin, Tubba_Blubba_s_Castle_1E] - - [0xF45500, bin, The_Castle_Crumbles_1F] - - [0xF465E0, bin, Shy_Guy_s_Toy_Box_20] - - [0xF474A0, bin, Toy_Train_Travel_21] - - [0xF47E10, bin, Big_Lantern_Ghost_s_Theme_22] - - [0xF48410, bin, Jade_Jungle_24] - - [0xF4A880, bin, Deep_Jungle_25] - - [0xF4BC00, bin, Lavalava_Island_26] - - [0xF4E690, bin, Search_for_the_Fearsome_5_27] - - [0xF50A00, bin, Raphael_the_Raven_28] - - [0xF52520, bin, Hot_Times_in_Mt_Lavalava_29] - - [0xF55C80, bin, Escape_from_Mt_Lavalava_2A] - - [0xF58ED0, bin, Cloudy_Climb_32] - - [0xF592B0, bin, Puff_Puff_Machine_33] - - [0xF5AFF0, bin, Flower_Fields_30] - - [0xF5C8D0, bin, Flower_Fields_Sunny_31] - - [0xF5DF40, bin, Sun_s_Tower_34] - - [0xF5F500, bin, Sun_s_Celebration_35] - - [0xF61700, bin, Shiver_City_38] - - [0xF62E50, bin, Detective_Mario_39] - - [0xF64220, bin, Snow_Road_3A] - - [0xF64CB0, bin, Over_Shiver_Mountain_3B] - - [0xF65B30, bin, Starborn_Valley_3C] - - [0xF66690, bin, Sanctuary_3D] - - [0xF66B70, bin, Crystal_Palace_37] - - [0xF67F80, bin, Star_Haven_60] - - [0xF69640, bin, Shooting_Star_Summit_61] - - [0xF6A050, bin, Legendary_Star_Ship_62] - - [0xF6C270, bin, Star_Sanctuary_63] - - [0xF6CED0, bin, Bowser_s_Castle_-_Caves_65] - - [0xF6EE40, bin, Bowser_s_Castle_64] - - [0xF73390, bin, Star_Elevator_2B] - - [0xF751F0, bin, Goomba_Bros_Defeated_7E] - - [0xF759C0, bin, Farewell_Twink_70] - - [0xF77200, bin, Peach_Cooking_71] - - [0xF77680, bin, Gourmet_Guy_72] - - [0xF78600, bin, Hope_on_the_Balcony_Peach_1_73] - - [0xF79070, bin, Peach_s_Theme_2_74] - - [0xF7A0C0, bin, Peach_Sneaking_75] - - [0xF7AA40, bin, Peach_Captured_76] - - [0xF7AD90, bin, Quiz_Show_Intro_77] - - [0xF7BEA0, bin, Unconscious_Mario_78] - - [0xF7C780, bin, Petunia_s_Theme_89] - - [0xF7DC00, bin, Flower_Fields_Door_appears_8A] - - [0xF7E190, bin, Beanstalk_7B] - - [0xF7EE20, bin, Lakilester_s_Theme_7D] - - [0xF80230, bin, The_Sun_s_Back_7F] - - [0xF81260, bin, Shiver_City_in_Crisis_79] - - [0xF82460, bin, Solved_Shiver_City_Mystery_7A] - - [0xF82D00, bin, Merlon_s_Spell_7C] - - [0xF83DC0, bin, Bowser_s_Theme_66] - - [0xF85590, bin, Train_Travel_80] - - [0xF860E0, bin, Whale_Trip_81] - - [0xF87000, bin, Chanterelle_s_Song_8C] - - [0xF87610, bin, Boo_s_Game_8D] - - [0xF88B30, bin, Dry_Dry_Ruins_rises_up_83] - - [0xF89570, bin, End_of_Chapter_40] - - [0xF8AAF0, bin, Beginning_of_Chapter_41] - - [0xF8B820, bin, Hammer_and_Jump_Upgrade_42] - - [0xF8BD90, bin, Found_Baby_Yoshi_s_4E] - - [0xF8C360, bin, New_Partner_JAP_96] - - [0xF8D110, bin, Unused_YI_Fanfare_4F] - - [0xF8D3E0, bin, Unused_YI_Fanfare_2_5D] - - [0xF90880, bin, Peach_s_Castle_inside_Bubble_5E] - - [0xF92A50, bin, Angry_Bowser_67] - - [0xF95510, bin, Bowser_s_Castle_explodes_5F] - - [0xF96280, bin, Peach_s_Wish_68] - - [0xF98520, bin, File_Select_69] - - [0xF98F90, bin, Title_Screen_6A] - - [0xF9B830, bin, Peach_s_Castle_in_Crisis_6B] - - [0xF9D3B0, bin, Mario_falls_from_Bowser_s_Castle_6C] - - [0xF9D690, bin, Peach_s_Arrival_6D] - - [0xF9EF30, bin, Star_Rod_Recovered_6F] - - [0xF9FA30, bin, Mario_s_House_94] - - [0xFA08A0, bin, Bowser_s_Attacks_95] - - [0xFA3C60, bin, End_Parade_1_90] - - [0xFA85F0, bin, End_Parade_2_91] - - [0xFABE90, bin, The_End_6E] - - [0xFACC80, bin, Koopa_Radio_Station_2D] - - [0xFAD210, bin, The_End_Low_Frequency__2E] - - [0xFAD8F0, bin, SMW_Remix_2F] - - [0xFADE70, bin, New_Partner_82] # BGM end - - [0xFAE860, bin] - - [0x19E09A8, Yay0] - - [0x19E1390, Yay0] - - [0x19E1888, Yay0] - - [0x19E2330, Yay0] - - [0x19E2DE0, Yay0] - - [0x19E3208, Yay0] - - [0x19E3BA8, Yay0] - - [0x19E3FD8, Yay0] - - [0x19E4828, Yay0] - - [0x19E4BE0, Yay0] - - [0x19E5758, Yay0] - - [0x19E5950, Yay0] - - [0x19E62A0, Yay0] - - [0x19E67B2, bin] - - [0x19E6B60, Yay0] - - [0x19E7528, Yay0] - - [0x19E9778, Yay0] - - [0x19EAF38, Yay0] - - [0x19EC4E0, Yay0] - - [0x19EDD30, Yay0] - - [0x19EEB18, Yay0] - - [0x19F0070, Yay0] - - [0x19F15A0, Yay0] - - [0x19F26D8, Yay0] - - [0x19F5390, Yay0] - - [0x19F7398, Yay0] - - [0x19FA128, Yay0] - - [0x19FCAE8, Yay0] - - [0x19FED70, Yay0] - - [0x1A00958, Yay0] - - [0x1A02D00, Yay0] - - [0x1A04400, Yay0] - - [0x1A05550, Yay0] - - [0x1A06390, Yay0] - - [0x1A06F98, Yay0] - - [0x1A07B68, Yay0] - - [0x1A0A0A0, Yay0] - - [0x1A0ACC8, Yay0] - - [0x1A0B780, Yay0] - - [0x1A0BBE0, Yay0] - - [0x1A0C000, Yay0] - - [0x1A0C438, Yay0] - - [0x1A0C860, Yay0] - - [0x1A0D1E8, Yay0] - - [0x1A0D5B0, Yay0] - - [0x1A0D970, Yay0] - - [0x1A0EF00, Yay0] - - [0x1A13920, Yay0] - - [0x1A15850, Yay0] - - [0x1A183F8, Yay0] - - [0x1A1A608, Yay0] - - [0x1A1C5E8, Yay0] - - [0x1A1D6D0, Yay0] - - [0x1A1E478, Yay0] - - [0x1A1F370, Yay0] - - [0x1A226B0, Yay0] - - [0x1A249B8, Yay0] - - [0x1A25E78, Yay0] - - [0x1A27FF0, Yay0] - - [0x1A28BE0, Yay0] - - [0x1A29680, Yay0] - - [0x1A2BC68, Yay0] - - [0x1A2E120, Yay0] - - [0x1A2F3F8, Yay0] - - [0x1A31D18, Yay0] - - [0x1A33AB0, Yay0] - - [0x1A35BB8, Yay0] - - [0x1A369A8, Yay0] - - [0x1A39600, Yay0] - - [0x1A3B018, Yay0] - - [0x1A3C310, Yay0] - - [0x1A3FCC8, Yay0] - - [0x1A423D8, Yay0] - - [0x1A449C0, Yay0] - - [0x1A46568, Yay0] - - [0x1A49340, Yay0] - - [0x1A4AC88, Yay0] - - [0x1A4D7E8, Yay0] - - [0x1A4E028, Yay0] - - [0x1A4FA60, Yay0] - - [0x1A531D0, Yay0] - - [0x1A53D48, Yay0] - - [0x1A56C80, Yay0] - - [0x1A58F58, Yay0] - - [0x1A5A5A8, Yay0] - - [0x1A62B40, Yay0] - - [0x1A641F8, Yay0] - - [0x1A666F0, Yay0] - - [0x1A68870, Yay0] - - [0x1A6C630, Yay0] - - [0x1A6D5A0, Yay0] - - [0x1A6EF50, Yay0] - - [0x1A70FF0, Yay0] - - [0x1A74FC0, Yay0] - - [0x1A78A40, Yay0] - - [0x1A79900, Yay0] - - [0x1A7D798, Yay0] - - [0x1A7EEA0, Yay0] - - [0x1A7EFD8, Yay0] - - [0x1A83450, Yay0] - - [0x1A85668, Yay0] - - [0x1A87958, Yay0] - - [0x1A8BF98, Yay0] - - [0x1A8FE28, Yay0] - - [0x1A93EA0, Yay0] - - [0x1A94188, Yay0] - - [0x1A94480, Yay0] - - [0x1A946A8, Yay0] - - [0x1A94A00, Yay0] - - [0x1A94C58, Yay0] - - [0x1A98D98, Yay0] - - [0x1A9BA80, Yay0] - - [0x1A9DB68, Yay0] - - [0x1AA0048, Yay0] - - [0x1AA0698, Yay0] - - [0x1AA1008, Yay0] - - [0x1AA6D58, Yay0] - - [0x1AAD600, Yay0] - - [0x1AB1BF0, Yay0] - - [0x1AB2368, Yay0] - - [0x1ABA290, Yay0] - - [0x1AC14A8, Yay0] - - [0x1AC3910, Yay0] - - [0x1ACBC98, Yay0] - - [0x1ACE058, Yay0] - - [0x1ACF910, Yay0] - - [0x1AD06D8, Yay0] - - [0x1AD0B98, Yay0] - - [0x1AD1E90, Yay0] - - [0x1AD2348, Yay0] - - [0x1AD27F8, Yay0] - - [0x1AD28A8, Yay0] - - [0x1AD44F0, Yay0] - - [0x1AD4758, Yay0] - - [0x1AD57C0, Yay0] - - [0x1AD9D50, Yay0] - - [0x1ADA498, Yay0] - - [0x1ADCFC0, Yay0] - - [0x1AE2168, Yay0] - - [0x1AE2EA0, Yay0] - - [0x1AE6A58, Yay0] - - [0x1AEB778, Yay0] - - [0x1AF4958, Yay0] - - [0x1AFCB18, Yay0] - - [0x1AFF748, Yay0] - - [0x1B00640, Yay0] - - [0x1B01390, Yay0] - - [0x1B01C08, Yay0] - - [0x1B02128, Yay0] - - [0x1B02970, Yay0] - - [0x1B03118, Yay0] - - [0x1B03C18, Yay0] - - [0x1B045E8, Yay0] - - [0x1B04FC0, Yay0] - - [0x1B05998, Yay0] - - [0x1B06C88, Yay0] - - [0x1B07C48, Yay0] - - [0x1B09440, Yay0] - - [0x1B0B290, Yay0] - - [0x1B0B9A0, Yay0] - - [0x1B0C548, Yay0] - - [0x1B0CAC0, Yay0] - - [0x1B0D130, Yay0] - - [0x1B0EB80, Yay0] - - [0x1B10CC0, Yay0] - - [0x1B129A0, Yay0] - - [0x1B13548, Yay0] - - [0x1B16420, Yay0] - - [0x1B17128, Yay0] - - [0x1B17840, Yay0] - - [0x1B19318, Yay0] - - [0x1B1A030, Yay0] - - [0x1B1B8C8, Yay0] - - [0x1B1BC88, Yay0] - - [0x1B1C7A0, Yay0] - - [0x1B1CD28, Yay0] - - [0x1B21C48, Yay0] - - [0x1B23290, Yay0] - - [0x1B253E0, Yay0] - - [0x1B26660, Yay0] - - [0x1B283F8, Yay0] - - [0x1B29C60, Yay0] - - [0x1B2A688, Yay0] - - [0x1B2B3E8, Yay0] - - [0x1B2C8D8, Yay0] - - [0x1B2D7B0, Yay0] - - [0x1B2E328, Yay0] - - [0x1B2ED60, Yay0] - - [0x1B2FA18, Yay0] - - [0x1B31A18, Yay0] - - [0x1B33000, Yay0] - - [0x1B34098, Yay0] - - [0x1B34928, Yay0] - - [0x1B34C00, Yay0] - - [0x1B35480, Yay0] - - [0x1B36440, Yay0] - - [0x1B38748, Yay0] - - [0x1B39A98, Yay0] - - [0x1B3A2E8, Yay0] - - [0x1B3A818, Yay0] - - [0x1B3C488, Yay0] - - [0x1B3CAC8, Yay0] - - [0x1B3D0A0, Yay0] - - [0x1B3D920, Yay0] - - [0x1B3F060, Yay0] - - [0x1B40048, Yay0] - - [0x1B40720, Yay0] - - [0x1B49570, Yay0] - - [0x1B4C3E8, Yay0] - - [0x1B4DEA0, Yay0] - - [0x1B4FD98, Yay0] - - [0x1B50CD8, Yay0] - - [0x1B51B08, Yay0] - - [0x1B54258, Yay0] - - [0x1B580A0, Yay0] - - [0x1B5A248, Yay0] - - [0x1B5BB88, Yay0] - - [0x1B5CC90, Yay0] - - [0x1B5E968, Yay0] - - [0x1B5ED88, Yay0] - - [0x1B608C0, Yay0] - - [0x1B625F8, Yay0] - - [0x1B633D0, Yay0] - - [0x1B64878, Yay0] - - [0x1B657E0, Yay0] - - [0x1B65A08, Yay0] - - [0x1B65E50, Yay0] - - [0x1B66238, Yay0] - - [0x1B69580, Yay0] - - [0x1B6C318, Yay0] - - [0x1B6DD98, Yay0] - - [0x1B6F150, Yay0] - - [0x1B71618, Yay0] - - [0x1B72890, Yay0] - - [0x1B73B08, Yay0] - - [0x1B747B8, Yay0] - - [0x1B76E30, Yay0] - - [0x1B78EC0, Yay0] - - [0x1B79A20, Yay0] - - [0x1B79F08, Yay0] - - [0x1B7AA08, Yay0] - - [0x1B7B008, Yay0] - - [0x1B7BB50, Yay0] - - [0x1B7EC68, Yay0] - - [0x1B7FF48, Yay0] - - [0x1B81E88, Yay0] - - [0x1B82058, Yay0] - - [0x1B82202, bin] + - [0xE21EB0, "bin"] + - [0xE225B0, "bin"] + - [0xF007C0, "bin", "Battle_Fanfare_02"] # BGM start + - [0xF02160, "bin", "Hey_You_03"] + - [0xF03740, "bin", "The_Goomba_King_s_Decree_07"] + - [0xF043F0, "bin", "Attack_of_the_Koopa_Bros_08"] + - [0xF073C0, "bin", "Trojan_Bowser_09"] + - [0xF08D40, "bin", "Chomp_Attack_0A"] + - [0xF09600, "bin", "Ghost_Gulping_0B"] + - [0xF0A550, "bin", "Keeping_Pace_0C"] + - [0xF0BAE0, "bin", "Go_Mario_Go_0D"] + - [0xF0DEC0, "bin", "Huffin_and_Puffin_0E"] + - [0xF0FD20, "bin", "Freeze_0F"] + - [0xF110D0, "bin", "Winning_a_Battle_8B"] + - [0xF116C0, "bin", "Winning_a_Battle_and_Level_Up_8E"] + - [0xF12320, "bin", "Jr_Troopa_Battle_04"] + - [0xF13C20, "bin", "Final_Bowser_Battle_interlude_05"] + - [0xF15F40, "bin", "Master_Battle_2C"] + - [0xF16F80, "bin", "Game_Over_87"] + - [0xF171D0, "bin", "Resting_at_the_Toad_House_88"] + - [0xF17370, "bin", "Running_around_the_Heart_Pillar_in_Ch1_84"] + - [0xF17570, "bin", "Tutankoopa_s_Warning_45"] + - [0xF18940, "bin", "Kammy_Koopa_s_Theme_46"] + - [0xF193D0, "bin", "Jr_Troopa_s_Theme_47"] + - [0xF19BC0, "bin", "Goomba_King_s_Theme_50"] + - [0xF1A6F0, "bin", "Koopa_Bros_Defeated_51"] + - [0xF1ABD0, "bin", "Koopa_Bros_Theme_52"] + - [0xF1C810, "bin", "Tutankoopa_s_Warning_2_53"] + - [0xF1DBF0, "bin", "Tutankoopa_s_Theme_54"] + - [0xF1F2E0, "bin", "Tubba_Blubba_s_Theme_55"] + - [0xF20FF0, "bin", "General_Guy_s_Theme_56"] + - [0xF21780, "bin", "Lava_Piranha_s_Theme_57"] + - [0xF22A00, "bin", "Huff_N_Puff_s_Theme_58"] + - [0xF23A00, "bin", "Crystal_King_s_Theme_59"] + - [0xF24810, "bin", "Blooper_s_Theme_5A"] + - [0xF25240, "bin", "Midboss_Theme_5B"] + - [0xF26260, "bin", "Monstar_s_Theme_5C"] + - [0xF27840, "bin", "Moustafa_s_Theme_86"] + - [0xF27E20, "bin", "Fuzzy_Searching_Minigame_85"] + - [0xF28E20, "bin", "Phonograph_in_Mansion_44"] + - [0xF29AC0, "bin", "Toad_Town_00"] + - [0xF2E130, "bin", "Bill_Blaster_Theme_48"] + - [0xF2EF90, "bin", "Monty_Mole_Theme_in_Flower_Fields_49"] + - [0xF30590, "bin", "Shy_Guys_in_Toad_Town_4A"] + - [0xF318B0, "bin", "Whale_s_Problem_4C"] + - [0xF32220, "bin", "Toad_Town_Sewers_4B"] + - [0xF33060, "bin", "Unused_Theme_4D"] + - [0xF33AA0, "bin", "Mario_s_House_Prologue_3E"] + - [0xF33F10, "bin", "Peach_s_Party_3F"] + - [0xF354E0, "bin", "Goomba_Village_01"] + - [0xF35ED0, "bin", "Pleasant_Path_11"] + - [0xF36690, "bin", "Fuzzy_s_Took_My_Shell_12"] + - [0xF379E0, "bin", "Koopa_Village_13"] + - [0xF38570, "bin", "Koopa_Bros_Fortress_14"] + - [0xF39160, "bin", "Dry_Dry_Ruins_18"] + - [0xF3A0D0, "bin", "Dry_Dry_Ruins_Mystery_19"] + - [0xF3A450, "bin", "Mt_Rugged_16"] + - [0xF3AF20, "bin", "Dry_Dry_Desert_Oasis_17"] + - [0xF3C130, "bin", "Dry_Dry_Outpost_15"] + - [0xF3CCC0, "bin", "Forever_Forest_1A"] + - [0xF3E130, "bin", "Boo_s_Mansion_1B"] + - [0xF3F3E0, "bin", "Bow_s_Theme_1C"] + - [0xF40F00, "bin", "Gusty_Gulch_Adventure_1D"] + - [0xF42F30, "bin", "Tubba_Blubba_s_Castle_1E"] + - [0xF45500, "bin", "The_Castle_Crumbles_1F"] + - [0xF465E0, "bin", "Shy_Guy_s_Toy_Box_20"] + - [0xF474A0, "bin", "Toy_Train_Travel_21"] + - [0xF47E10, "bin", "Big_Lantern_Ghost_s_Theme_22"] + - [0xF48410, "bin", "Jade_Jungle_24"] + - [0xF4A880, "bin", "Deep_Jungle_25"] + - [0xF4BC00, "bin", "Lavalava_Island_26"] + - [0xF4E690, "bin", "Search_for_the_Fearsome_5_27"] + - [0xF50A00, "bin", "Raphael_the_Raven_28"] + - [0xF52520, "bin", "Hot_Times_in_Mt_Lavalava_29"] + - [0xF55C80, "bin", "Escape_from_Mt_Lavalava_2A"] + - [0xF58ED0, "bin", "Cloudy_Climb_32"] + - [0xF592B0, "bin", "Puff_Puff_Machine_33"] + - [0xF5AFF0, "bin", "Flower_Fields_30"] + - [0xF5C8D0, "bin", "Flower_Fields_Sunny_31"] + - [0xF5DF40, "bin", "Sun_s_Tower_34"] + - [0xF5F500, "bin", "Sun_s_Celebration_35"] + - [0xF61700, "bin", "Shiver_City_38"] + - [0xF62E50, "bin", "Detective_Mario_39"] + - [0xF64220, "bin", "Snow_Road_3A"] + - [0xF64CB0, "bin", "Over_Shiver_Mountain_3B"] + - [0xF65B30, "bin", "Starborn_Valley_3C"] + - [0xF66690, "bin", "Sanctuary_3D"] + - [0xF66B70, "bin", "Crystal_Palace_37"] + - [0xF67F80, "bin", "Star_Haven_60"] + - [0xF69640, "bin", "Shooting_Star_Summit_61"] + - [0xF6A050, "bin", "Legendary_Star_Ship_62"] + - [0xF6C270, "bin", "Star_Sanctuary_63"] + - [0xF6CED0, "bin", "Bowser_s_Castle_-_Caves_65"] + - [0xF6EE40, "bin", "Bowser_s_Castle_64"] + - [0xF73390, "bin", "Star_Elevator_2B"] + - [0xF751F0, "bin", "Goomba_Bros_Defeated_7E"] + - [0xF759C0, "bin", "Farewell_Twink_70"] + - [0xF77200, "bin", "Peach_Cooking_71"] + - [0xF77680, "bin", "Gourmet_Guy_72"] + - [0xF78600, "bin", "Hope_on_the_Balcony_Peach_1_73"] + - [0xF79070, "bin", "Peach_s_Theme_2_74"] + - [0xF7A0C0, "bin", "Peach_Sneaking_75"] + - [0xF7AA40, "bin", "Peach_Captured_76"] + - [0xF7AD90, "bin", "Quiz_Show_Intro_77"] + - [0xF7BEA0, "bin", "Unconscious_Mario_78"] + - [0xF7C780, "bin", "Petunia_s_Theme_89"] + - [0xF7DC00, "bin", "Flower_Fields_Door_appears_8A"] + - [0xF7E190, "bin", "Beanstalk_7B"] + - [0xF7EE20, "bin", "Lakilester_s_Theme_7D"] + - [0xF80230, "bin", "The_Sun_s_Back_7F"] + - [0xF81260, "bin", "Shiver_City_in_Crisis_79"] + - [0xF82460, "bin", "Solved_Shiver_City_Mystery_7A"] + - [0xF82D00, "bin", "Merlon_s_Spell_7C"] + - [0xF83DC0, "bin", "Bowser_s_Theme_66"] + - [0xF85590, "bin", "Train_Travel_80"] + - [0xF860E0, "bin", "Whale_Trip_81"] + - [0xF87000, "bin", "Chanterelle_s_Song_8C"] + - [0xF87610, "bin", "Boo_s_Game_8D"] + - [0xF88B30, "bin", "Dry_Dry_Ruins_rises_up_83"] + - [0xF89570, "bin", "End_of_Chapter_40"] + - [0xF8AAF0, "bin", "Beginning_of_Chapter_41"] + - [0xF8B820, "bin", "Hammer_and_Jump_Upgrade_42"] + - [0xF8BD90, "bin", "Found_Baby_Yoshi_s_4E"] + - [0xF8C360, "bin", "New_Partner_JAP_96"] + - [0xF8D110, "bin", "Unused_YI_Fanfare_4F"] + - [0xF8D3E0, "bin", "Unused_YI_Fanfare_2_5D"] + - [0xF90880, "bin", "Peach_s_Castle_inside_Bubble_5E"] + - [0xF92A50, "bin", "Angry_Bowser_67"] + - [0xF95510, "bin", "Bowser_s_Castle_explodes_5F"] + - [0xF96280, "bin", "Peach_s_Wish_68"] + - [0xF98520, "bin", "File_Select_69"] + - [0xF98F90, "bin", "Title_Screen_6A"] + - [0xF9B830, "bin", "Peach_s_Castle_in_Crisis_6B"] + - [0xF9D3B0, "bin", "Mario_falls_from_Bowser_s_Castle_6C"] + - [0xF9D690, "bin", "Peach_s_Arrival_6D"] + - [0xF9EF30, "bin", "Star_Rod_Recovered_6F"] + - [0xF9FA30, "bin", "Mario_s_House_94"] + - [0xFA08A0, "bin", "Bowser_s_Attacks_95"] + - [0xFA3C60, "bin", "End_Parade_1_90"] + - [0xFA85F0, "bin", "End_Parade_2_91"] + - [0xFABE90, "bin", "The_End_6E"] + - [0xFACC80, "bin", "Koopa_Radio_Station_2D"] + - [0xFAD210, "bin", "The_End_Low_Frequency__2E"] + - [0xFAD8F0, "bin", "SMW_Remix_2F"] + - [0xFADE70, "bin", "New_Partner_82"] # BGM end + - [0xFAE860, "bin"] + - [0x19E09A8, "Yay0"] + - [0x19E1390, "Yay0"] + - [0x19E1888, "Yay0"] + - [0x19E2330, "Yay0"] + - [0x19E2DE0, "Yay0"] + - [0x19E3208, "Yay0"] + - [0x19E3BA8, "Yay0"] + - [0x19E3FD8, "Yay0"] + - [0x19E4828, "Yay0"] + - [0x19E4BE0, "Yay0"] + - [0x19E5758, "Yay0"] + - [0x19E5950, "Yay0"] + - [0x19E62A0, "Yay0"] + - start: 0x19E67B8 + type: PaperMarioNpcSprites + files: + - name: world_goombario + frames: + - walk1 + - still + - walk2 + - pain + - headbonk + - still_copy1 + - still_copy2 + - jump + - talk1 + - talk2 + - jump_copy + - fall + - happy1 + - happy2 + - read_paper + palettes: + - normal + - poisoned + - inactive + - electrified + - burnt + animations: + - still + - idle + - walk + - run + - jump + - fall + - headbonk # centered at 0,0,0 rather than at base of sprite + - pain + - talk + - celebrate + - read_paper + - world_kooper + - world_bombette + - world_parakarry + - world_bow + - world_watt + - world_sushi + - world_lakilester + - battle_goombario + - battle_kooper + - battle_bombette + - battle_parakarry + - battle_bow + - battle_watt + - battle_sushi + - battle_lakilester + - kooper_without_shell + - world_eldstar + - world_mamar + - world_skolar + - world_muskular + - world_misstar + - world_klevar + - world_kalmar + - battle_eldstar + - battle_mamar + - battle_skolar + - battle_muskular + - battle_misstar + - battle_klevar + - battle_kalmar + - twink + - jr_troopa + - spiked_jr_troopa + - spiked_para_jr_troopa + - mage_jr_troopa + - para_jr_troopa + - name: goomba + frames: + - still + - lean_left + - lean_right + - pain1 + - pain2 + - jump + - open_mouth + - open_mouth_wide + - sleep + - dizzy1 + - dizzy2 + - dizzy3 + - dizzy4 + - burn_pain1 + - burn_pain2 + - burn_dead + - tense + - fake_plant + - fake_mushroom + - electrocute + palettes: + - normal + - red # unused + - hyper + - blue # unused + - gloomba + - poisoned + - poisoned + - poisoned + - poisoned + - poisoned + - unk_purple_status + - unk_purple_status + - unk_purple_status + - unk_purple_status + - unk_purple_status + - electrified + - electrified + - electrified + - electrified + - electrified + - burnt + - fake_mushroom + - fake_plant + animations: + - still + - idle + - walk + - run + - headbonk # centered at 0,0,0 rather than at base of sprite + - pain + - dead + - dizzy + - asleep + - burn_pain + - burn_dead + - pre_headbonk # centered at 0,0,0 rather than at base of sprite + - fake_plant + - fake_mushroom + - electrocute + - spiked_goomba + - paragoomba + - koopa_troopa + - para_troopa + - fuzzy + - bob_omb + - bullet_bill + - bill_blaster + - monty_mole + - cleft + - pokey + - battle_bandit + - buzzy_beetle + - swooper + - stone_chomp + - putrid_piranha + - piranha_plant + - sentinel + - world_clubba + - battle_clubba + - shy_guy + - groove_guy + - sky_guy + - pyro_guy + - spy_guy + - medi_guy + - fuzzipede + - jungle_guy + - heart_plant + - hurt_plant + - m_bush + - bubble + - kent_c_koopa + - dayzee + - lakitu + - spiny + - bzzap + - ruff_puff + - spike_top + - duplighost + - albino_dino + - blooper + - baby_blooper + - gulpit + - dry_bones + - thrown_bone + - bony_beetle + - magikoopa + - flying_magikoopa + - world_koopatrol + - koopatrol + - hammer_bros + - bush_basic + - bush_blocky + - bush_dry + - bush_leafy + - bush_matted + - world_kammy + - battle_kammy + - goomba_bros + - goomba_king + - spiky_goomnut + - dark_toad + - koopa_bros + - buzzar + - tutankoopa + - chain_chomp + - world_tubba + - battle_tubba + - tubbas_heart + - big_lantern_ghost + - shy_squad_guy + - marshal_guy + - stilt_guy + - stilt_guy_unfold + - shy_stack_guy + - shy_stack_unfold + - shy_stack_damage + - shy_stack_rock + - general_guy + - general_guy_bomb + - tank_guy + - lava_piranha_head + - petit_piranha + - lava_bud + - huff_n_puff + - tuff_puff + - monstar + - crystal_king + - world_bowser + - battle_bowser + - luigi + - toad + - three_sisters + - vanna_t + - toad_kid + - toad_guard + - harry_t + - toad_minister + - postmaster + - conductor_toad + - train_station_toad + - fishmael + - artist_toad + - koopa + - koopa_without_shell + - world_bob_omb + - whacka + - dryite + - mouser + - boo + - yoshi + - yoshi_kid + - raven + - bubulb + - penguin + - shiver_toad + - world_bandit + - goompapa + - goombaria + - goomama + - goompa + - goomma + - the_master + - chan + - lee + - merlon + - chet_rippo + - rowf + - minh_t + - russ_t + - tayce_t + - fice_t + - bartender + - chanterelle + - rip_cheato + - chuck_quizmo + - merluvlee + - merlar + - merlow + - star_kid + - kolorado_wife + - koopa_koot + - kolorado + - battle_kolorado + - archeologist + - nomadimouse + - world_merlee + - battle_merlee + - disguised_moustafa + - moustafa + - oaklie + - bootler + - yakkey + - gourmet_guy + - village_leader + - leaders_friend + - rafael_raven + - tolielup + - gate_flower + - petunia + - posie + - lily + - rosie + - sun + - lakilulu + - ninji + - mayor_penguin + - mayor_penguin_wife + - penguin_patrol + - herringway + - merle + - star_rod + - fire + - coin + - parade_peach + - parade_koopas + - parade_burnt_bowser + - parade_luigi + - parade_partners + - parade_yoshis + - parada_kolorados + - parade_chicks + - parade_ice_show + - parade_toads + - parade_batons + - parade_drums + - parade_flags + - parade_horns + - parade_tubba_balloon + - parade_wizards + - parade_mario + - parade_shy_guys + - parade_twink + - leaf + - [0x1B82208, bin] - start: 0x1B83000 type: PaperMarioMessages files: @@ -7314,6 +7411,7 @@ segments: - quiz/options - end/credits - [0x1C84D30, bin] + - [0x1E00000, bin] # sprites here; Star Rod considers this a copy of sprite/npc.bin, but that doesnt appear to be true - [0x1E40000, PaperMarioMapFS] - [0x27FEE22, bin] - [0x2800000] diff --git a/tools/splat_ext/PaperMarioNpcSprites.py b/tools/splat_ext/PaperMarioNpcSprites.py new file mode 100644 index 0000000000..718b6a0e3d --- /dev/null +++ b/tools/splat_ext/PaperMarioNpcSprites.py @@ -0,0 +1,301 @@ +from segtypes.segment import N64Segment +from pathlib import Path +from util import Yay0decompress +from util.iter import iter_in_groups +from util.color import unpack_color +import png +import xml.etree.ElementTree as ET + +class Sprite: + def __init__(self): + self.max_components = 0 + self.num_variations = 0 + + self.images = [] + self.palettes = [] + self.animations = [] + + self.image_names = [] + self.palette_names = [] + self.animation_names = [] + + @staticmethod + def from_bytes(data): + self = Sprite() + + image_offsets = Sprite.read_offset_list(data[int.from_bytes(data[0:4], byteorder="big"):]) + palette_offsets = Sprite.read_offset_list(data[int.from_bytes(data[4:8], byteorder="big"):]) + self.max_components = int.from_bytes(data[8:0xC], byteorder="big") + self.num_variations = int.from_bytes(data[0xC:0x10], byteorder="big") + animation_offsets = Sprite.read_offset_list(data[0x10:]) + + for offset in palette_offsets: + # 16 colors + color_data = data[offset : offset + 16 * 2] + self.palettes.append([unpack_color(c) for c in iter_in_groups(color_data, 2)]) + + for offset in image_offsets: + img = Image.from_bytes(data[offset:], data) + self.images.append(img) + + for offset in animation_offsets: + anim = [] + + for comp_offset in Sprite.read_offset_list(data[offset:]): + comp = Component.from_bytes(data[comp_offset:], data) + anim.append(comp) + + self.animations.append(anim) + + return self + + @staticmethod + def read_offset_list(data): + l = [] + pos = 0 + + while True: + offset = int.from_bytes(data[pos:pos+4], byteorder="big", signed=True) + + if offset == -1: + break + + l.append(offset) + + pos += 4 + + return l + + def write_to_dir(self, path): + SpriteSheet = ET.Element("SpriteSheet", { + "a": str(self.max_components), + "b": str(self.num_variations), + }) + + PaletteList = ET.SubElement(SpriteSheet, "PaletteList") + RasterList = ET.SubElement(SpriteSheet, "RasterList") + AnimationList = ET.SubElement(SpriteSheet, "AnimationList") + + palette_to_raster = {} + + for i, image in enumerate(self.images): + name = self.image_names[i] if self.image_names else f"Raster_{i:02X}" + image.write(path / (name + ".png"), self.palettes[image.palette_index]) + + if image.palette_index not in palette_to_raster: + palette_to_raster[image.palette_index] = [] + palette_to_raster[image.palette_index].append(image) + + ET.SubElement(RasterList, "Raster", { + "id": f"{i:X}", + "palette": f"{image.palette_index:X}", + "src": name + ".png", + }) + + for i, palette in enumerate(self.palettes): + name = self.palette_names[i] if self.palette_names else f"Palette_{i:02X}" + + if i in palette_to_raster: + img = palette_to_raster[i][0] + else: + img = self.images[0] + + img.write(path / (name + ".png"), palette) + + ET.SubElement(PaletteList, "Palette", { + "id": f"{i:X}", + "src": name + ".png", + }) + + for i, components in enumerate(self.animations): + Animation = ET.SubElement(AnimationList, "Animation", { + "name": self.animation_names[i] if self.animation_names else f"Anim_{i:X}", + }) + + for j, comp in enumerate(components): + Component = ET.SubElement(Animation, "Component", { + "name": f"Comp_{j:X}", + "xyz": ",".join(map(str, [comp.x, comp.y, comp.z])), + }) + + for cmd in comp.commands: + ET.SubElement(Component, "Command", {"val": f"{cmd:X}"}) + + xml = ET.ElementTree(SpriteSheet) + + # pretty print (Python 3.9+) + if hasattr(ET, "indent"): + ET.indent(xml, " ") + + xml.write(str(path / "SpriteSheet.xml"), encoding="unicode") + + @staticmethod + def from_dir(path, read_images=True): + self = Sprite() + + xml = ET.parse(str(path / "SpriteSheet.xml")) + SpriteSheet = xml.getroot() + + true_max_components = 0 + self.max_components = int(SpriteSheet.get("a")) + self.num_variations = int(SpriteSheet.get("b")) + + for Palette in SpriteSheet.findall("./PaletteList/Palette"): + if read_images: + img = png.Reader(str(path / Palette.get("src"))) + img.preamble(True) + palette = img.palette(alpha="force") + + assert len(palette) == 16 + + self.palettes.append(palette) + + self.palette_names.append(Palette.get("src").split(".png")[0]) + + for Raster in SpriteSheet.findall("./RasterList/Raster"): + if read_images: + img_path = str(path / Raster.get("src")) + width, height, raster, info = png.Reader(img_path).read_flat() + + image = Image() + image.width = width + image.height = height + image.raster = raster + image.palette_index = int(Raster.get("palette"), base=16) + + assert (image.width % 8) == 0, f"{img_path} width is not a multiple of 8" + assert (image.height % 8) == 0, f"{img_path} height is not a multiple of 8" + + self.images.append(image) + + self.image_names.append(Raster.get("src").split(".png")[0]) + + for i, Animation in enumerate(SpriteSheet.findall("./AnimationList/Animation")): + components = [] + + for ComponentEl in Animation.findall("Component"): + comp = Component() + + x, y, z = ComponentEl.get("xyz", "0,0,0").split(",") + comp.x = int(x) + comp.y = int(y) + comp.z = int(z) + + for Command in ComponentEl: + comp.commands.append(int(Command.get("val"), base=16)) + + components.append(comp) + + self.animation_names.append(Animation.get("name")) + self.animations.append(components) + + if len(components) > true_max_components: + true_max_components = len(components) + + assert self.max_components == true_max_components, f"{true_max_components} component(s) used, but SpriteSheet.a = {self.max_components}" + + return self + +class Image: + @staticmethod + def from_bytes(data, sprite_data): + self = Image() + + raster_offset = int.from_bytes(data[0:4], byteorder="big") + self.width = data[4] & 0xFF + self.height = data[5] & 0xFF + self.palette_index = data[6] + assert data[7] == 0xFF + + self.set_raster_from_bytes(sprite_data[raster_offset:]) + + return self + + # CI-4 + def set_raster_from_bytes(self, data): + self.raster = bytearray() + + for i in range(self.width * self.height // 2): + self.raster.append(data[i] >> 4) + self.raster.append(data[i] & 0xF) + + def write(self, path, palette): + w = png.Writer(self.width, self.height, palette=palette) + + with open(path, "wb") as f: + w.write_array(f, self.raster) + +class Component: + def __init__(self): + self.x = 0 + self.y = 0 + self.z = 0 + self.commands = [] + + @staticmethod + def from_bytes(data, sprite_data): + self = Component() + + commands_offset = int.from_bytes(data[0:4], byteorder="big") + commands_size = int.from_bytes(data[4:6], byteorder="big") # size in bytes, not length! + commands_data = sprite_data[commands_offset : commands_offset + commands_size] + self.commands = [int.from_bytes(d[0:2], byteorder="big") for d in iter_in_groups(commands_data, 2)] + + self.x = int.from_bytes(data[6:8], byteorder="big") + self.y = int.from_bytes(data[8:10], byteorder="big") + self.z = int.from_bytes(data[10:12], byteorder="big") + + return self + +class N64SegPaperMarioNpcSprites(N64Segment): + DEFAULT_SPRITE_NAMES = [f"{i:02X}" for i in range(0xEA)] + + def __init__(self, segment, next_segment, options): + super().__init__(segment, next_segment, options) + + if type(segment) is dict and "files" in segment: + self.files = segment["files"] + else: + self.files = DEFAULT_SPRITE_NAMES + + def split(self, rom_bytes, base_path): + out_dir = self.create_split_dir(base_path, "sprite/" + self.name) + + data = rom_bytes[self.rom_start:self.rom_end] + pos = 0 + + for i, file in enumerate(self.files): + if type(file) is dict: + sprite_name = file["name"] + else: + sprite_name = file + + self.log(f"Splitting sprite {sprite_name}...") + + sprite_dir = self.create_split_dir(out_dir, sprite_name) + + start = int.from_bytes(data[i * 4 : (i + 1) * 4], byteorder="big") + end = int.from_bytes(data[(i + 1) * 4 : (i + 2) * 4], byteorder="big") + + sprite_data = Yay0decompress.decompress_yay0(data[start:end]) + + """ + with open(sprite_dir / "raw.bin", "wb") as f: + f.write(sprite_data) + """ + + sprite = Sprite.from_bytes(sprite_data) + + if type(file) is dict: + sprite.image_names = file.get("frames", []) + sprite.palette_names = file.get("palettes", []) + sprite.animation_names = file.get("animations", []) + + sprite.write_to_dir(sprite_dir) + + def get_ld_files(self): + return [("sprite", self.name, ".data")] + + @staticmethod + def get_default_name(addr): + return "npc"