From d6473ff28654a6658f1e2a3ecabc26e89df3cac4 Mon Sep 17 00:00:00 2001 From: ThirteenAG Date: Thu, 9 Nov 2023 19:15:27 +0800 Subject: [PATCH] the saboteur fov fix --- .github/docs/thesaboteur.md | 10 ++ .github/workflows/all.yml | 8 ++ .github/workflows/tag.yml | 2 + data/TheSaboteur.FusionFix/dinput8.ual | 1 + .../scripts/TheSaboteur.FusionFix.ini | 3 + includes/LED/LEDEffects.h | 2 - premake5.lua | 2 + source/TheSaboteur.FusionFix/dllmain.cpp | 123 ++++++++++++++++++ 8 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 .github/docs/thesaboteur.md create mode 100644 data/TheSaboteur.FusionFix/dinput8.ual create mode 100644 data/TheSaboteur.FusionFix/scripts/TheSaboteur.FusionFix.ini create mode 100644 source/TheSaboteur.FusionFix/dllmain.cpp diff --git a/.github/docs/thesaboteur.md b/.github/docs/thesaboteur.md new file mode 100644 index 000000000..36617d8e4 --- /dev/null +++ b/.github/docs/thesaboteur.md @@ -0,0 +1,10 @@ +![thesaboteur](https://thirteenag.github.io/screens/thesaboteur/main2.jpg) + +![](https://habrastorage.org/webt/ow/yy/mg/owyymgpibfqzfbwyf_iqoiqrede.png) Fixed Field of View + +![](https://habrastorage.org/webt/d_/eg/ym/d_egymd6w_tem2erocab-e9ikna.png) Added an option to make windowed mode borderless + + Installation: + Download and extract the archive to the game directory, where the exe is located. + +[Website](https://thirteenag.github.io/wfp#thesaboteur) | [Source](https://github.com/ThirteenAG/WidescreenFixesPack/blob/master/source/TheSaboteur.FusionFix/dllmain.cpp) | [Default INI File](https://github.com/ThirteenAG/WidescreenFixesPack/blob/master/data/TheSaboteur.FusionFix/plugins/TheSaboteur.FusionFix.ini) diff --git a/.github/workflows/all.yml b/.github/workflows/all.yml index 713d0e630..5bbedcb64 100644 --- a/.github/workflows/all.yml +++ b/.github/workflows/all.yml @@ -271,6 +271,14 @@ jobs: tag: driv3r artifacts: data/Archives/Driv3r.WidescreenFix.zip + - name: The Saboteur Fusion Fix + uses: ./.github/workflows/release_tag + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag_list: ${{ format('{0},{1}', github.event.inputs.tag_list, inputs.tag_list) }} + tag: thesaboteur + artifacts: data/Archives/TheSaboteur.FusionFix.zip + - name: The Suffering Widescreen Fix uses: ./.github/workflows/release_tag with: diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index f62212902..ac16d39b8 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -87,6 +87,7 @@ on: - thaw - thegodfather - thematrixpathofneo + - thesaboteur - thesuffering - thewarriors - thps2 @@ -180,6 +181,7 @@ env: thaw: "/t:TonyHawksAmericanWasteland_WidescreenFix" thegodfather: "/t:TheGodfather_WidescreenFix" thematrixpathofneo: "/t:TheMatrixPathOfNeo_WidescreenFix" + thesaboteur: "/t:TheSaboteur_FusionFix" thesuffering: "/t:TheSuffering_WidescreenFix" thewarriors: "/t:TheWarriors_PPSSPP_FusionMod" thps2: "/t:TonyHawksProSkater2_WidescreenFix" diff --git a/data/TheSaboteur.FusionFix/dinput8.ual b/data/TheSaboteur.FusionFix/dinput8.ual new file mode 100644 index 000000000..a36540af3 --- /dev/null +++ b/data/TheSaboteur.FusionFix/dinput8.ual @@ -0,0 +1 @@ +loadfromscriptsonly \ No newline at end of file diff --git a/data/TheSaboteur.FusionFix/scripts/TheSaboteur.FusionFix.ini b/data/TheSaboteur.FusionFix/scripts/TheSaboteur.FusionFix.ini new file mode 100644 index 000000000..953db5d8b --- /dev/null +++ b/data/TheSaboteur.FusionFix/scripts/TheSaboteur.FusionFix.ini @@ -0,0 +1,3 @@ +[MAIN] +FixFOV = 1 +BorderlessWindowed = 1 diff --git a/includes/LED/LEDEffects.h b/includes/LED/LEDEffects.h index 7e381dd84..5a1d9076f 100644 --- a/includes/LED/LEDEffects.h +++ b/includes/LED/LEDEffects.h @@ -8,8 +8,6 @@ #include #include #include -#include -#pragma comment(lib, "Comctl32.lib") class LEDEffects { diff --git a/premake5.lua b/premake5.lua index 6660366e4..8372f704d 100644 --- a/premake5.lua +++ b/premake5.lua @@ -590,6 +590,8 @@ project "TheMatrixPathOfNeo.WidescreenFix" setpaths("Z:/WFP/Games/The Matrix - Path Of Neo/", "Matrix3.exe") project "ThePunisher.WidescreenFix" setpaths("Z:/WFP/Games/The Punisher/", "pun.exe") +project "TheSaboteur.FusionFix" + setpaths("Z:/WFP/Games/The Saboteur/", "Saboteur.exe") project "TheSuffering.WidescreenFix" setpaths("Z:/WFP/Games/The Suffering/The Suffering/", "suffering.exe") project "TheWarriors.PPSSPP.FusionMod" diff --git a/source/TheSaboteur.FusionFix/dllmain.cpp b/source/TheSaboteur.FusionFix/dllmain.cpp new file mode 100644 index 000000000..b7369f1e6 --- /dev/null +++ b/source/TheSaboteur.FusionFix/dllmain.cpp @@ -0,0 +1,123 @@ +#include "stdafx.h" + +struct PblVector4 +{ + float x; + float y; + float z; + float w; +}; + +struct PblMatrix4x4 +{ + PblVector4 _m[4]; +}; + +struct OdinCamera +{ + bool m_bOrthographic; + PblMatrix4x4 m_ViewMatrix; + PblMatrix4x4 m_ViewMatrixInverse; + PblMatrix4x4 m_ProjMatrix; + PblMatrix4x4 m_ProjMatrixInverse; + unsigned int m_Dirty; + float m_NearPlane; + float m_FarPlane; + float m_TanHalfFOVWidth; + float m_TanHalfFOVHeight; + + static void __fastcall SetFOV(OdinCamera* OdinCamera, void* edx, float a_FovX, float a_AspectRatio) + { + // too high fov breaks rendering (sky and objects start to disappear) + auto fAspectRatioDiff = std::clamp((1.0f / a_AspectRatio) / (4.0f / 3.0f), 0.0f, 2.1f); // 2.1 seems safe before that happens + auto a_FovXa = a_FovX * 0.5f; + auto a_FovXb = tan(a_FovXa); + OdinCamera->m_Dirty |= 3u; + OdinCamera->m_bOrthographic = 0; + OdinCamera->m_TanHalfFOVWidth = a_FovXb * fAspectRatioDiff; + OdinCamera->m_TanHalfFOVHeight = a_FovXb * a_AspectRatio * fAspectRatioDiff; + } + + static void __fastcall SetPerspective(OdinCamera* OdinCamera, void* edx, float a_NearPlane, float a_FarPlane, float a_FovX, float a_AspectRatio) + { + auto a_FovXa = a_FovX * 0.5f; + auto a_FovXb = tan(a_FovXa); + OdinCamera->m_Dirty |= 3u; + OdinCamera->m_bOrthographic = 0; + OdinCamera->m_NearPlane = a_NearPlane; + OdinCamera->m_FarPlane = a_FarPlane; + OdinCamera->m_TanHalfFOVWidth = a_FovXb; + OdinCamera->m_TanHalfFOVHeight = a_FovXb * a_AspectRatio; + } + + static float __fastcall GetAspectRatio(OdinCamera* OdinCamera, void* edx) + { + return OdinCamera->m_TanHalfFOVHeight / OdinCamera->m_TanHalfFOVWidth; + } +}; + +void Init() +{ + WFP::onInitEventAsync() += []() + { + CIniReader iniReader(""); + auto bFixFOV = iniReader.ReadInteger("MAIN", "FixFOV", 1) != 0; + auto bBorderlessWindowed = iniReader.ReadInteger("MAIN", "BorderlessWindowed", 1) != 0; + + if (bFixFOV) + { + auto pattern = hook::pattern("D9 44 24 04 56 DC 0D ? ? ? ? 8B F1 D9 5C 24 08"); + injector::MakeJMP(pattern.get_first(0), OdinCamera::SetFOV, true); + + //auto pattern = hook::pattern("D9 44 24 0C 56 DC 0D ? ? ? ? 8B F1 D9 5C 24 10 D9 44 24 10 E8 ? ? ? ? D9 5C 24 10 D9 44 24 10 83 8E"); + //injector::MakeJMP(pattern.get_first(0), OdinCamera::SetPerspective, true); + + //pattern = hook::pattern("51 D9 81 ? ? ? ? D8 B1"); + //injector::MakeJMP(pattern.get_first(0), OdinCamera::GetAspectRatio, true); + } + + if (bBorderlessWindowed) + { + IATHook::Replace(GetModuleHandleA(NULL), "USER32.DLL", + std::forward_as_tuple("CreateWindowExA", WindowedModeWrapper::CreateWindowExA_Hook), + std::forward_as_tuple("CreateWindowExW", WindowedModeWrapper::CreateWindowExW_Hook), + std::forward_as_tuple("SetWindowLongA", WindowedModeWrapper::SetWindowLongA_Hook), + std::forward_as_tuple("SetWindowLongW", WindowedModeWrapper::SetWindowLongW_Hook), + std::forward_as_tuple("AdjustWindowRect", WindowedModeWrapper::AdjustWindowRect_Hook), + std::forward_as_tuple("SetWindowPos", WindowedModeWrapper::SetWindowPos_Hook) + ); + } + }; + + static auto futures = WFP::onInitEventAsync().executeAllAsync(); + + WFP::onGameInitEvent() += []() //todo: add onGameInitEvent hook + { + for (auto& f : futures.get()) + f.wait(); + futures.get().clear(); + }; + + WFP::onInitEvent().executeAll(); +} + +CEXP void InitializeASI() +{ + std::call_once(CallbackHandler::flag, []() + { + CallbackHandler::RegisterCallback(Init, hook::pattern("56 68 ? ? ? ? 68 ? ? ? ? 8B F1 6A 19")); + }); +} + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) +{ + if (reason == DLL_PROCESS_ATTACH) + { + if (!IsUALPresent()) { InitializeASI(); } + } + else if (reason == DLL_PROCESS_DETACH) + { + + } + return TRUE; +}