Merge branch 'master' into cornucopia

This commit is contained in:
Ethan Roseman 2020-11-22 10:07:20 -05:00
commit 4400966849
16 changed files with 1047 additions and 664 deletions

2
.gitignore vendored
View File

@ -18,8 +18,10 @@ settings.mk
/build/
/docs/doxygen/
/include/ld_addrs.h
/include/sprite/
# Assets
/bin
/img
/msg
/sprite

View File

@ -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"
],
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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),
},
};

View File

@ -2,7 +2,7 @@ default: all
all: Yay0compress
Yay0compress:
Yay0compress:
gcc Yay0compress.c -O3 -o Yay0compress
clean:

115
tools/compile_npc_sprite.py Executable file
View File

@ -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))

30
tools/compile_npc_sprites.py Executable file
View File

@ -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"))

View File

@ -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")

@ -1 +1 @@
Subproject commit 24aa8a9d284cbc170d691f20874171b018deb53e
Subproject commit 686be7cf19780b5dd619e0f51bb071f721b05b9a

0
tools/sort_symbol_addrs.py Normal file → Executable file
View File

View File

@ -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]

View File

@ -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"