diff --git a/patches.toml b/patches.toml index 38910ee..18a8856 100644 --- a/patches.toml +++ b/patches.toml @@ -9,7 +9,7 @@ single_file_output = true use_absolute_symbols = true # Point the recompiler at the symbol files so that it can resolve relocations during recompilation. func_reference_syms_file = "Zelda64RecompSyms/mm.us.rev1.syms.toml" -data_reference_syms_files = [ "Zelda64RecompSyms/mm.us.rev1.datasyms.toml", "Zelda64RecompSyms/mm.us.rev1.datasyms_static.toml" ] +data_reference_syms_files = [ "Zelda64RecompSyms/mm.us.rev1.datasyms.toml", "Zelda64RecompSyms/mm.us.rev1.datasyms_static.toml", "patches/custom_syms.toml" ] # Tell the recompiler to write the output binary. Doing this instead of using objcopy allows the recompiler to patch MIPS32 relocs. output_binary_path = "patches/patches.bin" # Do not emit warnings for unpaired LO16 values, as clang produces many of them. diff --git a/patches/custom_syms.toml b/patches/custom_syms.toml new file mode 100644 index 0000000..9128278 --- /dev/null +++ b/patches/custom_syms.toml @@ -0,0 +1,16 @@ +# Custom symbols used for patches. +[[section]] +name = "ABSOLUTE_SYMS" +vram = 0x00000000 +size = 0x0 + +symbols = [ + # Alternate references to symbols that would otherwise be relocated + { name = "Setup_Init_NORELOCATE", vram = 0x80173338 }, # Not relocatable, but here for uniformity with the other gamestate funcs + { name = "MapSelect_Init_NORELOCATE", vram = 0x80801B4C }, + { name = "ConsoleLogo_Init_NORELOCATE", vram = 0x8080074C }, + { name = "Play_Init_NORELOCATE", vram = 0x8016A2C8 }, + { name = "TitleSetup_Init_NORELOCATE", vram = 0x80803F30 }, + { name = "FileSelect_Init_NORELOCATE", vram = 0x80813C98 }, + { name = "DayTelop_Init_NORELOCATE", vram = 0x80815820 }, +] diff --git a/patches/options.c b/patches/options.c index c26133c..07c0a23 100644 --- a/patches/options.c +++ b/patches/options.c @@ -1036,6 +1036,9 @@ RECOMP_PATCH void FileSelect_ConfirmFile(GameState *thisx) { } } +// Non-relocatable reference to the original address of Play_Init. +void Play_Init_NORELOCATE(GameState*); + /** * Load the save for the appropriate file and start the game. * Update function for `SM_LOAD_GAME` @@ -1060,7 +1063,7 @@ RECOMP_PATCH void FileSelect_LoadGame(GameState* thisx) { gSaveContext.gameMode = GAMEMODE_NORMAL; STOP_GAMESTATE(&this->state); - SET_NEXT_GAMESTATE(&this->state, Play_Init, sizeof(PlayState)); + SET_NEXT_GAMESTATE(&this->state, Play_Init_NORELOCATE, sizeof(PlayState)); gSaveContext.respawnFlag = 0; gSaveContext.respawn[RESPAWN_MODE_DOWN].entrance = ENTR_LOAD_OPENING; diff --git a/patches/play_patches.c b/patches/play_patches.c index db5deeb..712b612 100644 --- a/patches/play_patches.c +++ b/patches/play_patches.c @@ -1,6 +1,30 @@ #include "play_patches.h" #include "z64debug_display.h" #include "input.h" +#include "prevent_bss_reordering.h" +#include "z64.h" +#include "regs.h" +#include "functions.h" +#include "z64vismono.h" +#include "z64visfbuf.h" +#include "buffers.h" + +#include "variables.h" +#include "macros.h" +#include "buffers.h" +#include "idle.h" +#include "sys_cfb.h" +#include "z64bombers_notebook.h" +#include "z64quake.h" +#include "z64rumble.h" +#include "z64shrink_window.h" +#include "z64view.h" + +#include "overlays/gamestates/ovl_daytelop/z_daytelop.h" +#include "overlays/gamestates/ovl_opening/z_opening.h" +#include "overlays/gamestates/ovl_file_choose/z_file_select.h" +#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" +#include "debug.h" extern Input D_801F6C18; @@ -98,3 +122,305 @@ RECOMP_PATCH s32 Room_HandleLoadCallbacks(PlayState* play, RoomContext* roomCtx) return 1; } + +void ZeldaArena_Init(void* start, size_t size); + +void Play_SpawnScene(PlayState* this, s32 sceneId, s32 spawn); +void Play_InitMotionBlur(void); + +extern s16 sTransitionFillTimer; +extern Input D_801F6C18; +extern TransitionTile sTransitionTile; +extern s32 gTransitionTileState; +extern VisMono sPlayVisMono; +extern Color_RGBA8_u32 gVisMonoColor; +extern VisFbuf sPlayVisFbuf; +extern VisFbuf* sPlayVisFbufInstance; +extern BombersNotebook sBombersNotebook; +extern u8 sBombersNotebookOpen; +extern u8 sMotionBlurStatus; + +extern s32 gDbgCamEnabled; +extern u8 D_801D0D54; + +extern u8 gPictoPhotoI8[]; +extern u8 D_80784600[]; + +// Non-relocatable references to the original addresses of these game state functions. +void DayTelop_Init_NORELOCATE(GameState*); +void TitleSetup_Init_NORELOCATE(GameState*); + +RECOMP_PATCH void Play_Init(GameState* thisx) { + PlayState* this = (PlayState*)thisx; + GraphicsContext* gfxCtx = this->state.gfxCtx; + s32 pad; + uintptr_t zAlloc; + s32 zAllocSize; + Player* player; + s32 i; + s32 spawn; + u8 sceneLayer; + s32 scene; + + if ((gSaveContext.respawnFlag == -4) || (gSaveContext.respawnFlag == -0x63)) { + if (CHECK_EVENTINF(EVENTINF_TRIGGER_DAYTELOP)) { + CLEAR_EVENTINF(EVENTINF_TRIGGER_DAYTELOP); + STOP_GAMESTATE(&this->state); + // Use non-relocatable reference to DayTelop_Init instead. + SET_NEXT_GAMESTATE(&this->state, DayTelop_Init_NORELOCATE, sizeof(DayTelopState)); + return; + } + + gSaveContext.unk_3CA7 = 1; + if (gSaveContext.respawnFlag == -0x63) { + gSaveContext.respawnFlag = 2; + } + } else { + gSaveContext.unk_3CA7 = 0; + } + + if (gSaveContext.save.entrance == -1) { + gSaveContext.save.entrance = 0; + STOP_GAMESTATE(&this->state); + // Use non-relocatable reference to TitleSetup_Init instead. + SET_NEXT_GAMESTATE(&this->state, TitleSetup_Init_NORELOCATE, sizeof(TitleSetupState)); + return; + } + + if ((gSaveContext.nextCutsceneIndex == 0xFFEF) || (gSaveContext.nextCutsceneIndex == 0xFFF0)) { + scene = ((void)0, gSaveContext.save.entrance) >> 9; + spawn = (((void)0, gSaveContext.save.entrance) >> 4) & 0x1F; + + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_SNOWHEAD_TEMPLE)) { + if (scene == ENTR_SCENE_MOUNTAIN_VILLAGE_WINTER) { + scene = ENTR_SCENE_MOUNTAIN_VILLAGE_SPRING; + } else if (scene == ENTR_SCENE_GORON_VILLAGE_WINTER) { + scene = ENTR_SCENE_GORON_VILLAGE_SPRING; + } else if (scene == ENTR_SCENE_PATH_TO_GORON_VILLAGE_WINTER) { + scene = ENTR_SCENE_PATH_TO_GORON_VILLAGE_SPRING; + } else if ((scene == ENTR_SCENE_SNOWHEAD) || (scene == ENTR_SCENE_PATH_TO_SNOWHEAD) || + (scene == ENTR_SCENE_PATH_TO_MOUNTAIN_VILLAGE) || (scene == ENTR_SCENE_GORON_SHRINE) || + (scene == ENTR_SCENE_GORON_RACETRACK)) { + gSaveContext.nextCutsceneIndex = 0xFFF0; + } + } + + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_WOODFALL_TEMPLE)) { + if (scene == ENTR_SCENE_SOUTHERN_SWAMP_POISONED) { + scene = ENTR_SCENE_SOUTHERN_SWAMP_CLEARED; + } else if (scene == ENTR_SCENE_WOODFALL) { + gSaveContext.nextCutsceneIndex = 0xFFF1; + } + } + + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_STONE_TOWER_TEMPLE) && (scene == ENTR_SCENE_IKANA_CANYON)) { + gSaveContext.nextCutsceneIndex = 0xFFF2; + } + + if (CHECK_WEEKEVENTREG(WEEKEVENTREG_CLEARED_GREAT_BAY_TEMPLE) && + ((scene == ENTR_SCENE_GREAT_BAY_COAST) || (scene == ENTR_SCENE_ZORA_CAPE))) { + gSaveContext.nextCutsceneIndex = 0xFFF0; + } + + // "First cycle" Termina Field + if (INV_CONTENT(ITEM_OCARINA_OF_TIME) != ITEM_OCARINA_OF_TIME) { + if ((scene == ENTR_SCENE_TERMINA_FIELD) && + (((void)0, gSaveContext.save.entrance) != ENTRANCE(TERMINA_FIELD, 10))) { + gSaveContext.nextCutsceneIndex = 0xFFF4; + } + } + //! FAKE: + gSaveContext.save.entrance = + Entrance_Create(((void)0, scene), spawn, ((void)0, gSaveContext.save.entrance) & 0xF); + } + + GameState_Realloc(&this->state, 0); + KaleidoManager_Init(this); + ShrinkWindow_Init(); + View_Init(&this->view, gfxCtx); + Audio_SetExtraFilter(0); + Quake_Init(); + Distortion_Init(this); + + for (i = 0; i < ARRAY_COUNT(this->cameraPtrs); i++) { + this->cameraPtrs[i] = NULL; + } + + Camera_Init(&this->mainCamera, &this->view, &this->colCtx, this); + Camera_ChangeStatus(&this->mainCamera, CAM_STATUS_ACTIVE); + + for (i = 0; i < ARRAY_COUNT(this->subCameras); i++) { + Camera_Init(&this->subCameras[i], &this->view, &this->colCtx, this); + Camera_ChangeStatus(&this->subCameras[i], CAM_STATUS_INACTIVE); + } + + this->cameraPtrs[CAM_ID_MAIN] = &this->mainCamera; + this->cameraPtrs[CAM_ID_MAIN]->uid = CAM_ID_MAIN; + this->activeCamId = CAM_ID_MAIN; + + Camera_OverwriteStateFlags(&this->mainCamera, CAM_STATE_0 | CAM_STATE_CHECK_WATER | CAM_STATE_2 | CAM_STATE_3 | + CAM_STATE_4 | CAM_STATE_DISABLE_MODE_CHANGE | CAM_STATE_6); + Sram_Alloc(&this->state, &this->sramCtx); + Regs_InitData(this); + Message_Init(this); + GameOver_Init(this); + SoundSource_InitAll(this); + EffFootmark_Init(this); + Effect_Init(this); + EffectSS_Init(this, 100); + CollisionCheck_InitContext(this, &this->colChkCtx); + AnimationContext_Reset(&this->animationCtx); + Cutscene_InitContext(this, &this->csCtx); + + if (gSaveContext.nextCutsceneIndex != 0xFFEF) { + gSaveContext.save.cutsceneIndex = gSaveContext.nextCutsceneIndex; + gSaveContext.nextCutsceneIndex = 0xFFEF; + } + + if (gSaveContext.save.cutsceneIndex == 0xFFFD) { + gSaveContext.save.cutsceneIndex = 0; + } + + if (gSaveContext.nextDayTime != NEXT_TIME_NONE) { + gSaveContext.save.time = gSaveContext.nextDayTime; + gSaveContext.skyboxTime = gSaveContext.nextDayTime; + } + + if ((CURRENT_TIME >= CLOCK_TIME(18, 0)) || (CURRENT_TIME < CLOCK_TIME(6, 30))) { + gSaveContext.save.isNight = true; + } else { + gSaveContext.save.isNight = false; + } + + func_800EDDB0(this); + + if (((gSaveContext.gameMode != GAMEMODE_NORMAL) && (gSaveContext.gameMode != GAMEMODE_TITLE_SCREEN)) || + (gSaveContext.save.cutsceneIndex >= 0xFFF0)) { + gSaveContext.nayrusLoveTimer = 0; + Magic_Reset(this); + gSaveContext.sceneLayer = (gSaveContext.save.cutsceneIndex & 0xF) + 1; + + // Set saved cutscene to 0 so it doesn't immediately play, but instead let the `CutsceneManager` handle it. + gSaveContext.save.cutsceneIndex = 0; + } else { + gSaveContext.sceneLayer = 0; + } + + sceneLayer = gSaveContext.sceneLayer; + + Play_SpawnScene( + this, Entrance_GetSceneIdAbsolute(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer)), + Entrance_GetSpawnNum(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer))); + KaleidoScopeCall_Init(this); + Interface_Init(this); + + if (gSaveContext.nextDayTime != NEXT_TIME_NONE) { + if (gSaveContext.nextDayTime == NEXT_TIME_DAY) { + gSaveContext.save.day++; + gSaveContext.save.eventDayCount++; + gSaveContext.dogIsLost = true; + gSaveContext.nextDayTime = NEXT_TIME_DAY_SET; + } else { + gSaveContext.nextDayTime = NEXT_TIME_NIGHT_SET; + } + } + + Play_InitMotionBlur(); + + R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_OFF; + R_PICTO_PHOTO_STATE = PICTO_PHOTO_STATE_OFF; + + PreRender_Init(&this->pauseBgPreRender); + PreRender_SetValuesSave(&this->pauseBgPreRender, gCfbWidth, gCfbHeight, NULL, NULL, NULL); + PreRender_SetValues(&this->pauseBgPreRender, gCfbWidth, gCfbHeight, NULL, NULL); + + this->unk_18E64 = gWorkBuffer; + this->pictoPhotoI8 = gPictoPhotoI8; + this->unk_18E68 = D_80784600; + this->unk_18E58 = D_80784600; + this->unk_18E60 = D_80784600; + gTransitionTileState = TRANS_TILE_OFF; + this->transitionMode = TRANS_MODE_OFF; + D_801D0D54 = false; + + FrameAdvance_Init(&this->frameAdvCtx); + Rand_Seed(osGetTime()); + Matrix_Init(&this->state); + + this->state.main = Play_Main; + this->state.destroy = Play_Destroy; + + this->transitionTrigger = TRANS_TRIGGER_END; + this->worldCoverAlpha = 0; + this->bgCoverAlpha = 0; + this->haltAllActors = false; + this->unk_18844 = false; + + if (gSaveContext.gameMode != GAMEMODE_TITLE_SCREEN) { + if (gSaveContext.nextTransitionType == TRANS_NEXT_TYPE_DEFAULT) { + this->transitionType = + (Entrance_GetTransitionFlags(((void)0, gSaveContext.save.entrance) + sceneLayer) >> 7) & 0x7F; + } else { + this->transitionType = gSaveContext.nextTransitionType; + gSaveContext.nextTransitionType = TRANS_NEXT_TYPE_DEFAULT; + } + } else { + this->transitionType = TRANS_TYPE_FADE_BLACK; + } + + TransitionFade_Init(&this->unk_18E48); + TransitionFade_SetType(&this->unk_18E48, 3); + TransitionFade_SetColor(&this->unk_18E48, RGBA8(160, 160, 160, 255)); + TransitionFade_Start(&this->unk_18E48); + VisMono_Init(&sPlayVisMono); + + gVisMonoColor.a = 0; + sPlayVisFbufInstance = &sPlayVisFbuf; + VisFbuf_Init(sPlayVisFbufInstance); + sPlayVisFbufInstance->lodProportion = 0.0f; + sPlayVisFbufInstance->mode = VIS_FBUF_MODE_GENERAL; + sPlayVisFbufInstance->primColor.r = 0; + sPlayVisFbufInstance->primColor.g = 0; + sPlayVisFbufInstance->primColor.b = 0; + sPlayVisFbufInstance->primColor.a = 0; + sPlayVisFbufInstance->envColor.r = 0; + sPlayVisFbufInstance->envColor.g = 0; + sPlayVisFbufInstance->envColor.b = 0; + sPlayVisFbufInstance->envColor.a = 0; + CutsceneFlags_UnsetAll(this); + THA_GetRemaining(&this->state.tha); + zAllocSize = THA_GetRemaining(&this->state.tha); + zAlloc = (uintptr_t)THA_AllocTailAlign16(&this->state.tha, zAllocSize); + + //! @bug: Incorrect ALIGN16s + ZeldaArena_Init((void*)((zAlloc + 8) & ~0xF), (zAllocSize - ((zAlloc + 8) & ~0xF)) + zAlloc); + + Actor_InitContext(this, &this->actorCtx, this->linkActorEntry); + + while (!Room_HandleLoadCallbacks(this, &this->roomCtx)) {} + + if ((CURRENT_DAY != 0) && ((this->roomCtx.curRoom.behaviorType1 == ROOM_BEHAVIOR_TYPE1_1) || + (this->roomCtx.curRoom.behaviorType1 == ROOM_BEHAVIOR_TYPE1_5))) { + Actor_Spawn(&this->actorCtx, this, ACTOR_EN_TEST4, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0); + } + + player = GET_PLAYER(this); + + Camera_InitFocalActorSettings(&this->mainCamera, &player->actor); + gDbgCamEnabled = false; + + if (PLAYER_GET_BG_CAM_INDEX(&player->actor) != 0xFF) { + Camera_ChangeActorCsCamIndex(&this->mainCamera, PLAYER_GET_BG_CAM_INDEX(&player->actor)); + } + + CutsceneManager_StoreCamera(&this->mainCamera); + Interface_SetSceneRestrictions(this); + Environment_PlaySceneSequence(this); + gSaveContext.seqId = this->sequenceCtx.seqId; + gSaveContext.ambienceId = this->sequenceCtx.ambienceId; + AnimationContext_Update(this, &this->animationCtx); + Cutscene_HandleEntranceTriggers(this); + gSaveContext.respawnFlag = 0; + sBombersNotebookOpen = false; + BombersNotebook_Init(&sBombersNotebook); +}