Add level model hotpatch for intro scene to fix gap in widescreen

Co-authored-by: Reonu <15913880+Reonu@users.noreply.github.com>
This commit is contained in:
Mr-Wiseguy
2025-12-13 20:14:03 -05:00
parent bb434b7757
commit b8e081d6e2
5 changed files with 145 additions and 21 deletions
+125
View File
@@ -0,0 +1,125 @@
#include "patches.h"
#include "misc_funcs.h"
#include "functions.h"
#include "bk_api.h"
#include "object_extension_funcs.h"
extern ActorMarker *D_8036E7C8;
extern u8 D_80383428[0x1C];
typedef struct {
s16 map_id; //enum map_e
s16 opa_model_id; //enum asset_e level_model_id
s16 xlu_model_id; //enum asset_e level2_model_id
s16 unk6[3]; // min bounds (for cubes?)
s16 unkC[3]; // max bounds (for cubes?)
// u8 pad12[0x2];
f32 scale;
}MapModelDescription;
extern struct {
void *unk0;
void *unk4;
BKCollisionList *collision_opa;
BKCollisionList *collision_xlu;
BKModel *model_opa;
BKModel *model_xlu;
BKModelBin *model_bin_opa;
BKModelBin *model_bin_xlu;
s32 unk20;
struct5Bs *unk24;
MapModelDescription *description;
u8 env_red;
u8 env_green;
u8 env_blue;
f32 scale;
}mapModel;
BKGfxList *model_getDisplayList(BKModelBin *arg0);
#define INTRO_OPA_DL_LENGTH 97
#define INTRO_OPA_DL_GRASS_PATCH_INDEX 63
#define INTRO_OPA_DL_WALL_PATCH_INDEX 84
#define INTRO_OPA_DL_HASH 0xA912614C535FA0BBULL
Vtx intro_grass_extension_verts[3] = {
{{ {-1262, 20, 1040}, 0, {214, 522}, {51, 190, 133, 255} }},
{{ {-10, 20, 5107}, 0, {3108, -10083}, {0, 13, 169, 255} }},
{{ {-10, 20, 1372}, 0, {3360, -211}, {51, 190, 133, 255} }},
};
Gfx intro_grass_extension_dl[] = {
// Copy of the replaced command.
gsSP1Triangle(31, 28, 29, 0),
// New commands.
gsSPVertex(intro_grass_extension_verts + 0, 3, 0),
gsSP1Triangle(0, 1, 2, 0),
gsSPEndDisplayList(),
};
Vtx intro_wall_extension_verts[4] = {
{{ {-1262, 20, 1040}, 0, {-31, 33}, {51, 190, 133, 255} }},
{{ {-1262, 1004, 1040}, 0, {-60, 1950}, {255, 255, 255, 255} }},
{{ {189, 1004, 4315}, 0, {-7416, 1801}, {255, 255, 255, 255} }},
{{ {189, 20, 4315}, 0, {-7387, -116}, {51, 190, 133, 255} }},
};
Gfx intro_wall_extension_dl[] = {
// Copy of the replaced command.
gsSP1Triangle(22, 24, 21, 0),
// New commands.
gsSPVertex(intro_wall_extension_verts + 0, 4, 0),
gsSP2Triangles(0, 1, 2, 0, 0, 2, 3, 0),
gsSPEndDisplayList(),
};
void hotpatch_intro_opa_map_model(BKModelBin* model_bin) {
BKGfxList *gfx_list = model_getDisplayList(model_bin);
Gfx* dl = &gfx_list->list[0];
// Hash the displaylist of the model to make sure it's unmodified. This will prevent the hotpatch from
// affecting mods.
u64 hash = recomp_xxh3(dl, INTRO_OPA_DL_LENGTH * sizeof(Gfx));
if (hash != INTRO_OPA_DL_HASH) {
return;
}
// Patch a call to the new displaylist after the grass material.
gSPDisplayList(&dl[INTRO_OPA_DL_GRASS_PATCH_INDEX], intro_grass_extension_dl);
// Patch a call to the new displaylist after the wall material.
gSPDisplayList(&dl[INTRO_OPA_DL_WALL_PATCH_INDEX], intro_wall_extension_dl);
}
// @recomp Patched to act as a point to run code when a new map is loaded.
// This includes:
// * Resetting all extended marker data and skip interpolation for the next frame.
// * Hotpatching the map model for the title cutscene to fix ultrawide effects.
RECOMP_PATCH void func_803329AC(void){
s32 i;
D_8036E7C8 = (ActorMarker *)malloc(0xE0*sizeof(ActorMarker));
for( i = 0; i < 0x1C; i++){
D_80383428[i] = 0;
}
for(i =0; i<0xE0; i++){
D_8036E7C8[i].unk5C = 0;
}
// @recomp Run any new code on map load.
// @recomp If the current map's model is ASSET_149D_MODEL_CS_START_NINTENDO_OPA,
// hotpatch it to fill in some regions for widescreen.
if (mapModel.description->opa_model_id == ASSET_149D_MODEL_CS_START_NINTENDO_OPA) {
hotpatch_intro_opa_map_model(mapModel.model_bin_opa);
}
// @recomp Reset all actor data and skip interpolation for the next frame.
// Interpolation is skipped as the next frame will potentially reuse IDs from the previous frame,
// as the marker ID tracking gets reset here.
recomp_clear_all_object_data(EXTENSION_TYPE_MARKER);
set_all_interpolation_skipped(TRUE);
}
-21
View File
@@ -15,27 +15,6 @@ extern u8 D_80383428[0x1C];
void func_8032F3D4(s32 arg0[3], ActorMarker *marker, s32 arg2);
ActorMarker * func_80332A60(void);
// @recomp Patched to reset all extended marker data and skip interpolation for the next frame.
RECOMP_PATCH void func_803329AC(void){
s32 i;
D_8036E7C8 = (ActorMarker *)malloc(0xE0*sizeof(ActorMarker));
for( i = 0; i < 0x1C; i++){
D_80383428[i] = 0;
}
for(i =0; i<0xE0; i++){
D_8036E7C8[i].unk5C = 0;
}
// @recomp Reset all actor data and skip interpolation for the next frame.
// Interpolation is skipped as the next frame will potentially reuse IDs from the previous frame,
// as the marker ID tracking gets reset here.
recomp_clear_all_object_data(EXTENSION_TYPE_MARKER);
set_all_interpolation_skipped(TRUE);
}
// @recomp Patched to create extension data for the marker.
RECOMP_PATCH ActorMarker * marker_init(s32 *pos, MarkerDrawFunc draw_func, int arg2, int marker_id, int arg4){
ActorMarker * marker = func_80332A60();
+1
View File
@@ -7,5 +7,6 @@ DECLARE_FUNC(void, recomp_load_overlays, u32 rom, void* ram, u32 size);
DECLARE_FUNC(void, recomp_puts, const char* data, u32 size);
DECLARE_FUNC(void, recomp_exit);
DECLARE_FUNC(void, recomp_error, const char* str);
DECLARE_FUNC(u64, recomp_xxh3, void* data, u32 size);
#endif
+1
View File
@@ -36,3 +36,4 @@ osPiStartDma_recomp = 0x8F00007C;
recomp_abort = 0x8F000080;
recomp_get_target_aspect_ratio = 0x8F000084;
osExQueueDisplaylistEvent_recomp = 0x8F000088;
recomp_xxh3 = 0x8F00008C;
+18
View File
@@ -14,6 +14,7 @@
#include "../patches/sound.h"
#include "ultramodern/ultramodern.hpp"
#include "ultramodern/config.hpp"
#include "../lib/N64ModernRuntime/thirdparty/xxHash/xxh3.h"
extern "C" void recomp_update_inputs(uint8_t* rdram, recomp_context* ctx) {
recomp::poll_inputs();
@@ -219,3 +220,20 @@ extern "C" void recomp_abort(uint8_t* rdram, recomp_context* ctx) {
assert(false);
ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__);
}
extern "C" void recomp_xxh3(uint8_t* rdram, recomp_context* ctx) {
PTR(void) data = _arg<0, PTR(void)>(rdram, ctx);
u32 size = _arg<1, u32>(rdram, ctx);
XXH3_state_t xxh3;
XXH3_64bits_reset(&xxh3);
// Hash 1 byte at a time to account for byteswapping.
for (size_t i = 0; i < size; i++) {
XXH3_64bits_update(&xxh3, TO_PTR(u8, data + i), 1);
}
uint64_t ret = XXH3_64bits_digest(&xxh3);
ctx->r2 = (int32_t)(ret >> 32);
ctx->r3 = (int32_t)(ret >> 0);
}