Fix authentic overflowed textures (#794)

* fix authentic overflow textures

* Add overflow assets and enable patching them
This commit is contained in:
Archez
2024-11-05 23:25:31 -05:00
committed by GitHub
parent c2ec2f2cc3
commit 2141922ee3
8 changed files with 261 additions and 2 deletions
+8
View File
@@ -8,6 +8,7 @@
#include <unordered_map>
#include <string>
#include "2s2h/Enhancements/Enhancements.h"
#include "2s2h/Enhancements/GfxPatcher/AuthenticGfxPatches.h"
#include "2s2h/DeveloperTools/DeveloperTools.h"
#include "HudEditor.h"
@@ -605,6 +606,13 @@ void DrawEnhancementsMenu() {
{ .tooltip = "Fixes a bug that results in the Ikana Great Fairy fountain looking "
"green instead of yellow, this was fixed in the EU version" });
if (UIWidgets::CVarCheckbox(
"Fix Texture overflow OOB", "gEnhancements.Fixes.FixTexturesOOB",
{ .tooltip = "Fixes textures that normally overflow to be patched with the correct size or format",
.defaultValue = true })) {
GfxPatcher_ApplyOverflowTexturePatches();
}
ImGui::EndMenu();
}
+8 -1
View File
@@ -1,5 +1,6 @@
#include "2s2h/Enhancements/Enhancements.h"
#include "2s2h/DeveloperTools/DeveloperTools.h"
#include "2s2h/Enhancements/GfxPatcher/AuthenticGfxPatches.h"
#include "UIWidgets.hpp"
#include "BenMenuBar.h"
#include "Notification.h"
@@ -1366,7 +1367,13 @@ void AddEnhancements() {
{ "Fix Ikana Great Fairy Fountain Color", "gFixes.FixIkanaGreatFairyFountainColor",
"Fixes a bug that results in the Ikana Great Fairy fountain looking green instead of yellow, this was "
"fixed in the EU version",
WIDGET_CVAR_CHECKBOX } } } });
WIDGET_CVAR_CHECKBOX },
{ .widgetName = "Fix Texture overflow OOB",
.widgetCVar = "gEnhancements.Fixes.FixTexturesOOB",
.widgetTooltip = "Fixes textures that normally overflow to be patched with the correct size or format",
.widgetType = WIDGET_CVAR_CHECKBOX,
.widgetOptions = { .defaultVariant = true },
.widgetCallback = [](widgetInfo& info) { GfxPatcher_ApplyOverflowTexturePatches(); } } } } });
enhancementsSidebar.push_back(
{ "Restorations",
3,
@@ -1,7 +1,11 @@
#include "AuthenticGfxPatches.h"
#include "libultraship/libultraship.h"
#include <cstring>
extern "C" {
#include <libultraship/libultra.h>
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_fz/object_fz.h"
#include "objects/object_ik/object_ik.h"
#include "overlays/ovl_En_Syateki_Okuta/ovl_En_Syateki_Okuta.h"
#include "overlays/ovl_Obj_Jgame_Light/ovl_Obj_Jgame_Light.h"
@@ -10,6 +14,231 @@ void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName);
Gfx* ResourceMgr_LoadGfxByName(const char* path);
}
#define dgameplay_keep_Tex_00CA30_Overflow "__OTR__objects/gameplay_keep/gameplay_keep_Tex_00CA30_Overflow"
static const ALIGN_ASSET(2) char gameplay_keep_Tex_00CA30_Overflow[] = dgameplay_keep_Tex_00CA30_Overflow;
#define dgEffIceFragmentTex_Overflow "__OTR__objects/gameplay_keep/gEffIceFragmentTex_Overflow"
static const ALIGN_ASSET(2) char gEffIceFragmentTex_Overflow[] = dgEffIceFragmentTex_Overflow;
#define dgIronKnuckleFireTex_Overflow "__OTR__objects/object_ik/gIronKnuckleFireTex_Overflow"
static const ALIGN_ASSET(2) char gIronKnuckleFireTex_Overflow[] = dgIronKnuckleFireTex_Overflow;
typedef struct {
const char* dlist;
int startInstruction;
} DListPatchInfo;
static DListPatchInfo freezardBodyDListPatchInfos[] = {
{ object_fz_DL_001130, 5 }, { object_fz_DL_0021A0, 5 }, { object_fz_DL_002CA0, 5 },
{ object_fz_DL_003260, 5 }, { object_fz_DL_0033F0, 5 },
};
static DListPatchInfo ironKnuckleDListPatchInfos[] = {
{ gIronKnuckleVambraceLeftDL, 39 },
{ gIronKnuckleVambraceLeftDL, 59 },
{ gIronKnuckleArmLeftDL, 38 },
{ gIronKnuckleVambraceRightDL, 39 },
{ gIronKnuckleVambraceRightDL, 59 },
{ gIronKnuckleArmRightDL, 38 },
{ gIronKnuckleWaistDL, 8 },
{ gIronKnuckleWaistDL, 28 },
{ gIronKnucklePauldronLeftDL, 8 },
{ gIronKnucklePauldronLeftDL, 31 },
{ gIronKnuckleBootTipLeftDL, 15 },
{ gIronKnuckleBootTipLeftDL, 37 },
{ gIronKnuckleBootTipLeftDL, 52 },
{ gIronKnuckleBootTipLeftDL, 68 },
{ gIronKnuckleWaistArmorLeftDL, 27 },
{ gIronKnuckleWaistArmorLeftDL, 46 },
{ gIronKnuckleWaistArmorLeftDL, 121 },
{ gIronKnucklePauldronRightDL, 8 },
{ gIronKnucklePauldronRightDL, 32 },
{ gIronKnuckleBootTipRightDL, 15 },
{ gIronKnuckleBootTipRightDL, 37 },
{ gIronKnuckleBootTipRightDL, 52 },
{ gIronKnuckleBootTipRightDL, 68 },
{ gIronKnuckleWaistArmorRightDL, 23 },
{ gIronKnuckleWaistArmorRightDL, 42 },
{ gIronKnuckleWaistArmorRightDL, 106 },
};
static DListPatchInfo arrowTipDListPatchInfos[] = {
{ gameplay_keep_DL_013FF0, 46 },
{ gameplay_keep_DL_014370, 5 },
};
void PatchArrowTipTexture() {
// Custom texture for Arrow tips that accounts for overflow texture reading
Gfx arrowTipTextureWithOverflowFixGfx =
gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b_LOAD_BLOCK, 1, gameplay_keep_Tex_00CA30_Overflow);
// Gfx instructions to fix authentic vanilla bug where the Arrow tips texture is read as the wrong size
Gfx arrowTipTextureWithSizeFixGfx[] = {
gsDPLoadTextureBlock(gameplay_keep_Tex_00CA30, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 16, 0, G_TX_MIRROR | G_TX_WRAP,
G_TX_MIRROR | G_TX_WRAP, 5, 5, 1, 1),
};
bool fixTexturesOOB = CVarGetInteger("gEnhancements.Fixes.FixTexturesOOB", 0);
for (const auto& patchInfo : arrowTipDListPatchInfos) {
const char* dlist = patchInfo.dlist;
int start = patchInfo.startInstruction;
// Patch using custom overflowed texture
if (!fixTexturesOOB) {
// Unpatch the other texture fix
for (size_t i = 4; i < 8; i++) {
int instruction = start + i;
std::string unpatchName = "arrowTipTextureWithSizeFix_" + std::to_string(instruction);
ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str());
}
std::string patchName = "arrowTipTextureWithOverflowFix_" + std::to_string(start);
std::string patchName2 = "arrowTipTextureWithOverflowFix_" + std::to_string(start + 1);
ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), start, arrowTipTextureWithOverflowFixGfx);
ResourceMgr_PatchGfxByName(dlist, patchName2.c_str(), start + 1, gsSPNoOp());
} else { // Patch texture to use correct image size/fmt
// Unpatch the other texture fix
std::string unpatchName = "arrowTipTextureWithOverflowFix_" + std::to_string(start);
std::string unpatchName2 = "arrowTipTextureWithOverflowFix_" + std::to_string(start + 1);
ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str());
ResourceMgr_UnpatchGfxByName(dlist, unpatchName2.c_str());
for (size_t i = 4; i < 8; i++) {
int instruction = start + i;
std::string patchName = "arrowTipTextureWithSizeFix_" + std::to_string(instruction);
if (i == 0) {
ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), instruction, gsSPNoOp());
} else {
ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), instruction,
arrowTipTextureWithSizeFixGfx[i - 1]);
}
}
}
}
}
void PatchFreezardBodyTexture() {
// Custom texture for Freezard effect that accounts for overflow texture reading
Gfx freezardBodyTextureWithOverflowFixGfx =
gsDPSetTextureImage(G_IM_FMT_IA, G_IM_SIZ_16b_LOAD_BLOCK, 1, gEffIceFragmentTex_Overflow);
// Gfx instructions to fix authentic vanilla bug where the Freezard effect texture is read as the wrong format
Gfx freezardBodyTextureWithFormatFixGfx[] = {
gsDPLoadTextureBlock(gEffIceFragmentTex, 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),
};
bool fixTexturesOOB = CVarGetInteger("gEnhancements.Fixes.FixTexturesOOB", 0);
for (const auto& patchInfo : freezardBodyDListPatchInfos) {
const char* dlist = patchInfo.dlist;
int start = patchInfo.startInstruction;
// Patch using custom overflowed texture
if (!fixTexturesOOB) {
// Unpatch the other texture fix
for (size_t i = 0; i < 8; i++) {
int instruction = start + i;
std::string unpatchName = "freezardBodyTextureWithFormatFix_" + std::to_string(instruction);
ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str());
}
std::string patchName = "freezardBodyTextureWithOverflowFix_" + std::to_string(start);
std::string patchName2 = "freezardBodyTextureWithOverflowFix_" + std::to_string(start + 1);
ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), start, freezardBodyTextureWithOverflowFixGfx);
ResourceMgr_PatchGfxByName(dlist, patchName2.c_str(), start + 1, gsSPNoOp());
} else { // Patch texture to use correct image size/fmt
// Unpatch the other texture fix
std::string unpatchName = "freezardBodyTextureWithOverflowFix_" + std::to_string(start);
std::string unpatchName2 = "freezardBodyTextureWithOverflowFix_" + std::to_string(start + 1);
ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str());
ResourceMgr_UnpatchGfxByName(dlist, unpatchName2.c_str());
for (size_t i = 0; i < 8; i++) {
int instruction = start + i;
std::string patchName = "freezardBodyTextureWithFormatFix_" + std::to_string(instruction);
if (i == 0) {
ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), instruction, gsSPNoOp());
} else {
ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), instruction,
freezardBodyTextureWithFormatFixGfx[i - 1]);
}
}
}
}
}
void PatchIronKnuckleFireTexture() {
// Custom texture for Iron Knuckle texture that accounts for overflow texture reading
Gfx ironKnuckleFireTexWithOverflowFixGfx =
gsDPSetTextureImage(G_IM_FMT_I, G_IM_SIZ_8b_LOAD_BLOCK, 1, gIronKnuckleFireTex_Overflow);
// Gfx instructions to fix authentic vanilla bug where the Iron Knuckle texture is read as the wrong size
Gfx ironKnuckleFireTexWithFormatFixGfx[] = {
gsDPLoadTextureBlock_4b(gIronKnuckleFireTex, G_IM_FMT_I, 32, 64, 0, G_TX_MIRROR | G_TX_WRAP,
G_TX_MIRROR | G_TX_WRAP, 5, 6, G_TX_NOLOD, G_TX_NOLOD),
};
bool fixTexturesOOB = CVarGetInteger("gEnhancements.Fixes.FixTexturesOOB", 0);
for (const auto& patchInfo : ironKnuckleDListPatchInfos) {
const char* dlist = patchInfo.dlist;
int start = patchInfo.startInstruction;
// Patch using custom overflowed texture
if (!fixTexturesOOB) {
// Unpatch the other texture fix
for (size_t i = 0; i < 8; i++) {
int instruction = start + i;
std::string unpatchName = "ironKnuckleFireTexWithSizeFix_" + std::to_string(instruction);
ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str());
}
std::string patchName = "ironKnuckleFireTexWithOverflowFix_" + std::to_string(start);
std::string patchName2 = "ironKnuckleFireTexWithOverflowFix_" + std::to_string(start + 1);
ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), start, ironKnuckleFireTexWithOverflowFixGfx);
ResourceMgr_PatchGfxByName(dlist, patchName2.c_str(), start + 1, gsSPNoOp());
} else { // Patch texture to use correct image size/fmt
// Unpatch the other texture fix
std::string unpatchName = "ironKnuckleFireTexWithOverflowFix_" + std::to_string(start);
std::string unpatchName2 = "ironKnuckleFireTexWithOverflowFix_" + std::to_string(start + 1);
ResourceMgr_UnpatchGfxByName(dlist, unpatchName.c_str());
ResourceMgr_UnpatchGfxByName(dlist, unpatchName2.c_str());
for (size_t i = 0; i < 8; i++) {
int instruction = start + i;
std::string patchName = "ironKnuckleFireTexWithSizeFix_" + std::to_string(instruction);
if (i == 0) {
ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), instruction, gsSPNoOp());
} else {
ResourceMgr_PatchGfxByName(dlist, patchName.c_str(), instruction,
ironKnuckleFireTexWithFormatFixGfx[i - 1]);
}
}
}
}
}
void GfxPatcher_ApplyOverflowTexturePatches() {
PatchArrowTipTexture();
PatchFreezardBodyTexture();
PatchIronKnuckleFireTexture();
}
void PatchMiniGameCrossAndCircleSymbols() {
// The X and O displayed in mini-games are incorrectly set to FMT_I instead of FMT_IA,
// Fast3D throws an assert and does nothing as FMT_I with SIZ_16 is not a valid texture type.
@@ -39,4 +268,6 @@ void PatchMiniGameCrossAndCircleSymbols() {
// Applies required patches for authentic bugs to allow the game to play and render properly
void GfxPatcher_ApplyNecessaryAuthenticPatches() {
PatchMiniGameCrossAndCircleSymbols();
GfxPatcher_ApplyOverflowTexturePatches();
}
@@ -2,5 +2,6 @@
#define AUTHENTIC_GFX_PATCHES_H
void GfxPatcher_ApplyNecessaryAuthenticPatches();
void GfxPatcher_ApplyOverflowTexturePatches();
#endif // AUTHENTIC_GFX_PATCHES_H
@@ -85,6 +85,8 @@
<Texture Name="gameplay_keep_Tex_00C830" OutName="tex_00C830" Format="rgba16" Width="16" Height="16" Offset="0xC830" />
<Texture Name="gameplay_keep_Tex_00CA30" OutName="tex_00CA30" Format="rgba16" Width="16" Height="16" Offset="0xCA30" />
<!-- 2S2H [Port] Additional entry for authentic overflowed texture usage. This entry has its offset off by "one pixel" due to ZAPD not allowing duplicates -->
<Texture Name="gameplay_keep_Tex_00CA30_Overflow" OutName="tex_00CA30_overflow" Format="rgba16" Width="32" Height="32" Offset="0xCA2E" />
<!-- 2S2H [Port] - gDekuStickDL loads this as a 16x16 texture, despite it being 8x8. We need to extract this extra 'garbage' data too -->
<Texture Name="gDekuStickTex" OutName="deku_stick" Format="i8" Width="16" Height="16" Offset="0xCC30" />
@@ -1158,6 +1160,8 @@
<Texture Name="gameplay_keep_Tex_0447B0" OutName="tex_0447B0" Format="i8" Width="32" Height="32" Offset="0x44800" />
<Texture Name="gameplay_keep_Tex_044BB0" OutName="tex_044BB0" Format="i8" Width="32" Height="32" Offset="0x44C00" />
<Texture Name="gEffIceFragmentTex" OutName="eff_ice_fragment" Format="i8" Width="32" Height="32" Offset="0x45000" />
<!-- 2S2H [Port] Additional entry for authentic overflowed texture usage. This entry has its offset off by "one pixel" due to ZAPD not allowing duplicates -->
<Texture Name="gEffIceFragmentTex_Overflow" OutName="eff_ice_fragment_overflow" Format="ia16" Width="32" Height="32" Offset="0x44FFF" />
<Texture Name="gameplay_keep_Tex_0453B0" OutName="tex_0453B0" Format="i8" Width="32" Height="32" Offset="0x45400" />
<!-- Goron Elegy of Emptiness Shell -->
@@ -58,6 +58,8 @@
<!-- Odd size-->
<Texture Name="gIronKnuckleAxeTLUT" OutName="iron_knuckle_axe_tlut" Format="rgba16" Width="183" Height="1" Offset="0x11030" />
<Texture Name="gIronKnuckleFireTex" OutName="iron_knuckle_fire" Format="i4" Width="32" Height="64" Offset="0x111A0" />
<!-- 2S2H [Port] Additional entry for authentic overflowed texture usage. This entry has its offset off by "one pixel" due to ZAPD not allowing duplicates -->
<Texture Name="gIronKnuckleFireTex_Overflow" OutName="iron_knuckle_fire_overflow" Format="i8" Width="32" Height="64" Offset="0x1119F" />
<Texture Name="gIronKnuckleRivet2Tex" OutName="iron_knuckle_rivet_2" Format="ia8" Width="32" Height="32" Offset="0x115A0" />
<Texture Name="gIronKnuckleBlockPatternTex" OutName="iron_knuckle_block_pattern" Format="rgba16" Width="16" Height="16" Offset="0x119A0" />
<Texture Name="gIronKnuckleChainMailTex" OutName="iron_knuckle_chain_mail" Format="rgba16" Width="16" Height="16" Offset="0x11BA0" />
@@ -85,6 +85,8 @@
<Texture Name="gameplay_keep_Tex_00C830" OutName="tex_00C830" Format="rgba16" Width="16" Height="16" Offset="0xC830" />
<Texture Name="gameplay_keep_Tex_00CA30" OutName="tex_00CA30" Format="rgba16" Width="16" Height="16" Offset="0xCA30" />
<!-- 2S2H [Port] Additional entry for authentic overflowed texture usage. This entry has its offset off by "one pixel" due to ZAPD not allowing duplicates -->
<Texture Name="gameplay_keep_Tex_00CA30_Overflow" OutName="tex_00CA30_overflow" Format="rgba16" Width="32" Height="32" Offset="0xCA2E" />
<!-- 2S2H [Port] - gDekuStickDL loads this as a 16x16 texture, despite it being 8x8. We need to extract this extra 'garbage' data too -->
<Texture Name="gDekuStickTex" OutName="deku_stick" Format="i8" Width="16" Height="16" Offset="0xCC30" />
@@ -1140,6 +1142,8 @@
<Texture Name="gameplay_keep_Tex_0447B0" OutName="tex_0447B0" Format="i8" Width="32" Height="32" Offset="0x447B0" />
<Texture Name="gameplay_keep_Tex_044BB0" OutName="tex_044BB0" Format="i8" Width="32" Height="32" Offset="0x44BB0" />
<Texture Name="gEffIceFragmentTex" OutName="eff_ice_fragment" Format="i8" Width="32" Height="32" Offset="0x44FB0" />
<!-- 2S2H [Port] Additional entry for authentic overflowed texture usage. This entry has its offset off by "one pixel" due to ZAPD not allowing duplicates -->
<Texture Name="gEffIceFragmentTex_Overflow" OutName="eff_ice_fragment_overflow" Format="ia16" Width="32" Height="32" Offset="0x44FAF" />
<Texture Name="gameplay_keep_Tex_0453B0" OutName="tex_0453B0" Format="i8" Width="32" Height="32" Offset="0x453B0" />
<!-- Goron Elegy of Emptiness Shell -->
@@ -58,6 +58,8 @@
<!-- Odd size-->
<Texture Name="gIronKnuckleAxeTLUT" OutName="iron_knuckle_axe_tlut" Format="rgba16" Width="183" Height="1" Offset="0x11030" />
<Texture Name="gIronKnuckleFireTex" OutName="iron_knuckle_fire" Format="i4" Width="32" Height="64" Offset="0x111A0" />
<!-- 2S2H [Port] Additional entry for authentic overflowed texture usage. This entry has its offset off by "one pixel" due to ZAPD not allowing duplicates -->
<Texture Name="gIronKnuckleFireTex_Overflow" OutName="iron_knuckle_fire_overflow" Format="i8" Width="32" Height="64" Offset="0x1119F" />
<Texture Name="gIronKnuckleRivet2Tex" OutName="iron_knuckle_rivet_2" Format="ia8" Width="32" Height="32" Offset="0x115A0" />
<Texture Name="gIronKnuckleBlockPatternTex" OutName="iron_knuckle_block_pattern" Format="rgba16" Width="16" Height="16" Offset="0x119A0" />
<Texture Name="gIronKnuckleChainMailTex" OutName="iron_knuckle_chain_mail" Format="rgba16" Width="16" Height="16" Offset="0x11BA0" />