diff --git a/soh/assets/objects/gameplay_keep/gameplay_keep.h b/soh/assets/objects/gameplay_keep/gameplay_keep.h index c67086b94..eebb19ab1 100644 --- a/soh/assets/objects/gameplay_keep/gameplay_keep.h +++ b/soh/assets/objects/gameplay_keep/gameplay_keep.h @@ -20,6 +20,9 @@ static const ALIGN_ASSET(2) char gBottleGlassTex[] = dgBottleGlassTex; #define dgDekuStickTex "__OTR__objects/gameplay_keep/gDekuStickTex" static const ALIGN_ASSET(2) char gDekuStickTex[] = dgDekuStickTex; +#define dgDekuStickOverflowTex "__OTR__objects/gameplay_keep/gDekuStickOverflowTex" +static const ALIGN_ASSET(2) char gDekuStickOverflowTex[] = dgDekuStickOverflowTex; + #define dgLinkHairTex "__OTR__objects/gameplay_keep/gLinkHairTex" static const ALIGN_ASSET(2) char gLinkHairTex[] = dgLinkHairTex; @@ -2375,6 +2378,9 @@ static const ALIGN_ASSET(2) char gEffUnknown11Tex[] = dgEffUnknown11Tex; #define dgEffUnknown12Tex "__OTR__objects/gameplay_keep/gEffUnknown12Tex" static const ALIGN_ASSET(2) char gEffUnknown12Tex[] = dgEffUnknown12Tex; +#define dgEffUnknown12OverflowTex "__OTR__objects/gameplay_keep/gEffUnknown12OverflowTex" +static const ALIGN_ASSET(2) char gEffUnknown12OverflowTex[] = dgEffUnknown12OverflowTex; + #define dgUnknownWoodBoardTex "__OTR__objects/gameplay_keep/gUnknownWoodBoardTex" static const ALIGN_ASSET(2) char gUnknownWoodBoardTex[] = dgUnknownWoodBoardTex; diff --git a/soh/assets/objects/object_ik/object_ik.h b/soh/assets/objects/object_ik/object_ik.h index 5996b0cf3..37c46d207 100644 --- a/soh/assets/objects/object_ik/object_ik.h +++ b/soh/assets/objects/object_ik/object_ik.h @@ -101,6 +101,9 @@ static const ALIGN_ASSET(2) char object_ik_Tlut_00F630[] = dobject_ik_Tlut_00F63 #define dobject_ik_Tex_00F7A0 "__OTR__objects/object_ik/object_ik_Tex_00F7A0" static const ALIGN_ASSET(2) char object_ik_Tex_00F7A0[] = dobject_ik_Tex_00F7A0; +#define dgIronKnuckleMetalOverflowTex "__OTR__objects/object_ik/gIronKnuckleMetalOverflowTex" +static const ALIGN_ASSET(2) char gIronKnuckleMetalOverflowTex[] = dgIronKnuckleMetalOverflowTex; + #define dobject_ik_Tex_00FBA0 "__OTR__objects/object_ik/object_ik_Tex_00FBA0" static const ALIGN_ASSET(2) char object_ik_Tex_00FBA0[] = dobject_ik_Tex_00FBA0; diff --git a/soh/assets/xml/GC_MQ_D/objects/gameplay_keep.xml b/soh/assets/xml/GC_MQ_D/objects/gameplay_keep.xml index c3011f8fd..2a8ca9b44 100644 --- a/soh/assets/xml/GC_MQ_D/objects/gameplay_keep.xml +++ b/soh/assets/xml/GC_MQ_D/objects/gameplay_keep.xml @@ -7,6 +7,10 @@ + + + @@ -809,6 +813,10 @@ + + + diff --git a/soh/assets/xml/GC_MQ_D/objects/object_ik.xml b/soh/assets/xml/GC_MQ_D/objects/object_ik.xml index 95606e6df..7d10babfa 100644 --- a/soh/assets/xml/GC_MQ_D/objects/object_ik.xml +++ b/soh/assets/xml/GC_MQ_D/objects/object_ik.xml @@ -38,6 +38,10 @@ + + + diff --git a/soh/assets/xml/GC_NMQ_D/objects/gameplay_keep.xml b/soh/assets/xml/GC_NMQ_D/objects/gameplay_keep.xml index fbcec6f37..28b2bab81 100644 --- a/soh/assets/xml/GC_NMQ_D/objects/gameplay_keep.xml +++ b/soh/assets/xml/GC_NMQ_D/objects/gameplay_keep.xml @@ -7,6 +7,10 @@ + + + @@ -806,6 +810,10 @@ + + + diff --git a/soh/assets/xml/GC_NMQ_D/objects/object_ik.xml b/soh/assets/xml/GC_NMQ_D/objects/object_ik.xml index 95606e6df..7d10babfa 100644 --- a/soh/assets/xml/GC_NMQ_D/objects/object_ik.xml +++ b/soh/assets/xml/GC_NMQ_D/objects/object_ik.xml @@ -38,6 +38,10 @@ + + + diff --git a/soh/assets/xml/GC_NMQ_PAL_F/objects/gameplay_keep.xml b/soh/assets/xml/GC_NMQ_PAL_F/objects/gameplay_keep.xml index fbcec6f37..28b2bab81 100644 --- a/soh/assets/xml/GC_NMQ_PAL_F/objects/gameplay_keep.xml +++ b/soh/assets/xml/GC_NMQ_PAL_F/objects/gameplay_keep.xml @@ -7,6 +7,10 @@ + + + @@ -806,6 +810,10 @@ + + + diff --git a/soh/assets/xml/GC_NMQ_PAL_F/objects/object_ik.xml b/soh/assets/xml/GC_NMQ_PAL_F/objects/object_ik.xml index 95606e6df..7d10babfa 100644 --- a/soh/assets/xml/GC_NMQ_PAL_F/objects/object_ik.xml +++ b/soh/assets/xml/GC_NMQ_PAL_F/objects/object_ik.xml @@ -38,6 +38,10 @@ + + + diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index d23cf73d6..7e13fd598 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -1,4 +1,5 @@ #include "CosmeticsEditor.h" +#include "authenticGfxPatches.h" #include #include "soh/Enhancements/game-interactor/GameInteractor.h" @@ -1817,6 +1818,7 @@ void InitCosmeticsEditor() { } SohImGui::RequestCvarSaveOnNextTick(); ApplyOrResetCustomGfxPatches(); + ApplyAuthenticGfxPatches(); RegisterOnLoadGameHook(); } diff --git a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp new file mode 100644 index 000000000..1eec0c096 --- /dev/null +++ b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp @@ -0,0 +1,191 @@ +#include +#include + +extern "C" { +#include +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_fz/object_fz.h" +#include "objects/object_ik/object_ik.h" +#include "objects/object_link_child/object_link_child.h" + +void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction); +void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName); +} + +typedef struct { + const char* dlist; + int startInstruction; +} DListPatchInfo; + +static DListPatchInfo freezardEffectDListPatchInfos[] = { + { gFreezardIntactDL, 5 }, + { gFreezardTopRightHornChippedDL, 5 }, + { gFreezardHeadChippedDL, 5 }, + { gFreezardIceTriangleDL, 5 }, + { gFreezardIceRockDL, 5 }, +}; + +static DListPatchInfo ironKnuckleDListPatchInfos[] = { + { object_ik_DL_01BE98, 39 }, + { object_ik_DL_01BE98, 59 }, + + { object_ik_DL_01C130, 38 }, + + { object_ik_DL_01C2B8, 39 }, + { object_ik_DL_01C2B8, 59 }, + + { object_ik_DL_01C550, 38 }, + + { object_ik_DL_01C7B8, 8 }, + { object_ik_DL_01C7B8, 28 }, + + { object_ik_DL_01CB58, 8 }, + { object_ik_DL_01CB58, 31 }, + + { object_ik_DL_01CCA0, 15 }, + { object_ik_DL_01CCA0, 37 }, + { object_ik_DL_01CCA0, 52 }, + { object_ik_DL_01CCA0, 68 }, + + { object_ik_DL_01CEE0, 27 }, + { object_ik_DL_01CEE0, 46 }, + { object_ik_DL_01CEE0, 125 }, + + { object_ik_DL_01D2B0, 8 }, + { object_ik_DL_01D2B0, 32 }, + + { object_ik_DL_01D3F8, 15 }, + { object_ik_DL_01D3F8, 37 }, + { object_ik_DL_01D3F8, 52 }, + { object_ik_DL_01D3F8, 68 }, + + { object_ik_DL_01D638, 23 }, + { object_ik_DL_01D638, 42 }, + { object_ik_DL_01D638, 110 }, +}; + +void PatchDekuStickTextureOverflow() { + // Custom texture for holding Deku Stick that accounts for overflow texture reading + Gfx gDekuStickOverflowTexFix = gsDPSetTextureImage(G_IM_FMT_I, G_IM_SIZ_8b, 1, gDekuStickOverflowTex); + + // Gfx instructions to fix authentic vanilla bug where the Deku Stick texture is read as the wrong size + Gfx gDekuStickTexFix[] = { + gsDPLoadTextureBlock(gDekuStickTex, G_IM_FMT_I, G_IM_SIZ_8b, 8, 8, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, 4, 4, G_TX_NOLOD, G_TX_NOLOD) + }; + + const char* dlist = gLinkChildLinkDekuStickDL; + int start = 5; + + if (!CVarGetInteger("gFixTexturesOOB", 0)) { + // Unpatch the other texture fix + for (size_t i = 0; i < 7; i++) { + int instruction = start + (i == 0 ? 0 : i + 1); + std::string unpatchName = "DekuStickFix" + std::to_string(instruction); + ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str()); + } + + std::string patchName = "DekuStickOverflow" + std::to_string(start); + ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), start, gDekuStickOverflowTexFix); + } else { + // Unpatch the other texture fix + std::string unpatchName = "DekuStickOverflow" + std::to_string(start); + ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str()); + + for (size_t i = 0; i < 7; i++) { + int instruction = start + (i == 0 ? 0 : i + 1); + std::string patchName = "DekuStickFix" + std::to_string(instruction); + ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), instruction, gDekuStickTexFix[i]); + } + } +} + +void PatchFreezardTextureOverflow() { + // Custom texture for Freezard effect that accounts for overflow texture reading + Gfx gEffUnknown12OverflowTextFix = gsDPSetTextureImage(G_IM_FMT_IA, G_IM_SIZ_16b, 1, gEffUnknown12OverflowTex); + + // Gfx instructions to fix authentic vanilla bug where the Freezard effect texture is read as the wrong format + Gfx gEffUnknown12TexFix[] = { + gsDPLoadTextureBlock(gEffUnknown12Tex, G_IM_FMT_I, G_IM_SIZ_8b, 32, 32, 0, G_TX_NOMIRROR | + G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD) + }; + + for (const auto& patchInfo : freezardEffectDListPatchInfos) { + const char* dlist = patchInfo.dlist; + int start = patchInfo.startInstruction; + + char patchNameBuf[24]; + + // Patch using custom overflowed texture + if (!CVarGetInteger("gFixTexturesOOB", 0)) { + // Unpatch the other texture fix + for (size_t i = 0; i < 7; i++) { + int instruction = start + (i == 0 ? 0 : i + 1); + std::string unpatchName = "gEffUnknown12Fix" + std::to_string(instruction); + ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str()); + } + + std::string patchName = "gEffUnknown12Overflow" + std::to_string(start); + ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), start, gEffUnknown12OverflowTextFix); + } else { // Patch texture to use correct image size + // Unpatch the other texture fix + std::string unpatchName = "gEffUnknown12Overflow" + std::to_string(start); + ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str()); + + for (size_t i = 0; i < 7; i++) { + int instruction = start + (i == 0 ? 0 : i + 1); + std::string patchName = "gEffUnknown12Fix" + std::to_string(instruction); + ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), instruction, gEffUnknown12TexFix[i]); + } + } + } +} + +void PatchIronKnuckleTextureOverflow() { + // Custom texture for Iron Knuckle that accounts for overflow texture reading + Gfx gIronKnuckleMetalOverflowTexFix = gsDPSetTextureImage(G_IM_FMT_I, G_IM_SIZ_8b, 1, gIronKnuckleMetalOverflowTex); + + // Gfx instructions to fix authentic vanilla bug where the Iron Knuckle texture is read as the wrong format + Gfx gIronKnuckleMetalTexFix[] = { + gsDPLoadTextureBlock(object_ik_Tex_00F7A0, G_IM_FMT_I, G_IM_SIZ_4b, 32, 64, 0, G_TX_MIRROR | G_TX_WRAP, + G_TX_MIRROR | G_TX_WRAP, 5, 6, G_TX_NOLOD, G_TX_NOLOD) + }; + + for (const auto& patchInfo : ironKnuckleDListPatchInfos) { + const char* dlist = patchInfo.dlist; + int start = patchInfo.startInstruction; + + // OTRTODO: Patching to use the correct size format for Iron Knuckle causes a tile size failure + // Until this is solved, Iron Knuckle will be hardcoded to always display with the "authentic" texture fix + + // Patch using custom overflowed texture + // if (!CVarGetInteger("gFixTexturesOOB", 0)) { + // Unpatch the other texture fix + for (size_t i = 0; i < 7; i++) { + int instruction = start + (i == 0 ? 0 : i + 1); + std::string unpatchName = "MetalTexFix" + std::to_string(instruction); + ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str()); + } + + std::string patchName = "MetalTexOverflow" + std::to_string(start); + ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), start, gIronKnuckleMetalOverflowTexFix); + // } else { // Patch texture to use correct image size + // // Unpatch the other texture fix + // std::string unpatchName = "MetalTexOverflow" + std::to_string(start); + // ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str()); + + // // Patch texture to use correct image size + // for (size_t i = 0; i < 7; i++) { + // int instruction = start + (i == 0 ? 0 : i + 1); + // std::string patchName = "MetalTexFix" + std::to_string(instruction); + // ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), instruction, gIronKnuckleMetalTexFix[i]); + // } + // } + } +} + +void ApplyAuthenticGfxPatches() { + PatchDekuStickTextureOverflow(); + PatchFreezardTextureOverflow(); + PatchIronKnuckleTextureOverflow(); +} diff --git a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.h b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.h new file mode 100644 index 000000000..fda0f5096 --- /dev/null +++ b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.h @@ -0,0 +1,3 @@ +#pragma once + +void ApplyAuthenticGfxPatches(); diff --git a/soh/soh/GameMenuBar.cpp b/soh/soh/GameMenuBar.cpp index a6d091624..952186786 100644 --- a/soh/soh/GameMenuBar.cpp +++ b/soh/soh/GameMenuBar.cpp @@ -34,6 +34,7 @@ #endif #include "Enhancements/game-interactor/GameInteractor.h" +#include "Enhancements/cosmetics/authenticGfxPatches.h" bool isBetaQuestEnabled = false; @@ -909,6 +910,10 @@ namespace GameMenuBar { UIWidgets::Tooltip( "Adds 5 higher pitches for the Silver Rupee Jingle for the rooms with more than 5 Silver Rupees. " "Currently only relevant in Master Quest."); + if (UIWidgets::PaddedEnhancementCheckbox("Fix out of bounds textures", "gFixTexturesOOB", true, false)) { + ApplyAuthenticGfxPatches(); + } + UIWidgets::Tooltip("Fixes authentic out of bounds texture reads, instead loading textures with the correct size"); ImGui::EndMenu(); }