From 5832f70384cdf7d8b35c79397b40d1bb47cb482d Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sun, 1 Mar 2026 14:34:07 +0100 Subject: [PATCH] process class inheritance vtable fix --- include/f_op/f_op_actor.h | 14 ++++++++++++++ include/f_op/f_op_actor_mng.h | 11 ++++++++--- include/f_pc/f_pc_base.h | 10 +++++++++- include/f_pc/f_pc_leaf.h | 8 ++++++++ src/d/actor/d_a_alink.cpp | 2 +- src/d/actor/d_a_grass.cpp | 2 +- src/d/actor/d_a_mg_fish.cpp | 2 +- src/d/actor/d_a_obj_smtile.cpp | 2 +- src/f_pc/f_pc_base.cpp | 6 ++++++ src/f_pc/f_pc_leaf.cpp | 8 ++++---- 10 files changed, 53 insertions(+), 12 deletions(-) diff --git a/include/f_op/f_op_actor.h b/include/f_op/f_op_actor.h index 100b4f8883..ccf559aed5 100644 --- a/include/f_op/f_op_actor.h +++ b/include/f_op/f_op_actor.h @@ -240,8 +240,14 @@ struct cull_box { /* 0xC */ Vec max; }; +#if __MWERKS__ +class fopAc_ac_c { +public: + /* 0x000 */ leafdraw_class base; +#else class fopAc_ac_c : public leafdraw_class { public: +#endif /* 0x0C0 */ int actor_type; /* 0x0C4 */ create_tag_class actor_tag; /* 0x0D8 */ create_tag_class draw_tag; @@ -284,8 +290,16 @@ public: /* 0x566 */ s8 field_0x566; /* 0x567 */ s8 field_0x567; +#if !__MWERKS__ + s8 actor_last_base_field; +#endif + fopAc_ac_c(); +#if !__MWERKS__ + ~fopAc_ac_c() override; +#else ~fopAc_ac_c(); +#endif static u32 getStopStatus() { return stopStatus; } static void setStopStatus(u32 status) { stopStatus = status; } diff --git a/include/f_op/f_op_actor_mng.h b/include/f_op/f_op_actor_mng.h index f4e5c4f60c..08d268c07b 100644 --- a/include/f_op/f_op_actor_mng.h +++ b/include/f_op/f_op_actor_mng.h @@ -17,20 +17,25 @@ // Modern compilers will zero the parent struct in default constructors. // So instead of adding default constructors to everything, // we'll just save & restore that data. +#define fopAcM_ct_placement_copy_length offsetof(fopAc_ac_c, actor_last_base_field) - offsetof(fopAc_ac_c, type) + #define fopAcM_ct_placement(ptr, ClassName) \ fopAc_ac_c copy; \ - memcpy(©, &(ptr)->base, sizeof(fopAc_ac_c)); \ + memcpy(©.type, &(ptr)->type, fopAcM_ct_placement_copy_length); \ new (ptr) ClassName() ; \ - memcpy(&(ptr)->base, ©, sizeof(fopAc_ac_c)); + memcpy(&(ptr)->type, ©.type, fopAcM_ct_placement_copy_length); #else #define fopAcM_ct_placement(ptr, ClassName) new (ptr) ClassName() #endif #define fopAcM_ct(ptr, ClassName) \ + if ((ptr)->layer_tag.layer == NULL) { OSPanic(__FILE__, __LINE__, "UH OH"); } \ if (!fopAcM_CheckCondition(ptr, fopAcCnd_INIT_e)) { \ fopAcM_ct_placement(ptr, ClassName); \ fopAcM_OnCondition(ptr, fopAcCnd_INIT_e); \ - } + } \ + if ((ptr)->layer_tag.layer == NULL) { OSPanic(__FILE__, __LINE__, "Oh come on"); } + #define fopAcM_RegisterDeleteID(i_this, actor_name_str) \ ("Delete -> " actor_name_str "(id=%d)\n", fopAcM_GetID(i_this)) diff --git a/include/f_pc/f_pc_base.h b/include/f_pc/f_pc_base.h index 896e7a9c9d..4910b1e86a 100644 --- a/include/f_pc/f_pc_base.h +++ b/include/f_pc/f_pc_base.h @@ -20,7 +20,7 @@ typedef struct state_class { } state_class; typedef struct base_process_class { - /* 0x00 */ int type; + /* 0x00 */ int type; // DUSK NOTE: fopAcM_ct_placement relies on this being the *very* first field! /* 0x04 */ fpc_ProcID id; /* 0x08 */ s16 name; /* 0x0A */ s8 unk_0xA; @@ -37,6 +37,14 @@ typedef struct base_process_class { /* 0xAC */ void* append; /* 0xB0 */ u32 parameters; /* 0xB4 */ int subtype; +#if !__MWERKS__ + // MSVC places vtables at the start of a class, *even* if that class inherits from something + // without vtable. This breaks everything. + // TO avoid issues with pointer casting, we make base_process_class also have a vtable and + // ensure we're using inheritance on it. + + virtual ~base_process_class(); +#endif } base_process_class; // Size: 0xB8 BOOL fpcBs_Is_JustOfType(int i_typeA, int i_typeB); diff --git a/include/f_pc/f_pc_leaf.h b/include/f_pc/f_pc_leaf.h index d9de82659c..413cdae871 100644 --- a/include/f_pc/f_pc_leaf.h +++ b/include/f_pc/f_pc_leaf.h @@ -13,8 +13,16 @@ typedef struct leafdraw_method_class { /* 0x10 */ process_method_func draw_method; } leafdraw_method_class; +#if __MWERKS__ +#define LEAFDRAW_BASE(val) (val)->base + typedef struct leafdraw_class { /* 0x00 */ base_process_class base; +#else +#define LEAFDRAW_BASE(val) (*(base_process_class*)val) + +typedef struct leafdraw_class : base_process_class { +#endif /* 0xB8 */ leafdraw_method_class* leaf_methods; /* 0xBC */ s8 unk_0xBC; /* 0xBD */ u8 unk_0xBD; diff --git a/src/d/actor/d_a_alink.cpp b/src/d/actor/d_a_alink.cpp index fc2f665ab7..a0bcf8ac3f 100644 --- a/src/d/actor/d_a_alink.cpp +++ b/src/d/actor/d_a_alink.cpp @@ -4857,7 +4857,7 @@ int daAlink_c::create() { dComIfGp_setPlayer(0, this); dComIfGp_setLinkPlayer(this); - fopAcM_setStageLayer(&base); + fopAcM_setStageLayer(&LEAFDRAW_BASE(this)); if (sceneMode == 7) { current.pos = dComIfGs_getTurnRestartPos(); diff --git a/src/d/actor/d_a_grass.cpp b/src/d/actor/d_a_grass.cpp index de07ca975f..f046529adf 100644 --- a/src/d/actor/d_a_grass.cpp +++ b/src/d/actor/d_a_grass.cpp @@ -350,7 +350,7 @@ int daGrass_c::create() { } m_myObj = this; - fopAcM_setStageLayer(&base); + fopAcM_setStageLayer(&LEAFDRAW_BASE(this)); return cPhs_COMPLEATE_e; } diff --git a/src/d/actor/d_a_mg_fish.cpp b/src/d/actor/d_a_mg_fish.cpp index 2fdb98d877..7edf1bfa11 100644 --- a/src/d/actor/d_a_mg_fish.cpp +++ b/src/d/actor/d_a_mg_fish.cpp @@ -119,7 +119,7 @@ static void* s_hitfish_sub(void* a, void* b) { if (fopAc_IsActor(a)) { mg_fish_class* fish = (mg_fish_class*)a; if (fopAcM_GetName(fish) == PROC_MG_FISH && fish->mCurAction == ACTION_MG_FISH_MF_HIT) { - return &fish->actor.base; + return &LEAFDRAW_BASE(&fish->actor); } } return NULL; diff --git a/src/d/actor/d_a_obj_smtile.cpp b/src/d/actor/d_a_obj_smtile.cpp index ebe2baaffa..6738908a2f 100644 --- a/src/d/actor/d_a_obj_smtile.cpp +++ b/src/d/actor/d_a_obj_smtile.cpp @@ -278,7 +278,7 @@ void daObj_SMTile_c::touchPrtcls(f32 param_1) { for (int i = 0; i < 21; i++) { if ((field_0xa28[i] == 1) || (field_0xa28[i] == 2)) { mParticleIds[i] = - dComIfGp_particle_set(mParticleIds[i], id[field_0xa28[i]], &field_0x788[i], 0, 0); + dComIfGp_particle_set(mParticleIds[i], ::id[field_0xa28[i]], &field_0x788[i], 0, 0); JPABaseEmitter* emitter = dComIfGp_particle_getEmitter(mParticleIds[i]); if (emitter != NULL) { f32 dVar6 = mpHIO->m.field_0x4 - mParticleTimers[i]; diff --git a/src/f_pc/f_pc_base.cpp b/src/f_pc/f_pc_base.cpp index 8e2e3f6a5e..bbeac76038 100644 --- a/src/f_pc/f_pc_base.cpp +++ b/src/f_pc/f_pc_base.cpp @@ -171,3 +171,9 @@ int fpcBs_SubCreate(base_process_class* i_proc) { return cPhs_ERROR_e; } } + +#if !__MWERKS__ +base_process_class::~base_process_class() { + // Nada. Only exists to ensure the base class has a vtable. +} +#endif diff --git a/src/f_pc/f_pc_leaf.cpp b/src/f_pc/f_pc_leaf.cpp index c3ab1e6c73..eab6532a92 100644 --- a/src/f_pc/f_pc_leaf.cpp +++ b/src/f_pc/f_pc_leaf.cpp @@ -42,7 +42,7 @@ int fpcLf_Delete(leafdraw_class* i_leaf) { int ret = fpcMtd_Delete(&i_leaf->leaf_methods->base, i_leaf); UNUSED(ret); // possible fakematch? this line fixes debug regalloc if (ret == 1) { - i_leaf->base.subtype = 0; + LEAFDRAW_BASE(i_leaf).subtype = 0; } return ret; } @@ -50,10 +50,10 @@ int fpcLf_Delete(leafdraw_class* i_leaf) { int g_fpcLf_type; int fpcLf_Create(leafdraw_class* i_leaf) { - if (i_leaf->base.state.init_state == 0) { - leaf_process_profile_definition* pprofile = (leaf_process_profile_definition*)i_leaf->base.profile; + if (LEAFDRAW_BASE(i_leaf).state.init_state == 0) { + leaf_process_profile_definition* pprofile = (leaf_process_profile_definition*)LEAFDRAW_BASE(i_leaf).profile; i_leaf->leaf_methods = pprofile->sub_method; - i_leaf->base.subtype = fpcBs_MakeOfType(&g_fpcLf_type); + LEAFDRAW_BASE(i_leaf).subtype = fpcBs_MakeOfType(&g_fpcLf_type); fpcDwPi_Init(&i_leaf->draw_priority, pprofile->priority); i_leaf->unk_0xBC = 0; }