From 049ab6e864d58d0ef35827f398f97410be32e323 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 2 Jun 2024 14:58:54 -0400 Subject: [PATCH 01/11] Make `WINE` makefile var configurable --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0dc3c52a..90624309 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ endif ifeq ($(OS),Windows_NT) WINE := else - WINE := wine + WINE ?= wine PYTHON ?= python3.11 endif From fa44ec1ca44569869e67cc3559a1b1658bf0de78 Mon Sep 17 00:00:00 2001 From: Aetias Date: Thu, 20 Jun 2024 18:47:12 +0200 Subject: [PATCH 02/11] Build context files alongside objects --- Makefile | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 90624309..bf37505a 100644 --- a/Makefile +++ b/Makefile @@ -27,11 +27,13 @@ ASSETS_TXT := assets.txt ASM_FILES := $(shell find asm -name *.s) CXX_FILES := $(shell find src -name *.cpp) $(shell find libs -name *.cpp) -C_FILES := $(shell find src -name *.c) $(shell find libs -name *.c) -ASM_OBJS = $(ASM_FILES:%.s=$(TARGET_DIR)/%.s.o) -ASM_INCS = $(ASM_FILES:%.s=%.inc) -CXX_OBJS = $(CXX_FILES:%.cpp=$(TARGET_DIR)/%.cpp.o) -C_OBJS = $(C_FILES:%.c=$(TARGET_DIR)/%.c.o) +C_FILES := $(shell find src -name *.c) $(shell find libs -name *.c) +ASM_OBJS = $(ASM_FILES:%.s=$(TARGET_DIR)/%.s.o) +ASM_INCS = $(ASM_FILES:%.s=%.inc) +CXX_OBJS = $(CXX_FILES:%.cpp=$(TARGET_DIR)/%.cpp.o) +C_OBJS = $(C_FILES:%.c=$(TARGET_DIR)/%.c.o) +CXX_CTXS = $(CXX_FILES:%.cpp=$(TARGET_DIR)/%.cpp.ctx) +C_CTXS = $(C_FILES:%.c=$(TARGET_DIR)/%.c.ctx) OV_BINS := $(wildcard $(TARGET_DIR)/overlays/*.bin) OV_LZS = $(OV_BINS:%.bin=%.lz) @@ -124,16 +126,20 @@ $(ASM_OBJS): $(TARGET_DIR)/%.o: % mkdir -p $(dir $@) LM_LICENSE_FILE=$(MW_LICENSE) $(WINE) $(MW_ASM) $(ASM_FLAGS) $< -o $@ -$(CXX_OBJS): $(TARGET_DIR)/%.o: % +$(CXX_OBJS): $(TARGET_DIR)/%.o: % $(TARGET_DIR)/%.ctx mkdir -p $(dir $@) LM_LICENSE_FILE=$(MW_LICENSE) $(WINE) $(MW_CC) $(CC_FLAGS) $(CXX_FLAGS) $< -o $@ $(TOOLS_DIR)/elf/elfkill -s $< -e $@ -$(C_OBJS): $(TARGET_DIR)/%.o: % +$(C_OBJS): $(TARGET_DIR)/%.o: % $(TARGET_DIR)/%.ctx mkdir -p $(dir $@) LM_LICENSE_FILE=$(MW_LICENSE) $(WINE) $(MW_CC) $(CC_FLAGS) $(C_FLAGS) $< -o $@ $(TOOLS_DIR)/elf/elfkill -s $< -e $@ +$(CXX_CTXS) $(C_CTXS): $(TARGET_DIR)/%.ctx: % + mkdir -p $(dir $@) + $(PYTHON) $(TOOLS_DIR)/m2ctx.py -f $@ $< + .PHONY: link link: lcf $(ASM_OBJS) $(CXX_OBJS) $(C_OBJS) cd $(TARGET_DIR) && LM_LICENSE_FILE=$(MW_LICENSE) $(WINE) $(MW_LD) $(LD_FLAGS) $(LCF_FILE) @$(OBJS_FILE) From 1fe5a6157587401e122c864f3b50cd223f4d4f98 Mon Sep 17 00:00:00 2001 From: Aetias Date: Thu, 20 Jun 2024 18:48:09 +0200 Subject: [PATCH 03/11] Remove `#ifdef STUBS` --- src/00_Core/Actor/Actor.cpp | 4 ---- src/00_Core/Actor/ActorManager.cpp | 4 ---- src/00_Core/Item/Item.cpp | 4 ---- src/00_Core/Map/Course.cpp | 4 ---- src/00_Core/Map/MapBase.cpp | 4 ---- src/00_Core/Map/MapManager.cpp | 4 ---- src/00_Core/Player/LinkStateBase.cpp | 4 ---- src/00_Core/Player/LinkStateDamage.cpp | 4 ---- src/00_Core/Player/LinkStateFollow.cpp | 4 ---- src/00_Core/Player/LinkStateInteract.cpp | 4 ---- src/00_Core/Player/LinkStateItem.cpp | 4 ---- src/00_Core/Player/LinkStateMove.cpp | 4 ---- src/00_Core/Player/LinkStateRoll.cpp | 4 ---- src/00_Core/Player/PlayerBase.cpp | 4 ---- src/00_Core/Player/PlayerControl.cpp | 4 ---- src/00_Core/Player/TouchControl.cpp | 4 ---- src/00_Core/Player/TouchGesture.cpp | 4 ---- src/00_Core/Save/AdventureFlags.cpp | 4 ---- src/14_Land/Actor/ActorRupee.cpp | 4 ---- tools/lcf.py | 19 ------------------- tools/mangle.py | 1 - 21 files changed, 96 deletions(-) diff --git a/src/00_Core/Actor/Actor.cpp b/src/00_Core/Actor/Actor.cpp index d0b2d086..06633177 100644 --- a/src/00_Core/Actor/Actor.cpp +++ b/src/00_Core/Actor/Actor.cpp @@ -1,7 +1,5 @@ #include "Actor/Actor.hpp" -#ifdef STUBS - Actor::Actor() {} Actor::~Actor() {} bool Actor::vfunc_08() {} @@ -105,5 +103,3 @@ void Actor::vfunc_b0() {} void Actor::GetLinkPos(Vec3p *result) {} void Actor::GetLinkDummyPos(Vec3p *result) {} Actor_UnkStruct_09c::Actor_UnkStruct_09c() {} - -#endif diff --git a/src/00_Core/Actor/ActorManager.cpp b/src/00_Core/Actor/ActorManager.cpp index ad56f951..18401b58 100644 --- a/src/00_Core/Actor/ActorManager.cpp +++ b/src/00_Core/Actor/ActorManager.cpp @@ -1,7 +1,5 @@ #include "Actor/ActorManager.hpp" -#ifdef STUBS - void ActorManager::DeleteActor(u32 index, bool param2) {} void ActorManager::func_ov00_020c3484(ActorRef *ref, Actor *actor, unk32 param3) {} void ActorManager::Actor_vfunc_10(u32 param1) {} @@ -19,5 +17,3 @@ s32 ActorManager::func_ov00_020c3bb0(unk32 param1, s32 *param2) {} void ActorManager::func_ov00_020c3ce8(unk32 param1, unk32 param2) {} void ActorManager::Actor_vfunc_28() {} bool ActorManager::ActorTypeIsOneOf(u32 type, u32 *types) {} - -#endif diff --git a/src/00_Core/Item/Item.cpp b/src/00_Core/Item/Item.cpp index 522dd51d..817d86db 100644 --- a/src/00_Core/Item/Item.cpp +++ b/src/00_Core/Item/Item.cpp @@ -1,8 +1,4 @@ #include "Item/Item.hpp" -#ifdef STUBS - bool Item::func_ov00_020ad020(ItemId item) {} bool Item::func_ov00_020ad068(ItemId item) {} - -#endif diff --git a/src/00_Core/Map/Course.cpp b/src/00_Core/Map/Course.cpp index 560169e8..316b05ee 100644 --- a/src/00_Core/Map/Course.cpp +++ b/src/00_Core/Map/Course.cpp @@ -1,7 +1,5 @@ #include "Map/Course.hpp" -#ifdef STUBS - void Course::GetDungeonProgress(CourseProgress *param_2) {} void Course::func_ov00_0207ca28(s32 param_2, unk32 param_3, unk32 param_4) {} char* Course::SetCourseName(char *src) {} @@ -51,5 +49,3 @@ void Course::SetMapDataFlag4(unk32 param_2, unk32 param_3) {} bool Course::GetMapDataFlag4(unk32 param_2) {} MapData* Course::FindMapData(u32 map) {} MapData* Course::FindCurrentMapData() {} - -#endif diff --git a/src/00_Core/Map/MapBase.cpp b/src/00_Core/Map/MapBase.cpp index 5a5ed636..1afc8c29 100644 --- a/src/00_Core/Map/MapBase.cpp +++ b/src/00_Core/Map/MapBase.cpp @@ -1,8 +1,6 @@ #include "Map/MapBase.hpp" #include "Map/MapManager.hpp" -#ifdef STUBS - MapBase::~MapBase() {} MapBase_Unk_180::~MapBase_Unk_180() {} void MapBase::SetBounds(unk32 map, unk32 course) {} @@ -112,5 +110,3 @@ unk32 TriggerBase::vfunc_10() {} void MapBase::func_ov00_02080de4() {} unk8 MapBase::func_ov00_02080de8(unk32 param_2) {} void MapBase::func_ov00_02080edc() {} - -#endif diff --git a/src/00_Core/Map/MapManager.cpp b/src/00_Core/Map/MapManager.cpp index 802043ac..b335e0fe 100644 --- a/src/00_Core/Map/MapManager.cpp +++ b/src/00_Core/Map/MapManager.cpp @@ -1,7 +1,5 @@ #include "Map/MapManager.hpp" -#ifdef STUBS - void MapManager::func_ov00_020820ec(unk32 *param_2) {} void MapManager::func_ov00_020820fc(s32 param_2, unk32 param_3, unk32 param_4) {} unk8 MapManager::func_ov00_0208210c(unk32 param_2, unk32 param_3) {} @@ -200,5 +198,3 @@ unk8 MapManager::func_ov00_02085c60(unk32 param_2, unk32 param_3, unk32 param_4, unk8 MapManager::func_ov00_02086044(unk32 param_2, unk32 param_3, unk32 param_4) {} bool MapManager::func_ov00_02086284(s32 *param_2, Vec3p *param_3, Vec3p *param_4, s32 param_5, u16 param_6, Vec3p *param_7, Vec3p *param_8) {} bool MapManager::func_ov00_02086a84(s32 *param_2, Vec3p *param_3, Vec3p *param_4, s32 param_5, s32 param_6, unk32 param_7, Vec3p *param_8, Vec3p *param_9) {} - -#endif diff --git a/src/00_Core/Player/LinkStateBase.cpp b/src/00_Core/Player/LinkStateBase.cpp index 482a8371..abe7bd34 100644 --- a/src/00_Core/Player/LinkStateBase.cpp +++ b/src/00_Core/Player/LinkStateBase.cpp @@ -1,7 +1,5 @@ #include "Player/LinkStateBase.hpp" -#ifdef STUBS - void LinkStateBase::vfunc_00() {} void LinkStateBase::OnStateEnter() {} void LinkStateBase::OnStateLeave(s32 param1) {} @@ -85,5 +83,3 @@ unk32 LinkStateBase::Get_PlayerControlData_Unk120() {} s32 LinkStateBase::Get_PlayerLinkBase_Unk38() {} DebugHierarchy* LinkStateBase::GetDebugHierarchy0() {} DebugHierarchy* LinkStateBase::GetDebugHierarchy1() {} - -#endif diff --git a/src/00_Core/Player/LinkStateDamage.cpp b/src/00_Core/Player/LinkStateDamage.cpp index 84674461..c544b154 100644 --- a/src/00_Core/Player/LinkStateDamage.cpp +++ b/src/00_Core/Player/LinkStateDamage.cpp @@ -1,7 +1,5 @@ #include "Player/LinkStateDamage.hpp" -#ifdef STUBS - void LinkStateDamage::vfunc_00() {} void LinkStateDamage::CreateDebugHierarchy() {} void LinkStateDamage::OnStateEnter() {} @@ -16,5 +14,3 @@ bool LinkStateDamage::vfunc_24(s32 param1) {} bool LinkStateDamage::vfunc_20(s32 param1) {} void LinkStateDamage::func_ov00_020acfe8(bool param1) {} LinkStateId LinkStateDamage::GetId() {} - -#endif diff --git a/src/00_Core/Player/LinkStateFollow.cpp b/src/00_Core/Player/LinkStateFollow.cpp index 54e87938..69d173dd 100644 --- a/src/00_Core/Player/LinkStateFollow.cpp +++ b/src/00_Core/Player/LinkStateFollow.cpp @@ -1,7 +1,5 @@ #include "Player/LinkStateFollow.hpp" -#ifdef STUBS - void LinkStateFollow::vfunc_00() {} LinkStateId LinkStateFollow::GetId() {} void LinkStateFollow::MoveTowardTarget() {} @@ -12,5 +10,3 @@ LinkStateCutscene* LinkStateFollow::GetLinkStateCutscene() {} bool LinkStateFollow::vfunc_34(Vec3p *param1) {} bool LinkStateFollow::func_ov00_020a9180(Vec3p *param1) {} bool LinkStateFollow::func_ov00_020a9210(Vec3p *param1, Actor *param2) {} - -#endif diff --git a/src/00_Core/Player/LinkStateInteract.cpp b/src/00_Core/Player/LinkStateInteract.cpp index a3a7ddca..6cbda713 100644 --- a/src/00_Core/Player/LinkStateInteract.cpp +++ b/src/00_Core/Player/LinkStateInteract.cpp @@ -1,7 +1,5 @@ #include "Player/LinkStateInteract.hpp" -#ifdef STUBS - void LinkStateInteract::vfunc_00() {} LinkStateId LinkStateInteract::GetId() {} void LinkStateInteract::CreateDebugHierarchy() {} @@ -30,5 +28,3 @@ void LinkStateInteract::func_ov00_020abbdc(ActorRef *ref) {} void LinkStateInteract::func_ov00_020abc18(ActorRef *ref) {} bool LinkStateInteract::func_ov00_020abc40() {} bool LinkStateInteract::func_ov00_020abc78(ActorRef *ref) {} - -#endif diff --git a/src/00_Core/Player/LinkStateItem.cpp b/src/00_Core/Player/LinkStateItem.cpp index 1faf16ab..eaf3c8b6 100644 --- a/src/00_Core/Player/LinkStateItem.cpp +++ b/src/00_Core/Player/LinkStateItem.cpp @@ -1,7 +1,5 @@ #include "Player/LinkStateItem.hpp" -#ifdef STUBS - void LinkStateItem::vfunc_00() {} LinkStateId LinkStateItem::GetId() {} s32 LinkStateItem::IsHammerEquipped() {} @@ -10,5 +8,3 @@ EquipBombchu* LinkStateItem::GetEquipBombchu() {} LinkStateMove* LinkStateItem::GetLinkStateMove() {} bool LinkStateItem::func_ov00_020abf70() {} bool LinkStateItem::vfunc_28() {} - -#endif diff --git a/src/00_Core/Player/LinkStateMove.cpp b/src/00_Core/Player/LinkStateMove.cpp index 586a331e..918a92fe 100644 --- a/src/00_Core/Player/LinkStateMove.cpp +++ b/src/00_Core/Player/LinkStateMove.cpp @@ -1,7 +1,5 @@ #include "Player/LinkStateMove.hpp" -#ifdef STUBS - void LinkStateMove::vfunc_00() {} LinkStateId LinkStateMove::GetId() {} void LinkStateMove::CreateDebugHierarchy() {} @@ -10,5 +8,3 @@ void LinkStateMove::OnStateLeave(s32 param1) {} bool LinkStateMove::vfunc_24(s32 param1) {} bool LinkStateMove::func_ov00_020a8f2c() {} bool LinkStateMove::func_ov00_020a8f4c() {} - -#endif diff --git a/src/00_Core/Player/LinkStateRoll.cpp b/src/00_Core/Player/LinkStateRoll.cpp index d6a93463..91044e21 100644 --- a/src/00_Core/Player/LinkStateRoll.cpp +++ b/src/00_Core/Player/LinkStateRoll.cpp @@ -1,7 +1,5 @@ #include "Player/LinkStateRoll.hpp" -#ifdef STUBS - void LinkStateRoll::vfunc_00() {} LinkStateId LinkStateRoll::GetId() {} void LinkStateRoll::CreateDebugHierarchy() {} @@ -12,5 +10,3 @@ bool LinkStateRoll::vfunc_24(s32 param1) {} void LinkStateRoll::func_ov00_020aee58(s16 param1, u16 param2) {} void LinkStateRoll::func_ov00_020aee84() {} bool LinkStateRoll::func_ov00_020aeeac() {} - -#endif diff --git a/src/00_Core/Player/PlayerBase.cpp b/src/00_Core/Player/PlayerBase.cpp index 24619493..a9d83c5b 100644 --- a/src/00_Core/Player/PlayerBase.cpp +++ b/src/00_Core/Player/PlayerBase.cpp @@ -1,7 +1,5 @@ #include "Player/PlayerLinkBase.hpp" -#ifdef STUBS - bool PlayerBase::vfunc_04() {} ItemFlag PlayerBase::GetEquipId() {} bool PlayerBase::CanMove() {} @@ -20,5 +18,3 @@ void PlayerBase::AddHealth(s16 amount) {} bool PlayerBase::Teleport(Vec3p *pos, s16 angle, unk32 param3, bool param4, bool param5) {} bool PlayerBase::TeleportToEntrance(unk32 entranceId, bool param2) {} bool PlayerBase::TeleportToLastEntrance(bool param1) {} - -#endif diff --git a/src/00_Core/Player/PlayerControl.cpp b/src/00_Core/Player/PlayerControl.cpp index 48c52e7a..f51d0a89 100644 --- a/src/00_Core/Player/PlayerControl.cpp +++ b/src/00_Core/Player/PlayerControl.cpp @@ -1,7 +1,5 @@ #include "Player/PlayerControl.hpp" -#ifdef STUBS - bool PlayerControl::func_ov00_020aeeac() {} void PlayerControl::func_ov00_020aeef8() {} void PlayerControl::func_ov00_020aef30() {} @@ -52,5 +50,3 @@ bool PlayerControl::IsNotTouching() {} bool PlayerControl::IsTouchingFast() {} bool PlayerControl::IsTappedNow() {} bool PlayerControl::func_ov00_020b13c4() {} - -#endif diff --git a/src/00_Core/Player/TouchControl.cpp b/src/00_Core/Player/TouchControl.cpp index df8bbfe0..5cae75a5 100644 --- a/src/00_Core/Player/TouchControl.cpp +++ b/src/00_Core/Player/TouchControl.cpp @@ -1,7 +1,5 @@ #include "Player/TouchControl.hpp" -#ifdef STUBS - TouchControl::TouchControl() {} void TouchControl::IncreaseSpeed(s16 increase) {} void TouchControl::UpdateFlags(u16 speed) {} @@ -11,5 +9,3 @@ bool TouchControl::func_ov00_0207aeac() {} void TouchControl::UpdateConditionally(TouchState *state, u16 speed) {} void TouchControl::func_ov00_0207af38(u16 speed, bool param2) {} TouchControl::~TouchControl() {} - -#endif diff --git a/src/00_Core/Player/TouchGesture.cpp b/src/00_Core/Player/TouchGesture.cpp index 83879415..027e3e0a 100644 --- a/src/00_Core/Player/TouchGesture.cpp +++ b/src/00_Core/Player/TouchGesture.cpp @@ -1,7 +1,5 @@ #include "Player/TouchGesture.hpp" -#ifdef STUBS - TouchGesture::TouchGesture() {} void TouchGesture::ResetTouchHistory() {} void TouchGesture::Update(void *param1) {} @@ -9,5 +7,3 @@ TouchGesture::~TouchGesture() {} void TouchGestureBase::ResetTouchHistory() {} TouchGestureBase::~TouchGestureBase() {} - -#endif STUBS diff --git a/src/00_Core/Save/AdventureFlags.cpp b/src/00_Core/Save/AdventureFlags.cpp index dd05dff6..14f377c1 100644 --- a/src/00_Core/Save/AdventureFlags.cpp +++ b/src/00_Core/Save/AdventureFlags.cpp @@ -1,7 +1,5 @@ #include "Save/AdventureFlags.hpp" -#ifdef STUBS - bool AdventureFlags::Exists() {} void AdventureFlags::CopyTo(unk32 *flags) {} void AdventureFlags::func_ov00_02097674() {} @@ -50,5 +48,3 @@ bool AdventureFlags::func_ov00_0209809c() {} bool AdventureFlags::func_ov00_020980ac() {} void AdventureFlags::func_ov00_020980bc(s32 param1) {} void AdventureFlags::func_ov00_020980d0() {} - -#endif diff --git a/src/14_Land/Actor/ActorRupee.cpp b/src/14_Land/Actor/ActorRupee.cpp index 20d98de4..d2641f38 100644 --- a/src/14_Land/Actor/ActorRupee.cpp +++ b/src/14_Land/Actor/ActorRupee.cpp @@ -1,7 +1,5 @@ #include "Actor/ActorRupee.hpp" -#ifdef STUBS - Resource ActorRupee::gResource; ActorType ActorRupee::gType; @@ -21,5 +19,3 @@ void ActorRupee::func_ov14_0213b5f4(RupeeId id, unk32 param2, Vec3p *param3, boo void ActorRupee::func_ov14_0213b6a4(RupeeId id, void *param2) {} bool ActorRupee::func_ov14_0213b70c(RupeeId id) {} ActorRupee::~ActorRupee() {} - -#endif diff --git a/tools/lcf.py b/tools/lcf.py index 084d03a9..7a37598e 100644 --- a/tools/lcf.py +++ b/tools/lcf.py @@ -33,63 +33,45 @@ DTCM_OBJECTS = [ ov00 = Overlay(name='ov00', after='ARM9', objects=[ 'asm/ov00/ov00_020773c0.s', - 'src/00_Core/Player/TouchControl.cpp', 'asm/ov00/Player/TouchControl.s', 'asm/ov00/ov00_0207afa0.s', 'asm/ov00/Map/Course.s', - 'src/00_Core/Map/Course.cpp', 'asm/ov00/Map/MapBase.s', - 'src/00_Core/Map/MapBase.cpp', 'asm/ov00/ov00_0207da38.s', 'asm/ov00/Map/MapManager.s', - 'src/00_Core/Map/MapManager.cpp', 'asm/ov00/ov00_02086cd0.s', - 'src/00_Core/Save/AdventureFlags.cpp', 'asm/ov00/Save/AdventureFlags.s', 'asm/ov00/ov00_020980f4.s', - 'src/00_Core/Player/PlayerBase.cpp', 'asm/ov00/Player/PlayerBase.s', - 'src/00_Core/Player/LinkStateBase.cpp', 'asm/ov00/Player/LinkStateBase.s', - 'src/00_Core/Player/LinkStateMove.cpp', 'asm/ov00/Player/LinkStateMove.s', - 'src/00_Core/Player/LinkStateFollow.cpp', 'asm/ov00/Player/LinkStateFollow.s', - 'src/00_Core/Player/TouchGesture.cpp', 'asm/ov00/Player/TouchGesture.s', 'asm/ov00/ov00_020a8e04.s', - 'src/00_Core/Player/LinkStateInteract.cpp', 'asm/ov00/Player/LinkStateInteract.s', - 'src/00_Core/Player/LinkStateItem.cpp', 'asm/ov00/Player/LinkStateItem.s', - 'src/00_Core/Player/LinkStateDamage.cpp', 'asm/ov00/Player/LinkStateDamage.s', - 'src/00_Core/Item/Item.cpp', 'asm/ov00/Item/Item.s', 'src/00_Core/Item/ItemManager.cpp', - 'src/00_Core/Player/LinkStateRoll.cpp', 'asm/ov00/Player/LinkStateRoll.s', - 'src/00_Core/Player/PlayerControl.cpp', 'asm/ov00/Player/PlayerControl.s', 'asm/ov00/ov00_020b1498.s', - 'src/00_Core/Actor/Actor.cpp', 'asm/ov00/Actor/Actor.s', 'asm/ov00/ov00_020c3348.s', - 'src/00_Core/Actor/ActorManager.cpp', 'asm/ov00/Actor/ActorManager.s', 'asm/ov00/ov00_020c3e54.s', @@ -136,7 +118,6 @@ ov13 = Overlay(name='ov13', after=[ov04, ov05, ov06, ov07], objects=[ ov14 = Overlay(name='ov14', after=[ov08, ov09, ov10, ov13], objects=[ 'asm/ov14/ov14_0211f640.s', - 'src/14_Land/Actor/ActorRupee.cpp', 'asm/ov14/Actor/ActorRupee.s', 'asm/ov14/ov14_0213b778.s', diff --git a/tools/mangle.py b/tools/mangle.py index a9bfcadf..d14bec54 100644 --- a/tools/mangle.py +++ b/tools/mangle.py @@ -38,7 +38,6 @@ cc.extend([ '-i', include_dir, '-i', libc_include_dir, '-i', libcpp_include_dir, - '-DSTUBS', args.file ]) From 2823cb8f6131d983aeed859579d641555453f61b Mon Sep 17 00:00:00 2001 From: Aetias Date: Thu, 20 Jun 2024 18:48:46 +0200 Subject: [PATCH 04/11] Export decomp.me scratch in objdiff --- tools/objdiff.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tools/objdiff.py b/tools/objdiff.py index 1e6ec65d..16078798 100644 --- a/tools/objdiff.py +++ b/tools/objdiff.py @@ -35,14 +35,15 @@ def find_asm_path(src_path: Path) -> Path | None: return asm_path -def get_build_path(path: Path) -> Path: +def get_build_path(path: Path, extension='.o') -> Path: region = args.region.lower() - return Path('build') / region / path.parent / (path.name + '.o') + return Path('build') / region / path.parent / (path.name + extension) config = dict() config["custom_make"] = "make" config["custom_args"] = [ + "-B", f"REGION={args.region}" ] config["build_target"] = True @@ -61,6 +62,7 @@ for (root, dirs, files) in os.walk(src_dir): src_path = Path(f'{root}/{file}') src_path = src_path.relative_to(src_dir) asm_path = find_asm_path(src_path) + src_path = 'src' / src_path if asm_path: asm_path = 'asm' / asm_path @@ -69,9 +71,17 @@ for (root, dirs, files) in os.walk(src_dir): obj = dict() obj["name"] = name if asm_path.exists(): obj["target_path"] = str(get_build_path(asm_path)) - obj["base_path"] = str(get_build_path('src' / src_path)) + obj["base_path"] = str(get_build_path(src_path)) obj["reverse_fn_order"] = False + scratch = dict() + scratch["platform"] = "nds_arm9" + scratch["compiler"] = "mwcc_30_131" + scratch["ctx_path"] = str(get_build_path(src_path, '.ctx')) + scratch["build_ctx"] = True + + obj["scratch"] = scratch + config["objects"].append(obj) with open(config_path, 'w') as f: From 33ea1fba3872db2bfcbb50239f3663b905a0297e Mon Sep 17 00:00:00 2001 From: Aetias Date: Thu, 20 Jun 2024 18:48:57 +0200 Subject: [PATCH 05/11] Update INSTALL.md --- INSTALL.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index bea8eff4..816f3278 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -12,13 +12,12 @@ Contents: 1. Use one of these platforms: - Windows (MSYS) - Linux via WSL - - Recommended for Windows users - Linux 2. Install the following: - Python 3.11+ and pip - GCC 9+ - Make - - **On Linux/WSL**: Wine + - **On Linux/WSL**: Wine/Wibo 3. Install the Python dependencies: ```shell python -m pip install -r tools/requirements.txt @@ -28,6 +27,9 @@ python -m pip install -r tools/requirements.txt python tools/setup.py ``` +> [!NOTE] +> For Linux users: If you plan to use Wibo instead of Wine, run make with `make WINE= ...`. + ## Build the ROM This repository does not include any of the game's assets, and you will need an original decrypted base ROM. From 470722e5b9482f6e9e7b7bc444c1f755b335f6e7 Mon Sep 17 00:00:00 2001 From: Aetias Date: Thu, 20 Jun 2024 18:59:15 +0200 Subject: [PATCH 06/11] Update `decompiling.md` --- docs/decompiling.md | 80 ++++++++++++++++++------------- docs/images/ghidra_decomp.png | Bin 0 -> 9695 bytes docs/images/objdiff_function.png | Bin 0 -> 29981 bytes docs/images/objdiff_match.png | Bin 0 -> 35154 bytes docs/images/objdiff_objects.png | Bin 0 -> 11676 bytes docs/images/objdiff_symbols.png | Bin 0 -> 18567 bytes 6 files changed, 46 insertions(+), 34 deletions(-) create mode 100644 docs/images/ghidra_decomp.png create mode 100644 docs/images/objdiff_function.png create mode 100644 docs/images/objdiff_match.png create mode 100644 docs/images/objdiff_objects.png create mode 100644 docs/images/objdiff_symbols.png diff --git a/docs/decompiling.md b/docs/decompiling.md index 3ff23f2b..b31b9d8d 100644 --- a/docs/decompiling.md +++ b/docs/decompiling.md @@ -20,43 +20,55 @@ again. Remember to make a pull request of any notable progress you made on the s [non-matching functions](/CONTRIBUTING.md#non-matching-functions). ## Decompiling a source file -It can be tricky to fully decompile an assembly file into a C/C++ source file, so here's some advice to make it easier: -- C/C++ code is built before assembly code - - This means you can take one function from the top of your assembly file, decompile it, and append it to the bottom of - your C/C++ file. -- Build the ROM often - - We recommend building every time you decompile a function. - - This is because functions can sometimes match in decomp.me, but not when building. - - If your ROM doesn't match, it's easier to know which function is wrong if it's the only function added since the last - successful build. +We use the object diffing tool [`objdiff`](https://github.com/encounter/objdiff) to track differences between C++ and assembly +code. +1. [Download the latest release.](https://github.com/encounter/objdiff/releases/latest) +1. Run `python tools/objdiff.py ` to generate `objdiff.json` in the project root. +1. In `objdiff`, set the project directory to the root of this project. This will load `objdiff.json`. +1. Select your source file in the left sidebar: +![List of objects in objdiff](images/objdiff_objects.png) +5. See the list of functions and data to decompile: +![List of symbols in objdiff](images/objdiff_symbols.png) + +> [!NOTE] +> If a source file is missing in `objdiff`, or `objdiff` fails to build a file, first rerun `objdiff.py` to update +> `objdiff.json`. If the problem persists, feel free to ask for help. ## Decompiling a function -Say you've found a function you want to decompile. Here are the steps we recommend for decompiling it: -1. Visit [decomp.me](https://decomp.me/) and start decomping. -1. Under the platforms, select "Nintendo DS". -1. Select compiler preset "Phantom Hourglass". -1. Copy and paste the target assembly for your function, including the `func_start` and `func_end` macros, and the pool -constants. For example: -```arm - .global func_ov09_0211bf48 - thumb_func_start func_ov09_0211bf48 -func_ov09_0211bf48: ; 0x0211bf48 - ldr r0, _0211bf50 ; =data_ov09_0211f59c - ldrb r0, [r0] - bx lr - nop - thumb_func_end func_ov09_0211bf48 -_0211bf50: .word data_ov09_0211f59c +Once you've opened a source file in `objdiff`, you can choose to decompile the functions in any order. We recommend starting +with a small function if you're unfamiliar with decompilation. Here's an example: + +![Function in objdiff](images/objdiff_function.png) + +As a starting point, we look at the decompiler output in Ghidra. You can request access to our shared Ghidra project [in this section](#the-ghidra-project). + +![Decompiler in Ghidra](images/ghidra_decomp.png) + +Looking at this output, we might try writing something like this: +```cpp +ARM bool Actor::SetVelocity(Vec3p *vel) { + if (mUnk_11b) { + mVel = *vel; + mUnk_11b = false; + return true; + } + return false; +} ``` -5. Run `m2ctx.py include/MyHeader.hpp -c` to generate a context and put it in your clipboard. - - If no suitable header file exists, make a new one and put any structs and types you need in there. -1. Paste the context into decomp.me, and create the scratch. -1. Decompile the function and try to get a 100% match. - - There's no ARM decompiler in decomp.me yet, but Ghidra does the job quite well. See [the Ghidra section](#the-ghidra-project) - for more info. - - If you're unable to get a 100% match, share your decomp.me scratch with other contributors and they may assist you. - - In the worst case, you can also contribute [non-matching functions](/CONTRIBUTING.md#non-matching-functions) to this - project. + +Now we can go back to `objdiff` and look at the result: + +![Matching function in objdiff](images/objdiff_match.png) + +Success! Note that this was a simple example and that you'll sometimes get stuck on a function. In that case, try the +following: +- Decompile a different function and come back later. +- Export to [decomp.me](https://decomp.me/): + 1. Press the `decomp.me` button in `objdiff`. + 1. Once you're sent to `decomp.me`, go to "Options" and change the preset to "Phantom Hourglass". + 1. Paste your code into the "Source code" tab. + 1. Share the link with us! +- Add the function as a [non-matching function](/CONTRIBUTING.md#non-matching-functions). ## Decompiling `.init` functions > [!NOTE] diff --git a/docs/images/ghidra_decomp.png b/docs/images/ghidra_decomp.png new file mode 100644 index 0000000000000000000000000000000000000000..d54ff6ab89a1902a53d3771f1c547b974114b83c GIT binary patch literal 9695 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 literal 0 HcmV?d00001 diff --git a/docs/images/objdiff_function.png b/docs/images/objdiff_function.png new file mode 100644 index 0000000000000000000000000000000000000000..34e8a1648d6f5d4fb016841b0e4079e7907dc0d3 GIT binary patch literal 29981 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 literal 0 HcmV?d00001 diff --git a/docs/images/objdiff_match.png b/docs/images/objdiff_match.png new file mode 100644 index 0000000000000000000000000000000000000000..a43726b7f9d8388ee2b4593c2c3bf9ba774889ae GIT binary patch literal 35154 zcmb5VbyQT}|LS_iQj(&yN(w`aI3O+EN(?o0w@9jVH&Qe75K4ECGz?w$ z@cw>2pWpA^weDxF`v>bRm|>lL_CEXhdcB^H*ACUvctK3SKyc^I9paZNim&h7xtDh5 z&RtnN9N;hbCwO;(%U!3}FXZo(3^JjCU$CHZ>T-APlt&R>nql95$3exw>CPRJj@zHR zNc+O~ckYC&zf_ca>ki(|z^`LgN;%Q_t&OC=a=ye+Qh&kf$MMIJXCrT?2&C(FC6|BO z6q`n7u4Z@dC0l4T;p$rk6G9 zbDl19vvMYVl3wyx=FcCU8((X$O?qgSkL!a#GI>09Mdt1aD&QJOR?QB1b9`I|gQ@WrQFj6MHB{CvUQkf7 zm_7K@*=iE5-@DFAhsE~ygXt1&QZ(ih^&8*9V|h!Pg!GRu?lJ`F290n9l3nEwO)zmO zjOv$}%I$&W<3XY(hpFROewxm>0-ahigrNTZOle8|xwJb21bNzc?B_4D%jqe~C#3kkO;iklXBgi%dnZO$b%Nw-CSS988^C01PsbvN4hm})URiH#lB$i;PhCR zoTnGz@p|b;?@qJpcXJW<&c6BS=*Db|Ph)t%A`=0r9K;+M$=p*8LB{61h~nDy+4YL+ z3a7ST$#Nug%GO^d}3yMULjGX>24v>#^Kg! z`_|cv4k(dkl>jpm&*}v=f-AMraT)P`eK4cMZOhi)YZ`9m1}FCH3<($xG#hY)*^1fE zZ7I75nx6K#m8@rF1=FY5`&~?DEc%?xx=98g+CdBBMFyj18d0

S?j{PU{W3e7~2( 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 literal 0 HcmV?d00001 diff --git a/docs/images/objdiff_objects.png b/docs/images/objdiff_objects.png new file mode 100644 index 0000000000000000000000000000000000000000..0bde5aaa1b7922db2e1fb7e3dd1aab58435e7c75 GIT binary patch literal 11676 zcmb_?XIN9))-IM!k(L0`1rkD&c8Gw|B`hEail8C^1SFwJQ97Zjw1gfIq_aU#bR$i= z2o_4{MFl~cw1D*9;jXyPIp4Yaocn$EKF|G;Cs`|V%{AM2$2;CJh}W;_GN0i+LqkKu ztcM1SX=qL$!H*$5EqJHP4>bgSo$xT$MbQ*>@y~%5r|mVbYSPe@#xWk;{SCZkc!0)u z(9p1aqJB=a+%Le<&`8Va0h%{`Ef+Jlt2l0LRgGoaziw4}^ib&WY0^~uY0Yy8x>lu- zhf_=mN&Gt36J{?xW6vNT-yYk{@LA0I?A879=l5G9!#|G>Rtmr067%$O(pd0#<-dHq zJ#Tfa3`G&n3zHaN=i>m;&;nJqR7ma`8iyjZ{rzUYknFn+GJ(CHu52vh5r)7S@L67rb^=B{7WO14#r z^`k@{s9Ei6*)pdA8KMD!B`=$I!OX>mmrnKv>G$g~J}jbF)1DNXmclU=_in8<^WERzwqFU*PnL4i|6$vnEK>Y# zD@`Si9D7bBxg+($i{@&#Nx$fABGLKhmzS;K%zPG4B6_p}d8addM%|ze1+S$L$lg{C zPM)$$!AAijt4Wf#Gye9CG9hdHW()i+`c&4+drifC3hh4EC$bF_Q1kPA&8px2OgFAS z_7PrVZ}!>L&g!Hqf853UqYJ>~w)q>om?$>GOy%k1_hQ#F9<29c6V zz`N^3VZMla9e&*|ZoPRq_}E{}Sw&}xAnToB*Y-rBHCH>zLZ0VdwxUZ~x*)?oh3JZz zk+rBUSyDygauXfRaO|Q6=t|d-AuWCkTqR(qpS+PwCI|_t2c&eQ$UW2LGd`M#l1>xWrlwC2vdY5K5R(YS{ZJDJECM!*J)VcjpIn zZlhaJlAG2CT$8`G@hK~usvZBa&;#M7J#MztS=^arQhsYBnJ_~*-v=Fhc;Zw>q4T5~ z2Pv#MUo)I6@`aYbw3{S?3_(NOV7yaJeZAHg^p~Gel7akesTHfWf@*$K0_=fhp5=;G zxXUp}Gnywdu}NYFu}g+C0#F?gpYUhQ1+l_DoG<>}U9!mhq@)^wNBBgZDD4k(_^QJ(=m~!}ts# z_!^u&)4*K2dHX$tGjIp0sBe(`3Xl&7JmJ_QtJVS<b2oEyKHlH$!txC3a`TXt3(Vf1C zPkr(0l?>g+iR2!Q8Cq>&_$jz_Go%-KsOG!!K9e#pIxIAfCn1)TVop#u&E+gl3A8KX z+>a8T*l#-}`$onNEar-GEUT+rIdwY|QEGc`uNUqN+Ntaf(44|=w}aRbjSXA>IL$emqbuLJ04m=`_&zBB#4!04G@dA=hquJF!-swV}EcxbO% z#FJ^~g8RJG(RN?O$a=f*c~ zT?wVKfh$kq3<&jCv!R1MIa(uCHmBc7Sf~RXOe`#( z9100c23yFBAp48dHI7!}5(SS|B9^}ImLHGPX*|$iF^ppeUj}hECOijpl=*`^T~<8c zeG~DI!@cu#a-ago3-%g+WODsi8--^*_)lDevO%j+)}C9>1P{rrvVliW8xCwY@L`Y< z?IB&cE9`?l4f`W$b$#%4;cBbHpSNWySQdeMxEtD*(e{>Iq^~f95)ANx{`uBNX6{&? zyx+UnQUysc@u6?JlPP8HPaeHlJ*cQh5tc-!@*r1<(H$>B=va$BgaRaby2dy8f&eja8Kc1!CvaY}{) zf%Ce4_lDv#Gv5S(?)sw`_yTDt&vlxRtAPSHng|EG&RaLWI)Ly9ZY2+;^}8(CM_*_L zN3H}(;Uvmtxg)m5yw#`ez&rO%_Q8}`g$IiM2Rk^IWTF8*ERxOL7(^fXUV8>5380Al zq*<4KRaIOehIQW}0VD^RItbRlCC_t);8E-Qu(Jmj#PJ6 zRW;IB^?bgS+-+@+;d5$H@mVqu06oO;TOV1>TW^;*Y;fV~M@CshcF^9-ea)cSMbImF zyUeZIYPCJ@*`-{4YuVr+$c8zC^b(>&*gfdjy&g>8ncZ@6EFf{X)=RR+gA$wAmwLh8 z!UQseR>V&>$C!PDhpy4`PJ+-$v z|3R^LTU-ihyq=*RICRKdu^&3K%@Fe_QOr=3(w)8hfygfQ_z))FV2DO1ie9~oEr|&viHz%-vcqwRYN^gUE$2y^|51|mzO+Q%k!36-Cqd8mOM#IWrCRiO=ux4B)b8PN zm+Dr_$>k(l_*VDDwLQItBhYt#-F#$HXPA2_3}`-n0s_?4EH(cbc+_>l?4ASl9J!T^ zxf|R3jysnl_Oz(m5uYq;mE}BC-g+3XVV$?y(^1JO(4`Xz2JjmGQ zo8f(QtO6T5;}@fdOgRU0hQaOwZu?s<+rt)Bo8RC0SE=|c4k^%4MuU$7ePW#^hIZ(h zX7<*zj~8Ypj@62uiYT^hObUJtI^3%XvI;)*y7NW^PiGSug)AtEMO)EC3_H5b9EBa<0s=+w8Z9@*n8zmj1W~{>0h7*h5xiZxd+R z_k8?_Or#TWQ8w^NCA|9EWYA035Yk$JD2u1Z>SfWCVFFZmSWW?6tt0S)Qv2Y?mOVQORh!-UA)%Z!kj;sa91}Cjcn&mY)&(C-3br;J zA1(`&WDpmhhEa`TRa4X|a5E{9;rLDX9yd6VKIBPUYxB{Ump4W{R$!B|Eg3J=csE0d_F;z#Gv zD1>?zp8vw1P@Gkb^)7+Kx5FwH&%b|O2Lzu0QYiwL`mtecVR7Y$(@h*0pBt?>=3s&l0 ze^6D`JD}mv^ZO7|?T_qPJ~U3^o4%$Q5PW3SMK!l0RnwDY@;^da8V*|G1d9V79J1CQ zU-Il2Wfr_rU-XFH#nCZ+B*We#%^_~U+J|O&tRZ{hdsVmE(Rc{!F#pD+A7RP)2rdst zUe-Z}Q|wz?1#bJj$)*>Vznx58u%q+%8`niwzx6%LE02y~u`PY`O?Bb~8h3&~U;QcK z{Z6}E0caW;tV@M4f*SR6tLlI^zyzEFe7v*BAs#u!j4PTi+LF!*tM5H;_}C*;?Yf1y zI`gonTD0Ev$F|F9OToUoJgs$gV{=cXs`{_#UkZz48}P<+%UG6aCda z&0Qj`?pNKm3RL)J7q53Kl`(Xd|GWgU{)3_S^|gdm>}@%F)h2E7u+ot>cHy4&ZA*1db?lac(Ir((o+YDu|w)~(< z8~zwVw1wRPx9XREraBfRd?jS!`ddhDg}gMuqr1!(E;9FJC`omei}!v!aRThl zP+Ek!!3)6U89qNpA}KtvSiD@0!?qS)e#KYqIcL16y7IOaWDv@la1nhzY`jEr-40Gj z#D}|qf@tCLAXP=)99FEdy*2KWjbO$x&3U4`DBTjF;t|ian+0x|;V}Ex0(wXW0vn7m z7YUW7LTb8ktIDis3z*E>dFK`EAG|EbmhUtIF=6Z*{2 z$uZh#F3E8>Z|0huknlB~Kni|Dyx@*|^hGXO_)va8Cl5dV{>1Ig*5GR}K#9Z5Y|jW| zB2W9pZ6lv6Bq(Q<_OT7-3az1vuB#W&hy`?Twl0AFkzC1(nW$(oQ9Lfg(mS>hpNvmB z{RqJKCyQ2=0}twC^Pa4BGs4>PZu&4n6Qb`a`&%LmjJ+21)n$=27+fA%(-%%Q z?ORP5UZiguTZt$qfBTFIKVo}g`&?Rq#DOaR$WCZoN;v87Dy5-U=|FkN$5#2Y#p?)^ zW$w)u6+&`-I5;uSGL$?-#f#*@joa2V0Q44Klv*_(B~5!>B*8=#_q2L#%y5_87+l%7 zotJK?Br0z8RIih{5pC`y*{?&Uh?+fRokx@SBQqepR>$bytPN|=yl7WkV^a{&2pYa6X;x~ZUFPz=5(g?C6^dEORHme&q19H_-LP4a&pkke)N!Y*{6h9u_tD{B+_>F&2J{< z146y9iS=#<*ek#c>s3NzBwm{=dL zs&9!(7aLC>HgQaG2b#ksb;c+VZ0Gk0^t_4w+emCnn=i6l)7KWs> zeF_}fO0g{o^8>|i#p8yZfB=xg7p0pMT%SLq9-8E*is|SmxCp`;F*L!AxlWS!Lt6f+ zW?m>6m7p2fp~s>S`x18fr^{5M9Gvm&)w{(@py1$|dXFMwSvQ-juELr7eNLRTYlN{J za_+MbK8I|Cb3`VTXm+#7{OYJor@2(spbDlGI(0i}Lg%4vRRuvRb1->Y>aj3VojV84`HeU3bI|uLdS;OQPCYK29s*rg zMv2rKLq&c|#CR;_<&75^dEEN)O(TXw$`_tuYpO_{o|j zFkg#s-1`-NDmmnWT}MF@!KqxD)shiuaNE&uwTbHvhobuwxpp1@;M7Cr;Gzg6RAA_= zNv$RBU6^e3ka`B|7%Sr@7pnZP5$eC3>tA6j$P1Db;vl)o|As{vhWOo0@R)i<3#irh z*IR#hj02Cxl_p^zDxQ3W#t0LByKf_Y`r~oV?xRz57Db0Tkx%JiesMtZW5I{!$rz9p z)jOg`wEWKL-@3ZRXuJo>A@xPHSofM07SMadx9x%MWN8@;uq^zX`mDcuFu;x?faH&c zPhI5G3go2A7}!M-lDCUI6Hwu*vW>0?xQhQ) z;e0nfGCQ7*xp#N{4D3jQ4Rab^ItpNW_(-;A&w?m0d+r~%AnZI=@2Wwi_cgaH?w|iL zTsH_czOT+PRk1Z-6|DB7OGIC{QQmdNahnanQCjUHV=>L>ayIjFB80Y$yug z%pt3u*MS56)Mj#q${MMWB>G-nu!(I)BImzcd`|x|KyA2A?Iz{LMfeS{gEU<*!fjT4 z2mRGhl?@Y$oFNhon$V%*Ttq1p34b#hAZ)>@L&=h&{&tW5PouHz6b^Mz1D`INfl|+% z6biFW4>OAcM^}gsg}kP|<^N(*|Lg|7UjAl>{$*)u@X~P2l5Zmc+FIc>kiaf>ZDUpv zctITx(oiKTF5Z_U#go@C;|t=V%pbHb%RdK(&EEWT?p?b5krTp%X!@0{#qTmvwAj?o zK&4$L??tW_F~ckayUw)fu1`5y5f(p%_AXQfzs5|GpLws0Yx*!x8EW-=zb+l1x&Lm{ zr0o8f`qNkYpti~qg$NUW3{EK;-v@WnUWHz!CxnpWEF1`pKxOdp5pI2^QwEhMt8|PB z>TAwW73{N-xPrTA{r=&w8)5P1*FRb#&q@wYf)R`uh%cQ1%;j=i-K*4qGwl>QhCV?N z7AXWy44XBYSL<52Lm)E2Ak#3mTm(B$Nnxgv$8&u$!IbdvQXDxY;+?X8P(!1I1HV!q zm@}D5?yi&Bi_ZlQ?>kz7@9yaobb}a*L-X%@i+R(eYk;$mbRjo$4UWF^A5pzsWZ_1F z^sHHdv94QGCfY8U0!=0>Iy6%tEe$~FQKy>40$BbUel}|2-#>z%30?C`azEUKj3G5#m zjUzJk_UOnr1#HkMuFY{#TLV7x{n9>x3nQz3P+phPfy?QDs9D&r6IbSNdlfp9e#y~SS$M2ouLK8)G_GI%Pjv48V86e#!NSu!(Cz8=r-@*W=B=PtHkkU5(FTpU0*(6( zrmMztZl{JtX|A?6TTE|?rUDFEZ&vSze6=^1WfjSBEQEpOj}Y3#XEgaR*!4(Xde~1A`Q#2Y zh)E9r$WNxtk+!Jr$!tG=^Z}V*Q2NAL_~bJL-$jPCzc+6xVit(i%NI+tMld=O`t~ z-%7D}NX&5Q`tV29)hey^w=JQp*1^m*=afAQmRRgepsX+e1R1^SJag3aBH!j*rCn5T z0U-hs1N)2a{%;=b&jjy(;pac0=3fwNN)xH$9v?QO%p4Tj-Ca%Y|#byNUvFjjmjQ*bURlbU1;)m}sQ`Gdo9mN%G{ecH$C$RVMFn$qP%UWQM}2n@uIp zwaDq6b#c^d>@|>M3k`LS&)y7N@4V;~h}CabCBZN=tCCvNcNpPX@_9cMsa8IpUQr$( zaXl|g)fw@8>Tq0n_EUx^(voXUG{)CYy;lu1^2m;mM#7bEVnd*qx<3v>nr{>Xn z)8wy}2WLm9*fWSn2X;opv*u|69};2}}dUMb#jci&uwBP9e5b_!lJu3j7zRs>sbh}`#+>7$90p&_| z`V`JAlUr(oU=>g;c&nuw+DKP%u?f8fG>5C`n{)~1xJ@?KtOG>)P!IYiP95w#xI6k; zWEwo1+iHN+Z#AyDFHK3Dqk>B(*HsrTmX(*M3)y#Pd9C+!?YDK(9oGOpR9EMe1p$SZ zVr{{9k5~gpHi+mS3cHW_74F?!jK$s;_mc*^hc{N9iOqnkgR;tE%-OvNA$Ss3OkeFE zYH$DQK){GUIc4~8q}(wv>ite_*pl%X!C&lyFtL)vl$^S7VNHQ`A65K627vzt7#KZy zGiStA6~++y-j`^cNUuXFNq$j^2~MRS`9it_RH!Z~|B7jhjG>ux-^mohHyr*M>{wcXoYEp&gvk_Xs9u{Jg21MN#Nei?b1p4oO}^{?Sa&*5L#yUxg6Ge|RX&_JM*6%%+=!wywVCK5%;Ytc=FUvLqINL_gQ%9qH z`5Fk!m?j+uu>_RroSP0Bw-7;E8tV4F=l?*p>x!22uU0~AP6*!i^NJs_a0SJM!Es8; zJ&nBD%KDs-LV)`)P0633Q2Qd7Ycq-iOycWogn?Ux=#PTHEp(LXZvRCS?oiwF?Yd`N zN^aV=-ot`n2k2skPNUN09gRzvQDK^wbUAe1={FQZa{s4j<^PEtQ|1_((B2E*eiVJ^ zvyS7I`#7y2Jn39p47o+6A)Z;2kE#d{$oJ#pw1 zeeCRmwW-!3=`CIdb~x)Vvc7jkj*XoMCDf*c0Q9_FF3Vr?m@&A0o2C>J<|aUbap<$? zg%9yuu*1a)2lG1C4^k$TXSIycfSrdyy5eg^IwQX2vvJsBO@huZcR*((6i>Mm-3T<# zgC!1sa>vY2!UiFJG;6xO`fi@@q4IMD6IB3a5Vq^+ledpGw4Y;QePBv2$oVf{&5vOA$(M07Zpd} zEB$w`XgYdULVY6J35-A>BY$pP+j;N?R-=$KAq*C{nEdhK^h zwa^mJcIYX7<^f;;JOT#iVDM+Nku)KU@;vz;Cj3wF?ccEX->UI!$fIqnpTL?T9%n8T zhb~<55vWFq;T})R>T&1|Vi{OK)0+)Coz+Evg^IHh3r~}~uJjLpX$?`3e*3gUaZ10c zkj0cytxkO&t1Gh+7%EgPRvEsoG6j#^>@K_{*zGz?w3kPe=u1f1_?6yfYaP!>d zS+=kLeHpBG8=iz|g;%!#!O<@oHgCY#dSk$zAev^l48~1`01~M$t?n+l5qw_QPqs1wcoV=a;7!E|DfEou7YSF$Q-yyndbZ?bVwQ!?qQ_%bJ~%vm^eB>b0<0ZQtZ zae%sREWcaL2DG)@@$Jh!;(ZTMLYW_ILnwEZ=_l19h@Z&qkEc4yn z>ioYM`bLwh1*m<22nLEG)V}y}VIm*l_sR;3bPRByg6g#fFV=u^)Eu>**5^Y#i=khh z?R|F5Im2EiD{i5b_Rv|v37T5g6zPhBM_opMj)(TB5Ba6uch%;$XQPI0uVte)vq9|3 zc!0$A)5J_LInjj9O)|0A-D@Jp$?I+eozZdGO?e7CK`!e zhfInKP)q@u0ghXU=DA>Dl;HUm0O180d}3W{)v4I>yPBD!xKpeFo9hAI>N z_1OK(0sZd-hw@WvsiZ%ozL=cdc~l=+PL+{C&ejc`!CL@n9{=bh`m@$~0RemBb8!q@ z%LZYIkXF<{j#ay1rElm!!K%>lpTEU_Q2a8=gh}-bBZGSe3OTqUG-J3D$k|dX>H`K3 zg@)G%Cn&FJ7~++`-+$4;kLA{v3EDp=3fH<`o~hzfbnRx|E{wU-irh%9O$Mu1YCJV@ z&((sD>e3UV`KE48@*9cbdwrRxl{u59@aD*DoSGn5!AOmXY&+Q5cyeJC3aQqZa5$_F z^Eo-TkdOyC87F;1s$HoW{#Xv%{H3{6O3SW8PXlq$wvFRyp<7pm(gzDi`~d0^JDb{2 z#wl7Rw5UPKc<8;a*}hD9;x_!fVu4yshGki-;MPikpn;sxi;A|u1wH=;mf_Ygl}PPh z0+R&(7GJu`6$P6yVIq>(K0a4+EQO}tGF@|=GJ`<B1{9&JAO9~^G#JVN literal 0 HcmV?d00001 diff --git a/docs/images/objdiff_symbols.png b/docs/images/objdiff_symbols.png new file mode 100644 index 0000000000000000000000000000000000000000..d1af868b56758de12d66e40e132e1da0eea8476b GIT binary patch literal 18567 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 Date: Thu, 20 Jun 2024 18:59:26 +0200 Subject: [PATCH 07/11] Update `inline_assembler.md` --- docs/inline_assembler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/inline_assembler.md b/docs/inline_assembler.md index 0ef8d669..72339c4e 100644 --- a/docs/inline_assembler.md +++ b/docs/inline_assembler.md @@ -21,7 +21,7 @@ dcd 0x1234 #### `ldconst`: Loads a literal 32-bit value ```asm -ldconst 0x1234 +ldconst r0, 0x1234 bx lr ``` This code is equivalent to the above example using `dcd`. From 559dd0fa85fb114a00d1b9e8f4ccb5b46d3c1f88 Mon Sep 17 00:00:00 2001 From: Aetias Date: Fri, 21 Jun 2024 09:06:59 +0200 Subject: [PATCH 08/11] Update decompiling.md --- docs/decompiling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/decompiling.md b/docs/decompiling.md index b31b9d8d..caf723de 100644 --- a/docs/decompiling.md +++ b/docs/decompiling.md @@ -68,7 +68,7 @@ following: 1. Once you're sent to `decomp.me`, go to "Options" and change the preset to "Phantom Hourglass". 1. Paste your code into the "Source code" tab. 1. Share the link with us! -- Add the function as a [non-matching function](/CONTRIBUTING.md#non-matching-functions). +- In the worst case, add the function as a [non-matching function](/CONTRIBUTING.md#non-matching-functions). ## Decompiling `.init` functions > [!NOTE] From 9f495cd49492b0fee6fd06181d7631d85730af5b Mon Sep 17 00:00:00 2001 From: Aetias Date: Fri, 21 Jun 2024 10:11:01 +0200 Subject: [PATCH 09/11] Fix build errors in stubs --- include/Actor/ActorType.hpp | 1 + include/Player/LinkStateRoll.hpp | 1 + src/00_Core/Actor/ActorManager.cpp | 2 +- src/14_Land/Actor/ActorRupee.cpp | 1 - 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/Actor/ActorType.hpp b/include/Actor/ActorType.hpp index dcbc9409..07f1c493 100644 --- a/include/Actor/ActorType.hpp +++ b/include/Actor/ActorType.hpp @@ -75,6 +75,7 @@ struct ActorType { /* 10 */ ActorType *next; /* 14 */ + inline ActorType() {} ActorType(ActorTypeId id, ActorCreateFunc create, unk32 (*unk_08)()); ~ActorType(); unk32 func_0203e7c8(); diff --git a/include/Player/LinkStateRoll.hpp b/include/Player/LinkStateRoll.hpp index 9ff28899..52b42b43 100644 --- a/include/Player/LinkStateRoll.hpp +++ b/include/Player/LinkStateRoll.hpp @@ -42,4 +42,5 @@ public: void func_ov00_020aee58(s16 param1, u16 param2); void func_ov00_020aee84(); + bool func_ov00_020aeeac(); }; diff --git a/src/00_Core/Actor/ActorManager.cpp b/src/00_Core/Actor/ActorManager.cpp index 18401b58..a8a202b3 100644 --- a/src/00_Core/Actor/ActorManager.cpp +++ b/src/00_Core/Actor/ActorManager.cpp @@ -6,7 +6,7 @@ void ActorManager::Actor_vfunc_10(u32 param1) {} Actor* ActorManager::FindActorById(u32 id) {} Actor* ActorManager::GetActor(ActorRef *ref) {} bool FilterActor::Filter(Actor *actor) {} -s32 ActorManager::FilterActors(ActorFilterBase *filter, ActorList *filteredActors) {} +s32 ActorManager::FilterActors(FilterActorBase *filter, ActorList *filteredActors) {} void ActorManager::FindActorByType(ActorRef *ref, ActorManager *manager, u32 type) {} void ActorManager::FindNearestActorOfType(ActorRef *ref, ActorManager *manager, u32 type, Vec3p *pos) {} void ActorManager::func_ov00_020c398c() {} diff --git a/src/14_Land/Actor/ActorRupee.cpp b/src/14_Land/Actor/ActorRupee.cpp index d2641f38..5288f3bd 100644 --- a/src/14_Land/Actor/ActorRupee.cpp +++ b/src/14_Land/Actor/ActorRupee.cpp @@ -1,6 +1,5 @@ #include "Actor/ActorRupee.hpp" -Resource ActorRupee::gResource; ActorType ActorRupee::gType; ActorRupee* ActorRupee::Create() {} From 21e2fce74308ee56c69b88f388b74ff865e16744 Mon Sep 17 00:00:00 2001 From: Aetias Date: Fri, 21 Jun 2024 10:12:14 +0200 Subject: [PATCH 10/11] Remove function definitions in `m2ctx` --- tools/m2ctx.py | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/tools/m2ctx.py b/tools/m2ctx.py index 299d1834..24e12d7a 100755 --- a/tools/m2ctx.py +++ b/tools/m2ctx.py @@ -6,6 +6,8 @@ import subprocess import os from pathlib import Path import argparse +import re +import tempfile parser = argparse.ArgumentParser(description="Generates a context for decomp.me") parser.add_argument('file', help="Input file to preprocess") @@ -28,19 +30,40 @@ root_dir = script_dir / ".." def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) -try: - ctx: str = subprocess.check_output([ - 'gcc', - '-E', '-P', '-fworking-directory', '-undef', '-dD', - *CXX_FLAGS, - args.file - ], cwd=root_dir, encoding=args.encoding) -except subprocess.CalledProcessError as e: - eprint(f"Failed to preprocess '{args.file}'") - if args.verbose: eprint(e) - else: eprint("Use -v for more verbose error output") - exit(1) +# Finds all lines starting with #include followed by <...> or "..." +INCLUDE_REGEX = r'^\s*#\s*include\s*([<"][\S ]+[>"])\s*$' +# Finds all line comments and multiline comments +COMMENT_REGEX = r'\/\/.*$|\/\*(?:.|\r|\n)+?\*\/' +# Finds all lines from #ifndef NONMATCHING to #else +NONMATCH_REGEX = r'^\s*#\s*ifndef\s*NONMATCHING\s*(?:.|\r|\n)*\n\s*#\s*else\s*$' +with open(args.file, 'r') as f: + contents = f.read() +contents = re.sub(COMMENT_REGEX, '', contents, 0, re.MULTILINE) +contents = re.sub(NONMATCH_REGEX, '', contents, 0, re.MULTILINE) +includes = re.findall(INCLUDE_REGEX, contents, re.MULTILINE) + +_, suffix = os.path.splitext(args.file) + +with tempfile.NamedTemporaryFile(delete=True, suffix=suffix) as tmp_file: + # Write includes + for include in includes: + tmp_file.write(f'#include {include}\n'.encode()) + tmp_file.flush() + + # Run preprocessor + try: + ctx: str = subprocess.check_output([ + 'gcc', + '-E', '-P', '-fworking-directory', '-undef', '-dD', + *CXX_FLAGS, + tmp_file.name + ], cwd=root_dir, encoding=args.encoding) + except subprocess.CalledProcessError as e: + eprint(f"Failed to preprocess '{args.file}'") + if args.verbose: eprint(e) + else: eprint("Use -v for more verbose error output") + exit(1) lines = ctx.splitlines(True) for i in reversed(range(len(lines))): From 5f4d3da8679729d138344af3628e15df6030386b Mon Sep 17 00:00:00 2001 From: Aetias Date: Fri, 21 Jun 2024 11:30:48 +0200 Subject: [PATCH 11/11] Update build_system.md --- docs/build_system.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build_system.md b/docs/build_system.md index ee46340f..a81d6e4e 100644 --- a/docs/build_system.md +++ b/docs/build_system.md @@ -56,7 +56,7 @@ When the code is linked, all code of the same section will be written adjacent t ## Compiling code This game was written in C++, so most of the code we decompile will be in this programming language. In C++, we typically don't have to express which section we want the code to be written to. Instead, the compiler determines the section automatically. -Here are a few examples of how to +Here are a few examples of how to generate code for different section types. - `.text` - Functions and member functions (aka methods)