mirror of
https://github.com/pmret/papermario.git
synced 2024-11-18 08:52:40 +01:00
Merge branch 'master' into cornucopia
This commit is contained in:
commit
2462200b48
22
.clang-tidy
22
.clang-tidy
@ -1,2 +1,20 @@
|
||||
Checks: '-*,clang-analyzer-core.*,clang-analyzer-deadcode.*,readability-*,-readability-magic-numbers,-readability-else-after-return,-readability-named-parameter,-readability-braces-around-statements,-readability-isolate-declaration,-readability-uppercase-literal-suffix,-readability-function-size,-readability-non-const-parameter'
|
||||
HeaderFilterRegex: '(src|include)\/.*\.h'
|
||||
Checks: '-*,clang-analyzer-core.*,clang-analyzer-deadcode.*,readability-*,-readability-magic-numbers,-readability-else-after-return,-readability-named-parameter,-readability-braces-around-statements,-readability-isolate-declaration,-readability-uppercase-literal-suffix,-readability-function-size,-readability-non-const-parameter,readability-identifier-naming'
|
||||
CheckOptions:
|
||||
- key: readability-identifier-naming.LocalVariableCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.ParameterCase
|
||||
value: camelBack
|
||||
|
||||
- key: readability-identifier-naming.TypedefCase
|
||||
value: CamelCase
|
||||
|
||||
# TODO: rename all unk_XXX members
|
||||
#- key: readability-identifier-naming.MemberCase
|
||||
# value: camelBack
|
||||
|
||||
# TODO: rename all D_XXXXXXXX symbols
|
||||
#- key: readability-identifier-naming.GlobalVariableCase
|
||||
# value: CamelCase
|
||||
#- key: readability-identifier-naming.GlobalVariablePrefix
|
||||
# value: g
|
||||
HeaderFilterRegex: '(src|include)\/[^P].*\.h' # ignore inclue/PR/
|
||||
|
2
Doxyfile
2
Doxyfile
@ -104,7 +104,7 @@ ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
PREDEFINED = DOXYGEN NON_MATCHING __attribute__((x))=
|
||||
EXPAND_AS_DEFINED = INCLUDE_ASM UNK_TYPE UNK_PTR UNK_RET UNK_FUN_ARG UNK_FUN_PTR UNK_ARGS
|
||||
EXPAND_AS_DEFINED = INCLUDE_ASM UNK_TYPE UNK_PTR UNK_RET UNK_FUN_ARG UNK_FUN_PTR UNK_ARGS M
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
|
||||
SEARCH_INCLUDES = YES
|
||||
|
38
Makefile
38
Makefile
@ -1,5 +1,8 @@
|
||||
SHELL=/bin/bash -o pipefail
|
||||
|
||||
MAKEFLAGS += --no-builtin-rules
|
||||
MAKEFLAGS += --no-builtin-variables
|
||||
|
||||
################ Target Executable and Sources ###############
|
||||
|
||||
# BUILD_DIR is location where all build artifacts are placed
|
||||
@ -8,9 +11,10 @@ BUILD_DIR = build
|
||||
SRC_DIRS := $(shell find src -type d)
|
||||
ASM_DIRS := asm asm/os
|
||||
INCLUDE_DIRS := include include/PR src
|
||||
DATA_DIRS := $(shell find bin -type d -not -name Yay0)
|
||||
DATA_DIRS := $(shell mkdir -p bin && find bin -type d -not -name Yay0)
|
||||
YAY0_DIRS := bin/Yay0
|
||||
ASSETS_FS_DIRS := assets/fs
|
||||
ASSETS_FS_BIN := $(BUILD_DIR)/assets/fs.bin
|
||||
|
||||
# Source code files
|
||||
C_FILES := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c))
|
||||
@ -23,10 +27,11 @@ YAY0_FILES := $(foreach dir,$(YAY0_DIRS),$(wildcard $(dir)/*.bin))
|
||||
ASSETS_FS_FILES := $(foreach dir,$(ASSETS_FS_DIRS),$(wildcard $(dir)/*.*))
|
||||
|
||||
# Object files
|
||||
O_FILES := $(foreach file,$(C_FILES),$(BUILD_DIR)/$(file:.c=.o)) \
|
||||
$(foreach file,$(S_FILES),$(BUILD_DIR)/$(file:.s=.o)) \
|
||||
$(foreach file,$(DATA_FILES),$(BUILD_DIR)/$(file:.bin=.o)) \
|
||||
$(foreach dir,$(ASSETS_FS_DIRS),$(BUILD_DIR)/$(dir).o) \
|
||||
ASSETS_FS_O := $(ASSETS_FS_BIN:.bin=.o)
|
||||
O_FILES := $(foreach file,$(C_FILES),$(BUILD_DIR)/$(file:.c=.c.o)) \
|
||||
$(foreach file,$(S_FILES),$(BUILD_DIR)/$(file:.s=.s.o)) \
|
||||
$(foreach file,$(DATA_FILES),$(BUILD_DIR)/$(file:.bin=.bin.o)) \
|
||||
$(ASSETS_FS_O) \
|
||||
$(foreach file,$(YAY0_FILES),$(BUILD_DIR)/$(file:.bin=.Yay0.o))
|
||||
|
||||
####################### Tools #########################
|
||||
@ -49,13 +54,11 @@ TARGET = papermario
|
||||
CPPFLAGS = -Iinclude -Isrc -D _LANGUAGE_C -ffreestanding -DF3DEX_GBI_2
|
||||
ASFLAGS = -EB -Iinclude -march=vr4300 -mtune=vr4300
|
||||
OLDASFLAGS = -EB -Iinclude -G 0
|
||||
CFLAGS = -O2 -quiet -G 0 -mcpu=vr4300 -mfix4300 -mips3 -mgp32 -mfp32
|
||||
CFLAGS = -O2 -quiet -G 0 -mcpu=vr4300 -mfix4300 -mips3 -mgp32 -mfp32 -Wimplicit -Wuninitialized -Wshadow
|
||||
LDFLAGS = -T undefined_syms.txt -T undefined_funcs.txt -T $(LD_SCRIPT) -Map $(BUILD_DIR)/papermario.map --no-check-sections
|
||||
|
||||
######################## Targets #############################
|
||||
|
||||
$(foreach dir,$(SRC_DIRS) $(ASM_DIRS) $(DATA_DIRS) $(ASSETS_FS_DIRS) ,$(shell mkdir -p build/$(dir)))
|
||||
|
||||
default: all
|
||||
|
||||
LD_SCRIPT = $(TARGET).ld
|
||||
@ -64,6 +67,11 @@ all: $(TARGET).ld $(BUILD_DIR) $(TARGET).z64 verify
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(TARGET).z64
|
||||
make $(BUILD_DIR)
|
||||
|
||||
clean-code:
|
||||
rm -rf $(BUILD_DIR)/src $(TARGET).z64
|
||||
make $(BUILD_DIR)
|
||||
|
||||
submodules:
|
||||
git submodule update --init --recursive
|
||||
@ -81,23 +89,25 @@ print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
@mkdir -p $(foreach dir,$(SRC_DIRS) $(ASM_DIRS) $(DATA_DIRS) $(ASSETS_FS_DIRS),build/$(dir))
|
||||
|
||||
$(BUILD_DIR)/%.o: %.s
|
||||
$(BUILD_DIR)/%.s.o: %.s
|
||||
$(AS) $(ASFLAGS) -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/%.o: %.c $(H_FILES)
|
||||
$(BUILD_DIR)/%.c.o: %.c $(H_FILES)
|
||||
cpp $(CPPFLAGS) $< | $(CC) $(CFLAGS) -o - | $(OLD_AS) $(OLDASFLAGS) - -o $@
|
||||
|
||||
$(BUILD_DIR)/%.o: %.bin
|
||||
$(BUILD_DIR)/%.bin.o: %.bin
|
||||
$(LD) -r -b binary -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/assets/fs/%: $(ASSETS_FS_FILES)
|
||||
$(BUILD_DIR)/assets/fs/%: assets/fs/%
|
||||
@rm -f $@
|
||||
$(TOOLS)/build_assets_fs.py $*
|
||||
|
||||
$(BUILD_DIR)/assets/fs.bin: assets/fs.json $(TOOLS)/build_assets_fs.py $(foreach file,$(ASSETS_FS_FILES),build/$(file))
|
||||
$(TOOLS)/build_assets_fs.py
|
||||
|
||||
$(BUILD_DIR)/assets/fs.o: $(BUILD_DIR)/assets/fs.bin
|
||||
$(ASSETS_FS_O): $(ASSETS_FS_BIN)
|
||||
$(LD) -r -b binary -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/%.Yay0.o: %.bin
|
||||
@ -122,4 +132,4 @@ $(TARGET).z64: $(BUILD_DIR)/$(TARGET).bin
|
||||
verify: $(TARGET).z64
|
||||
sha1sum -c checksum.sha1
|
||||
|
||||
.PHONY: all clean default
|
||||
.PHONY: all clean default $(BUILD_DIR)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
body, table, div, p, dl { font: inherit }
|
||||
|
||||
body, table, div, p, dl, p.reference, p.definition, .memberdecls .header {
|
||||
body, table, div, p, dl, p.reference, p.definition, .memberdecls .header, .mdescRight {
|
||||
color: #121212;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
|
||||
}
|
||||
@ -48,7 +48,7 @@ body {
|
||||
flex-direction: row;
|
||||
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
max-width: 1100px;
|
||||
min-height: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@ -92,9 +92,7 @@ table {
|
||||
|
||||
#details + .groupheader,
|
||||
.definition,
|
||||
.memtitle,
|
||||
.contents > p:first-child > a:first-child /* "Go to the source code of this file." */
|
||||
{ display: none }
|
||||
.memtitle { display: none }
|
||||
|
||||
table.memname * {
|
||||
display: inline;
|
||||
@ -125,19 +123,22 @@ code {
|
||||
display: block !important;
|
||||
border-left: 1px solid #eee;
|
||||
padding-left: 1em;
|
||||
margin: 1em 0;
|
||||
margin: 1.5em 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.memproto {
|
||||
padding: 8px;
|
||||
margin-bottom: .5em;
|
||||
box-shadow: none;
|
||||
text-shadow: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.memdoc p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.memname {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
@ -115,6 +115,8 @@ typedef struct Npc {
|
||||
/* 0x0AB */ char unk_AB[661];
|
||||
} Npc; // size = 0x340
|
||||
|
||||
typedef Npc* NpcList[MAX_NPCS];
|
||||
|
||||
typedef struct PlayerData {
|
||||
/* 0x000 */ s8 bootsLevel;
|
||||
/* 0x001 */ s8 hammerLevel;
|
||||
@ -204,6 +206,8 @@ typedef struct Trigger {
|
||||
/* 0x34 */ ScriptID runningScriptID;
|
||||
} Trigger; // size = 0x38
|
||||
|
||||
typedef Trigger* TriggerList[MAX_TRIGGERS];
|
||||
|
||||
typedef struct ScriptInstance {
|
||||
/* 0x000 */ u8 state;
|
||||
/* 0x001 */ u8 currentArgc;
|
||||
@ -281,6 +285,10 @@ typedef struct Entity {
|
||||
/* 0xB1 */ char unk_B1[71];
|
||||
} Entity; // size = 0xF8
|
||||
|
||||
typedef Entity* EntityList[MAX_ENTITIES];
|
||||
|
||||
typedef UNK_TYPE* DynamicEntityList[MAX_DYNAMIC_ENTITIES];
|
||||
|
||||
typedef struct StaticEntityData {
|
||||
/* 0x00 */ s16 flags;
|
||||
/* 0x02 */ s16 argSize;
|
||||
@ -374,6 +382,7 @@ typedef struct UiStatus {
|
||||
/* 0x68 */ s32 iconIndex13;
|
||||
/* 0x6C */ s8 unk_6C[4];
|
||||
} UiStatus; // size = 0x70
|
||||
|
||||
typedef struct Collider {
|
||||
/* 0x00 */ s32 flags;
|
||||
/* 0x04 */ s16 nextSibling;
|
||||
@ -682,6 +691,8 @@ typedef struct Model {
|
||||
/* 0xAA */ char unk_AA[6];
|
||||
} Model; // size = 0xB0
|
||||
|
||||
typedef Model* ModelList[MAX_MODELS];
|
||||
|
||||
typedef struct AnimatedMesh {
|
||||
/* 0x000 */ s32 flags;
|
||||
/* 0x004 */ u8 renderMode;
|
||||
@ -931,6 +942,8 @@ typedef struct Shadow {
|
||||
/* 0x28 */ char unk_28[80];
|
||||
} Shadow; // size = 0x78
|
||||
|
||||
typedef Shadow* ShadowList[MAX_SHADOWS];
|
||||
|
||||
typedef struct PushBlockGrid {
|
||||
/* 0x00 */ s8* cells;
|
||||
/* 0x04 */ u8 numCellsX;
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#define ASSERT(condition) if (!(condition)) { while (1) {} }
|
||||
#define PANIC() ASSERT(0)
|
||||
#define STATIC_ASSERT(condition) enum { static_assert_fail = 1/(!!(condition)) } // Causes division by zero ("not integer constant") if false
|
||||
|
||||
#define GAME_STATUS (*gGameStatusPtr)
|
||||
#define PLAYER_STATUS (&gPlayerStatus)
|
||||
@ -23,7 +24,13 @@
|
||||
#define MAX_MAPVARS 16
|
||||
#define MAX_MAPFLAGS 3
|
||||
|
||||
#define MAX_MODELS 256
|
||||
#define MAX_SCRIPTS 128
|
||||
#define MAX_NPCS 64
|
||||
#define MAX_TRIGGERS 64
|
||||
#define MAX_SHADOWS 60
|
||||
#define MAX_ENTITIES 30
|
||||
#define MAX_DYNAMIC_ENTITIES 16
|
||||
|
||||
//NOTE: SCRIPT_ALLOC is probably not quite correct, but this is the closest thing to matching for the functions its used in. Needs more work.
|
||||
#define SCRIPT_ALLOC(new, index) \
|
||||
@ -50,4 +57,7 @@
|
||||
// Fixed-point short literal
|
||||
#define F16(f) (s16)(f * 327.67f)
|
||||
|
||||
#define _NAMESPACE(x, y) x ## _ ## y
|
||||
#define NAMESPACE(x, y) _NAMESPACE(x, y)
|
||||
|
||||
#endif
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
// TODO: consider moving Npc here
|
||||
|
||||
#define M(sym) NAMESPACE(MAP_NAME, sym)
|
||||
|
||||
#define ENTRY_COUNT(entryList) (sizeof(entryList) / sizeof(Vec4f))
|
||||
|
||||
typedef Vec4f EntryList[];
|
||||
@ -61,12 +63,25 @@ typedef struct ItemDrop {
|
||||
/* 0x04 */ s16 unk_08;
|
||||
} ItemDrop; // size = 0x06
|
||||
|
||||
/// @brief Describes heart/flower drop chances after defeating an Npc in the overworld.
|
||||
///
|
||||
/// The algorithm for calculating the number of hearts/flowers from a StatDrop is:
|
||||
/// - If current HP/FP > cutoff, drop 0.
|
||||
/// - Roll generalChance. If it fails, drop 0.
|
||||
/// - Roll chancePerAttempt attempts times. For each success, drop a heart/flower.
|
||||
///
|
||||
/// StaticNpc holds a table of StatDrops for each stat (hearts, flowers). All are checked together
|
||||
/// and the number of hearts/flowers to drop is the total number of successful attempts for each stat.
|
||||
///
|
||||
/// Each heart/flower is worth 1 HP and 1 FP respectively, if picked up.
|
||||
///
|
||||
/// cutoff, generalChance, and chancePerAttempt are short fixed-point percentage values.
|
||||
/// That is, `F16(0)` is a 0% chance and `F16(100)` is a 100% chance.
|
||||
typedef struct StatDrop {
|
||||
// NOTE: these %s are F16
|
||||
/* 0x00 */ s16 hpCutoff; // % of max HP/FP
|
||||
/* 0x02 */ s16 generalChance; // %
|
||||
/* 0x04 */ s16 attempts;
|
||||
/* 0x06 */ s16 chancePerAttempt; // %
|
||||
/* 0x00 */ s16 cutoff; ///< % of max HP/FP. If current HP/FP > cutoff, no hearts/flowers can be dropped.
|
||||
/* 0x02 */ s16 generalChance; ///< % chance for any hearts/flowers to be dropped at all from this StatDrop.
|
||||
/* 0x04 */ s16 attempts; ///< Maximum number of hearts/flowers that can be dropped from this StatDrop.
|
||||
/* 0x06 */ s16 chancePerAttempt; ///< % chance for a single heart/flower to be dropped from each attempt.
|
||||
} StatDrop; // size = 0x08
|
||||
|
||||
#define NO_DROPS { F16(100), F16(0), 0, F16(0) }
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "common_structs.h"
|
||||
#include "si.h"
|
||||
|
||||
typedef Bytecode Script[];
|
||||
typedef Bytecode Script[0];
|
||||
|
||||
ApiStatus FadeBackgroundToBlack(ScriptInstance* script, s32 isInitialCall);
|
||||
ApiStatus UnfadeBackgroundFromBlack(ScriptInstance* script, s32 isInitialCall);
|
||||
|
@ -38,30 +38,31 @@ extern s32 gScriptIndexList[MAX_SCRIPTS];
|
||||
|
||||
extern s32 gMoveScriptTable[][4];
|
||||
|
||||
extern Model* gWorldModelList[256];
|
||||
extern Model* gBattleModelList[256];
|
||||
extern Model** gCurrentModelListPtr[256];
|
||||
extern ModelList gWorldModelList;
|
||||
extern ModelList gBattleModelList;
|
||||
extern ModelList* gCurrentModelListPtr;
|
||||
|
||||
// TODO: potentially a display list, figure this out
|
||||
extern u32* gWorldModelSpecialDls[32];
|
||||
extern u32* gBattleModelSpecialDls[32];
|
||||
extern u32** gCurrentModelSpecialDlsPtr[32];
|
||||
|
||||
extern Entity* gWorldEntityList[30];
|
||||
extern Entity* gBattleEntityList[30];
|
||||
extern Entity** gCurrentEntityListPtr[30];
|
||||
extern EntityList gWorldEntityList;
|
||||
extern EntityList gBattleEntityList;
|
||||
extern EntityList* gCurrentEntityListPtr;
|
||||
extern s32 gLastCreatedEntityIndex;
|
||||
|
||||
extern UNK_TYPE* gWorldDynamicEntityList[16];
|
||||
extern UNK_TYPE* gBattleDynamicEntityList[16];
|
||||
extern UNK_TYPE*** gCurrentDynamicEntityListPtr;
|
||||
extern DynamicEntityList gWorldDynamicEntityList;
|
||||
extern DynamicEntityList gBattleDynamicEntityList;
|
||||
extern DynamicEntityList* gCurrentDynamicEntityListPtr;
|
||||
|
||||
extern Npc* gWorldNpcList[64];
|
||||
extern Npc* gBattleNpcList[64];
|
||||
extern Npc** gCurrentNpcListPtr[64];
|
||||
extern NpcList gWorldNpcList;
|
||||
extern NpcList gBattleNpcList;
|
||||
extern NpcList* gCurrentNpcListPtr;
|
||||
|
||||
extern Shadow* gWorldShadowList[60];
|
||||
extern Shadow* gBattleShadowList[60];
|
||||
extern Shadow** gCurrentShadowListPtr[60];
|
||||
extern ShadowList gWorldShadowList;
|
||||
extern ShadowList gBattleShadowList;
|
||||
extern ShadowList* gCurrentShadowListPtr;
|
||||
|
||||
extern Camera gCameras[4];
|
||||
extern s32 gCurrentCameraID;
|
||||
@ -89,9 +90,9 @@ extern PrintContext* D_802DB268;
|
||||
|
||||
// Triggers
|
||||
extern s16 gTriggerCount;
|
||||
extern Trigger* gTriggerList1[64];
|
||||
extern Trigger* gTriggerList2[64];
|
||||
extern Trigger** gCurrentTriggerListPtr[64];
|
||||
extern TriggerList gTriggerList1;
|
||||
extern TriggerList gTriggerList2;
|
||||
extern TriggerList* gCurrentTriggerListPtr;
|
||||
|
||||
// Map transition data. Should probably be a struct
|
||||
extern u16 gMapTransitionAlpha;
|
||||
|
@ -34,24 +34,24 @@ Shadow* get_shadow_by_index(s32 index) {
|
||||
return (*gCurrentShadowListPtr)[index & 0xFFF];
|
||||
}
|
||||
|
||||
Entity** get_entity_list(void) {
|
||||
Entity** ret;
|
||||
EntityList* get_entity_list(void) {
|
||||
EntityList* ret;
|
||||
|
||||
if (!GAME_STATUS->isBattle) {
|
||||
ret = gWorldEntityList;
|
||||
ret = &gWorldEntityList;
|
||||
} else {
|
||||
ret = gBattleEntityList;
|
||||
ret = &gBattleEntityList;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Shadow** get_shadow_list(void) {
|
||||
Shadow** ret;
|
||||
ShadowList* get_shadow_list(void) {
|
||||
ShadowList* ret;
|
||||
|
||||
if (!GAME_STATUS->isBattle) {
|
||||
ret = gWorldShadowList;
|
||||
ret = &gWorldShadowList;
|
||||
} else {
|
||||
ret = gBattleShadowList;
|
||||
ret = &gBattleShadowList;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ INCLUDE_ASM(s32, "code_dbd70_len_700", clear_trigger_data);
|
||||
|
||||
void init_trigger_list(void) {
|
||||
if (!GAME_STATUS->isBattle) {
|
||||
*gCurrentTriggerListPtr = gTriggerList1;
|
||||
gCurrentTriggerListPtr = &gTriggerList1;
|
||||
} else {
|
||||
*gCurrentTriggerListPtr = gTriggerList2;
|
||||
gCurrentTriggerListPtr = &gTriggerList2;
|
||||
}
|
||||
|
||||
gTriggerCount = 0;
|
||||
@ -22,16 +22,17 @@ INCLUDE_ASM(s32, "code_dbd70_len_700", update_triggers);
|
||||
|
||||
void delete_trigger(Trigger* toDelete) {
|
||||
s32 i;
|
||||
TriggerList** currentTriggerListPtr = &gCurrentTriggerListPtr;
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(gCurrentTriggerListPtr); i++) {
|
||||
if ((*gCurrentTriggerListPtr)[i] == toDelete) {
|
||||
for (i = 0; i < MAX_TRIGGERS; i++) {
|
||||
if ((**currentTriggerListPtr)[i] == toDelete) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < ARRAY_COUNT(gCurrentTriggerListPtr)) {
|
||||
heap_free((*gCurrentTriggerListPtr)[i]);
|
||||
(*gCurrentTriggerListPtr)[i] = NULL;
|
||||
if (i < MAX_TRIGGERS) {
|
||||
heap_free((**currentTriggerListPtr)[i]);
|
||||
(**currentTriggerListPtr)[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +49,7 @@ s32 func_80145CE8(s32 arg0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(gCurrentTriggerListPtr); i++) {
|
||||
for (i = 0; i < MAX_TRIGGERS; i++) {
|
||||
Trigger* trigger = (*gCurrentTriggerListPtr)[i];
|
||||
|
||||
if ((trigger != NULL) &&
|
||||
|
@ -1,37 +1,33 @@
|
||||
#include "kmr_12.h"
|
||||
|
||||
static Script make_entities;
|
||||
static Script read_west_sign;
|
||||
static NpcGroupList npc_groups;
|
||||
Script M(ExitWest) = EXIT_WALK_SCRIPT(60, 0, "kmr_07", 1);
|
||||
Script M(ExitEast) = EXIT_WALK_SCRIPT(60, 1, "kmr_11", 0);
|
||||
|
||||
static Script exit_west = EXIT_WALK_SCRIPT(60, 0, "kmr_07", 1);
|
||||
static Script exit_east = EXIT_WALK_SCRIPT(60, 1, "kmr_11", 0);
|
||||
|
||||
static Script bind_exits = {
|
||||
SI_BIND(exit_west, TriggerFlag_FLOOR_ABOVE, 0 /* deili1 */, NULL),
|
||||
SI_BIND(exit_east, TriggerFlag_FLOOR_ABOVE, 3 /* deili2 */, NULL),
|
||||
Script M(BindExits) = {
|
||||
SI_BIND(M(ExitWest), TriggerFlag_FLOOR_ABOVE, 0 /* deili1 */, NULL),
|
||||
SI_BIND(M(ExitEast), TriggerFlag_FLOOR_ABOVE, 3 /* deili2 */, NULL),
|
||||
SI_RETURN(),
|
||||
SI_END(),
|
||||
};
|
||||
|
||||
Script kmr_12_main = {
|
||||
Script M(Main) = {
|
||||
SI_SET(SI_SAVE_VAR(425), 31),
|
||||
SI_CALL(SetSpriteShading, -1),
|
||||
SI_CALL(SetCamPerspective, 0, 3, 25, 16, 4096),
|
||||
SI_CALL(SetCamBGColor, 0, 0, 0, 0),
|
||||
SI_CALL(SetCamEnabled, 0, 1),
|
||||
SI_CALL(MakeNpcs, 0, npc_groups),
|
||||
SI_EXEC_WAIT(make_entities),
|
||||
SI_EXEC(kmr_12_play_music),
|
||||
SI_SET(SI_VAR(0), bind_exits),
|
||||
SI_CALL(MakeNpcs, 0, M(npcGroupList)),
|
||||
SI_EXEC_WAIT(M(MakeEntities)),
|
||||
SI_EXEC(M(PlayMusic)),
|
||||
SI_SET(SI_VAR(0), M(BindExits)),
|
||||
SI_EXEC(EnterWalk),
|
||||
SI_WAIT_FRAMES(1),
|
||||
SI_BIND(read_west_sign, TriggerFlag_WALL_INTERACT, 10, NULL),
|
||||
SI_BIND(M(ReadWestSign), TriggerFlag_WALL_INTERACT, 10, NULL),
|
||||
SI_RETURN(),
|
||||
SI_END(),
|
||||
};
|
||||
|
||||
static NpcAISettings goomba_ai_settings = {
|
||||
NpcAISettings M(goombaAISettings) = {
|
||||
.moveSpeed = 1.5f,
|
||||
.moveTime = 30,
|
||||
.waitTime = 30,
|
||||
@ -46,16 +42,16 @@ static NpcAISettings goomba_ai_settings = {
|
||||
.unk_2C = TRUE,
|
||||
};
|
||||
|
||||
static Script goomba_ai = {
|
||||
SI_CALL(DoBasicAI, &goomba_ai_settings),
|
||||
Script M(GoombaAI) = {
|
||||
SI_CALL(DoBasicAI, &M(goombaAISettings)),
|
||||
SI_RETURN(),
|
||||
SI_END(),
|
||||
};
|
||||
|
||||
static NpcSettings goomba_npc_settings = {
|
||||
NpcSettings M(goombaNpcSettings) = {
|
||||
.height = 20,
|
||||
.radius = 23,
|
||||
.ai = &goomba_ai,
|
||||
.ai = &M(GoombaAI),
|
||||
.onHit = EnemyNpcHit,
|
||||
.onDefeat = EnemyNpcDefeat,
|
||||
.level = 5,
|
||||
@ -63,7 +59,7 @@ static NpcSettings goomba_npc_settings = {
|
||||
|
||||
// *INDENT-OFF*
|
||||
/// @bug The RETURN command is after the END command, so this script will never terminate.
|
||||
static Script read_west_sign = {
|
||||
Script M(ReadWestSign) = {
|
||||
SI_GROUP(0),
|
||||
|
||||
// "Eat a Mushroom to regain your energy!"
|
||||
@ -73,7 +69,7 @@ static Script read_west_sign = {
|
||||
SI_RESUME_GROUP(1),
|
||||
|
||||
SI_SET(SI_FLAG(0), FALSE),
|
||||
SI_CALL(kmr_12_get_goomba_ref),
|
||||
SI_CALL(GetGoomba),
|
||||
SI_IF_NE(SI_VAR(0), FALSE),
|
||||
SI_CALL(GetNpcVar, NpcId_GOOMBA, 0, SI_VAR(0)),
|
||||
SI_IF_EQ(SI_VAR(0), FALSE),
|
||||
@ -92,7 +88,7 @@ static Script read_west_sign = {
|
||||
SI_RETURN(),
|
||||
};
|
||||
|
||||
static Script goomba_idle = {
|
||||
Script M(GoombaIdle) = {
|
||||
SI_WAIT_FRAMES(1),
|
||||
|
||||
SI_CALL(SetSelfVar, 0, FALSE),
|
||||
@ -142,28 +138,28 @@ static Script goomba_idle = {
|
||||
SI_CALL(SetSelfEnemyFlagBits, 0x40000000, TRUE),
|
||||
|
||||
// We're done jumping off; the player can read the sign again
|
||||
SI_BIND(read_west_sign, TriggerFlag_WALL_INTERACT, 10, NULL),
|
||||
SI_BIND(M(ReadWestSign), TriggerFlag_WALL_INTERACT, 10, NULL),
|
||||
|
||||
// Behave like a normal enemy from now on
|
||||
SI_CALL(BindNpcAI, NpcId_SELF, &goomba_ai),
|
||||
SI_CALL(BindNpcAI, NpcId_SELF, &M(GoombaAI)),
|
||||
|
||||
SI_RETURN(),
|
||||
SI_END(),
|
||||
};
|
||||
|
||||
static Script goomba_init = {
|
||||
SI_CALL(BindNpcIdle, NpcId_SELF, &goomba_idle),
|
||||
Script M(GoombaInit) = {
|
||||
SI_CALL(BindNpcIdle, NpcId_SELF, &M(GoombaIdle)),
|
||||
SI_RETURN(),
|
||||
SI_END(),
|
||||
};
|
||||
// *INDENT-ON*
|
||||
|
||||
static StaticNpc goomba_npc = {
|
||||
StaticNpc M(goombaNpc) = {
|
||||
.id = NpcId_GOOMBA,
|
||||
.settings = &goomba_npc_settings,
|
||||
.settings = &M(goombaNpcSettings),
|
||||
.pos = { -33.0f, 30.0f, -25.0f },
|
||||
.flags = 0x00000C00,
|
||||
.init = goomba_init,
|
||||
.init = M(GoombaInit),
|
||||
.yaw = 90,
|
||||
.dropFlags = 0x80,
|
||||
.itemDropChance = 5,
|
||||
@ -204,13 +200,13 @@ static StaticNpc goomba_npc = {
|
||||
},
|
||||
};
|
||||
|
||||
static NpcGroupList npc_groups = {
|
||||
NPC_GROUP(goomba_npc, FORMATION_ID(1, 0, 3)),
|
||||
NpcGroupList M(npcGroupList) = {
|
||||
NPC_GROUP(M(goombaNpc), FORMATION_ID(1, 0, 3)),
|
||||
NPC_GROUP_LIST_END(),
|
||||
};
|
||||
|
||||
// *INDENT-OFF*
|
||||
static Script read_east_sign = {
|
||||
Script M(ReadEastSign) = {
|
||||
SI_CALL(func_800441F0, SI_VAR(0)),
|
||||
SI_IF_EQ(SI_VAR(0), 1),
|
||||
SI_RETURN(),
|
||||
@ -228,9 +224,9 @@ static Script read_east_sign = {
|
||||
SI_END(),
|
||||
};
|
||||
|
||||
static Script make_entities = {
|
||||
Script M(MakeEntities) = {
|
||||
SI_CALL(MakeEntity, 0x802EAFDC, 436, 0, -42, 0, 0x80000000),
|
||||
SI_CALL(AssignScript, &read_east_sign),
|
||||
SI_CALL(AssignScript, &M(ReadEastSign)),
|
||||
|
||||
SI_RETURN(),
|
||||
SI_END(),
|
||||
|
@ -1,25 +1,25 @@
|
||||
#include "kmr_12.h"
|
||||
|
||||
static EntryList entryList = {
|
||||
EntryList M(entryList) = {
|
||||
{ -126.0f, 0.0f, 12.0f, 90.0f }, // west, towards Red/Blue Goomba miniboss room
|
||||
{ 471.0f, 0.0f, 12.0f, 270.0f }, // east, towards Goomba King's Fortress
|
||||
};
|
||||
|
||||
MapConfig config = {
|
||||
.main = kmr_12_main,
|
||||
.entryList = entryList,
|
||||
.entryCount = ENTRY_COUNT(entryList),
|
||||
MapConfig M(config) = {
|
||||
.main = M(Main),
|
||||
.entryList = M(entryList),
|
||||
.entryCount = ENTRY_COUNT(M(entryList)),
|
||||
.background = &gBackgroundImage,
|
||||
.tattle = MessageID_TATTLE_KMR_12,
|
||||
};
|
||||
|
||||
Script kmr_12_play_music = {
|
||||
Script M(PlayMusic) = {
|
||||
SI_CALL(SetMusicTrack, 0, Song_PLEASANT_PATH, 0, 8),
|
||||
SI_RETURN(),
|
||||
SI_END(),
|
||||
};
|
||||
|
||||
ApiStatus kmr_12_get_goomba_ref(ScriptInstance* script, s32 isInitialCall) {
|
||||
ApiStatus GetGoomba(ScriptInstance* script, s32 isInitialCall) {
|
||||
script->varTable[0] = get_enemy_safe(NpcId_GOOMBA);
|
||||
return ApiStatus_DONE2;
|
||||
}
|
||||
|
@ -1,8 +1,16 @@
|
||||
#include "common.h"
|
||||
#include "map.h"
|
||||
|
||||
#define MAP_NAME kmr_12
|
||||
|
||||
#define NpcId_GOOMBA 0
|
||||
|
||||
Script kmr_12_main;
|
||||
Script kmr_12_play_music;
|
||||
ApiStatus kmr_12_get_goomba_ref(ScriptInstance* script, s32 isInitialCall);
|
||||
#define GetGoomba M(GetGoomba)
|
||||
ApiStatus GetGoomba(ScriptInstance* script, s32 isInitialCall);
|
||||
|
||||
Script M(Main);
|
||||
Script M(PlayMusic);
|
||||
Script M(MakeEntities);
|
||||
Script M(ReadWestSign);
|
||||
|
||||
NpcGroupList M(npcGroupList);
|
||||
|
188
tools/disasm_map.py
Executable file
188
tools/disasm_map.py
Executable file
@ -0,0 +1,188 @@
|
||||
#! /usr/bin/python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import yaml
|
||||
from struct import unpack
|
||||
from disasm_script import disassemble as disassemble_script
|
||||
|
||||
def disassemble(bytes, offset, midx, symbol_map = {}, map_name = "map"):
|
||||
out = ""
|
||||
found_data = False
|
||||
|
||||
while len(midx) > 0:
|
||||
struct = midx.pop(0)
|
||||
name = struct["name"]
|
||||
if name == "Script_Main": name = f"M(Main)"
|
||||
|
||||
#print(f"{offset:X} ({name}, start = {struct['start']:X}, len = {struct['length']:X})")
|
||||
|
||||
if struct["start"] == offset:
|
||||
found_data = True
|
||||
|
||||
if struct["start"] != offset:
|
||||
# end of data / padding
|
||||
break
|
||||
|
||||
# format struct
|
||||
if struct["type"].startswith("Script"):
|
||||
out += disassemble_script(bytes, f"M({name})", symbol_map)
|
||||
elif struct["type"] == "Padding":
|
||||
# nops at end of file
|
||||
bytes.seek(offset % 4, 1)
|
||||
return out
|
||||
elif struct["type"] == "EntryList":
|
||||
out += f"EntryList M(entryList) = {{"
|
||||
for i in range(0, struct["length"], 4 * 4):
|
||||
x,y,z,yaw = unpack(">ffff", bytes.read(4 * 4))
|
||||
out += f"\n {{ {x}f, {y}f, {z}f, {yaw}f }},"
|
||||
out += f"\n}};\n"
|
||||
elif struct["type"] == "Header":
|
||||
out += f"MapConfig M(config) = {{\n"
|
||||
|
||||
bytes.read(0x10)
|
||||
|
||||
main,entry_list,entry_count = unpack(">IIi", bytes.read(4 * 3))
|
||||
out += f" .main = M(Main)\n"
|
||||
out += f" .entryList = M(entryList)\n"
|
||||
out += f" .entryCount = {entry_count}, // prefer ENTRY_COUNT(M(entryList)) if it matches\n"
|
||||
|
||||
bytes.read(0x1C)
|
||||
|
||||
bg,tattle = unpack(">II", bytes.read(4 * 2))
|
||||
out += f" .background = {'&gBackgroundImage' if bg == 0x80200000 else 'NULL'},\n"
|
||||
out += f" .tattle = {tattle:X},\n"
|
||||
|
||||
out += f"}};\n"
|
||||
else: # unknown type of struct
|
||||
out += f"s32 M({name})[] = {{"
|
||||
for i in range(0, struct["length"], 4):
|
||||
if (i % 0x20) == 0:
|
||||
out += f"\n "
|
||||
|
||||
word = int.from_bytes(bytes.read(4), byteorder="big")
|
||||
|
||||
if word in symbol_map:
|
||||
out += f" M({symbol_map[word]}),"
|
||||
else:
|
||||
out += f" 0x{word:08X},"
|
||||
|
||||
out += f"\n}};\n"
|
||||
|
||||
out += "\n"
|
||||
elif found_data:
|
||||
if struct["type"] != "Padding":
|
||||
# put struct back on list
|
||||
midx.insert(0, struct)
|
||||
|
||||
# nops at end of file
|
||||
bytes.seek(offset % 4, 1)
|
||||
|
||||
return out
|
||||
|
||||
if struct["type"] != "Function" and not struct["type"] == "Padding" and not (struct["type"] == "Missing" and not found_data):
|
||||
offset += struct["length"]
|
||||
|
||||
# end of data
|
||||
return out
|
||||
|
||||
def parse_midx(file, prefix = ""):
|
||||
structs = []
|
||||
|
||||
for line in file.readlines():
|
||||
s = line.split("#")
|
||||
if len(s) == 5:
|
||||
if s[0] == "$Start": continue
|
||||
if s[0] == "$End": continue
|
||||
structs.append({
|
||||
"name": prefix + name_struct(s[0]),
|
||||
"type": s[1],
|
||||
"start": int(s[2], 16),
|
||||
"vaddr": int(s[3], 16),
|
||||
"length": int(s[4], 16),
|
||||
"end": int(s[2], 16) + int(s[4], 16),
|
||||
})
|
||||
elif "Missing" in s:
|
||||
start = int(s[1], 16)
|
||||
end = int(s[2], 16)
|
||||
vaddr = start + 0x80240000
|
||||
structs.append({
|
||||
"name": f"{prefix}unk_missing_{vaddr:X}",
|
||||
"type": "Missing",
|
||||
"start": start,
|
||||
"vaddr": vaddr,
|
||||
"length": end - start,
|
||||
"end": end,
|
||||
})
|
||||
elif "Padding" in s:
|
||||
start = int(s[1], 16)
|
||||
end = int(s[2], 16)
|
||||
vaddr = start + 0x80240000
|
||||
structs.append({
|
||||
"name": f"{prefix}__padding__",
|
||||
"type": "Padding",
|
||||
"start": start,
|
||||
"vaddr": vaddr,
|
||||
"length": end - start,
|
||||
"end": end,
|
||||
})
|
||||
|
||||
structs.sort(key=lambda s: s["start"])
|
||||
return structs
|
||||
|
||||
def name_struct(s):
|
||||
s = s[1:].replace("???", "unk")
|
||||
|
||||
# use ThisCase for scripts
|
||||
if s.startswith("$Script"):
|
||||
return s[0].upper() + s[1:]
|
||||
|
||||
return s[0].lower() + s[1:]
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) == 1:
|
||||
print("usage: ./disasm_map.py <file.midx>")
|
||||
print("Converts split map data into C files using a .midx file from Star Rod.")
|
||||
exit()
|
||||
|
||||
map_name = os.path.splitext(os.path.basename(sys.argv[1]))[0]
|
||||
area_name = "area_" + map_name.split("_")[0]
|
||||
if len(area_name) > 8:
|
||||
area_name = area_name[:8]
|
||||
|
||||
with open(sys.argv[1], "r") as f:
|
||||
midx = parse_midx(f)
|
||||
|
||||
symbol_map = {}
|
||||
for struct in midx:
|
||||
symbol_map[struct["vaddr"]] = struct["name"]
|
||||
|
||||
bin_dir = f"bin/world/{area_name}/{map_name}"
|
||||
src_dir = f"src/world/{area_name}/{map_name}"
|
||||
|
||||
splits = []
|
||||
rom_start = 0
|
||||
with open(os.path.join(os.path.dirname(__file__), "splat.yaml")) as splat:
|
||||
splat = yaml.safe_load(splat)
|
||||
|
||||
for segment in splat["segments"]:
|
||||
if type(segment) == dict and segment.get("name") == f"world/{area_name}/{map_name}/":
|
||||
rom_start = segment.get("start", 0)
|
||||
splits = segment.get("files", [])
|
||||
continue
|
||||
if len(splits) == 0:
|
||||
print(f"unable to find {map_name} in splat.yaml")
|
||||
exit(1)
|
||||
|
||||
# advance to the EntryList (start of data)
|
||||
while midx[0]["type"] != "EntryList":
|
||||
midx.pop(0)
|
||||
|
||||
for split in splits:
|
||||
rom_addr = split[0]
|
||||
filetype = split[1]
|
||||
|
||||
if filetype == "bin":
|
||||
with open(f"{bin_dir}/{rom_addr:X}.bin", "rb") as bytes:
|
||||
print(f"// {rom_addr:X}")
|
||||
print(disassemble(bytes, rom_addr - rom_start, midx, symbol_map, map_name))
|
@ -42,14 +42,11 @@ def star_rod_lib():
|
||||
|
||||
return _star_rod_lib
|
||||
|
||||
def addr_ref(addr):
|
||||
return star_rod_lib().get(addr, f"0x{addr:08X}")
|
||||
|
||||
def disassemble(bytes, indent = 0, script_name = "script"):
|
||||
def disassemble(bytes, script_name = "script", symbol_map = {}):
|
||||
out = ""
|
||||
prefix = ""
|
||||
|
||||
indent += 1
|
||||
indent = 1
|
||||
indent_used = False
|
||||
|
||||
def write_line(line):
|
||||
@ -65,6 +62,9 @@ def disassemble(bytes, indent = 0, script_name = "script"):
|
||||
prefix += "\n"
|
||||
|
||||
def var(arg):
|
||||
if arg in symbol_map:
|
||||
return symbol_map[arg]
|
||||
|
||||
v = arg - 2**32 # convert to s32
|
||||
if v > -250000000:
|
||||
if v <= -220000000: return f"SI_FIXED({(v + 230000000) / 1024}f)"
|
||||
@ -86,6 +86,11 @@ def disassemble(bytes, indent = 0, script_name = "script"):
|
||||
else:
|
||||
return f"{arg}"
|
||||
|
||||
def addr_ref(addr):
|
||||
if addr in symbol_map:
|
||||
return symbol_map[addr]
|
||||
return star_rod_lib().get(addr, f"0x{addr:08X}")
|
||||
|
||||
def trigger(trigger):
|
||||
if trigger == 0x00000080: trigger = "TriggerFlag_FLOOR_TOUCH"
|
||||
if trigger == 0x00800000: trigger = "TriggerFlag_FLOOR_ABOVE"
|
||||
@ -107,6 +112,10 @@ def disassemble(bytes, indent = 0, script_name = "script"):
|
||||
while True:
|
||||
opcode = read_word()
|
||||
argc = read_word()
|
||||
|
||||
if opcode > 0xFF or argc > 0xFF:
|
||||
return f"/* malformed script: {script_name} */\n"
|
||||
|
||||
argv = []
|
||||
for i in range(0, argc):
|
||||
argv.append(read_word())
|
||||
@ -167,7 +176,7 @@ def disassemble(bytes, indent = 0, script_name = "script"):
|
||||
write_line(f"SI_END_IF(),")
|
||||
elif opcode == 0x14:
|
||||
write_line(f"SI_SWITCH({var(argv[0])}),")
|
||||
indent += 1
|
||||
indent += 2
|
||||
elif opcode == 0x15:
|
||||
write_line(f"SI_SWITCH_CONST(0x{argv[0]:X}),")
|
||||
indent += 2
|
||||
@ -197,7 +206,7 @@ def disassemble(bytes, indent = 0, script_name = "script"):
|
||||
indent += 1
|
||||
elif opcode == 0x1C:
|
||||
indent -= 1
|
||||
write_line(f"SI_CASE_DEFAULT({var(argv[0])}),")
|
||||
write_line(f"SI_CASE_DEFAULT(),")
|
||||
indent += 1
|
||||
elif opcode == 0x1D:
|
||||
indent -= 1
|
||||
@ -265,7 +274,7 @@ def disassemble(bytes, indent = 0, script_name = "script"):
|
||||
elif opcode == 0x4E:
|
||||
if argv[4] != 0:
|
||||
raise "BIND_PADLOCK argv[4] != NULL"
|
||||
if argv[5] != 0:
|
||||
if argv[5] != 1:
|
||||
raise "BIND_PADLOCK argv[5] != 1"
|
||||
|
||||
write_line(f"SI_BIND_PADLOCK({addr_ref(argv[0])}, {trigger(argv[1])}, {var(argv[2])}, {var(argv[3])}),")
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit cc1ce6a64b60e3b0dbb032a2d198cd67210ad827
|
||||
Subproject commit 895abeff31cc0bd5de3a05a55715d3457f8425eb
|
@ -5,6 +5,7 @@ options:
|
||||
pycparser_flags: ["-Iinclude", "-D_LANGUAGE_C", "-ffreestanding", "-DF3DEX_GBI_2", "-DSPLAT"]
|
||||
compiler: "GCC"
|
||||
mnemonic_ljust: 10
|
||||
ld_o_replace_extension: no
|
||||
segments:
|
||||
- name: header
|
||||
type: header
|
||||
|
Loading…
Reference in New Issue
Block a user