diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..568ff5a
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,20 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "gitsubmodule"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ groups:
+ submodules:
+ patterns:
+ - "*"
\ No newline at end of file
diff --git a/.github/funding.yml b/.github/funding.yml
new file mode 100644
index 0000000..4df779f
--- /dev/null
+++ b/.github/funding.yml
@@ -0,0 +1,6 @@
+# These are supported funding model platforms
+
+github: [ThirteenAG]
+ko_fi: thirteenag
+patreon: ThirteenAG
+custom: [https://paypal.me/SergeyP13, https://boosty.to/thirteenag/donate]
\ No newline at end of file
diff --git a/.github/workflows/msvc_x86.yml b/.github/workflows/msvc_x86.yml
index 1abc172..7e6c5a3 100644
--- a/.github/workflows/msvc_x86.yml
+++ b/.github/workflows/msvc_x86.yml
@@ -1,57 +1,97 @@
-name: Build
+name: GitHub Actions Build
on:
- pull_request:
push:
- release:
- types: published
+ paths-ignore:
+ - "**/*.md"
+ - '**/*.txt'
+ branches:
+ - '**'
+ pull_request:
+ paths-ignore:
+ - "**/*.md"
+ - '**/*.txt'
+ workflow_dispatch:
+ inputs:
+ release:
+ description: "Create a release"
+ type: choice
+ required: false
+ default: 'false'
+ options:
+ - 'true'
+ - 'false'
+
+concurrency:
+ group: ${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: write
jobs:
build:
- runs-on: windows-2022
- strategy:
- matrix:
- platform: [Win32]
- buildtype: [Release]
+ runs-on: windows-latest
steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+
- name: Add msbuild to PATH
- uses: microsoft/setup-msbuild@v1.0.2
- - uses: actions/checkout@v2
+ uses: microsoft/setup-msbuild@main
+
+ - name: Auto Increment Version
+ uses: MCKanpolat/auto-semver-action@v1
+ id: versioning
with:
- submodules: 'true'
+ releaseType: minor
+ incrementPerCommit: false
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+
- name: Configure build
- run: |
- ./premake5 vs2022
+ run: ./premake5 vs2022 --with-version=${{ steps.versioning.outputs.version }}
+
- name: Build
run: |
- msbuild -m build/MaxPayne3.FusionFix.sln /property:Configuration=${{matrix.buildtype}} /property:Platform=${{matrix.platform}}
- - name: Move binaries to data
+ msbuild -m build/MaxPayne3.FusionFix.sln /property:Configuration=Release /property:Platform=Win32
+
+ - name: Download Ultimate ASI Loader x86
+ uses: robinraju/release-downloader@v1.8
+ with:
+ repository: "ThirteenAG/Ultimate-ASI-Loader"
+ tag: "Win32-latest"
+ fileName: "dinput8-Win32.zip"
+
+ - name: Unpack dependencies
+ run: |
+ 7z x dinput8-Win32.zip -odata/ -y
+ del dinput8-Win32.zip
+ del data\dinput8-Win32.SHA512
+
+ - name: Pack binaries
run: |
- cp "./bin/MaxPayne3.FusionFix.asi" "./data/plugins/MaxPayne3.FusionFix.asi"
- 7z a MaxPayne3.FusionFix.zip ./data/*
- - name: Upload artifact to actions
- uses: actions/upload-artifact@v2
+ ./release.bat
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v4
with:
name: MaxPayne3.FusionFix.zip
- path: ./data/*
- - name: Create Release
- id: create_release
- uses: actions/create-release@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- tag_name: v1.${{ github.run_number }}
- release_name: MaxPayne3.FusionFix v1.${{ github.run_number }}
- draft: false
- prerelease: false
- - name: Upload Release Asset
- id: upload-release-asset
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ path: data/*
+
+ - name: Upload Release
+ if: |
+ github.event.inputs.release == 'true' &&
+ github.ref_name == 'main' &&
+ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') &&
+ github.repository == 'ThirteenAG/MaxPayne3.FusionFix'
+ uses: ncipollo/release-action@main
with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: ./MaxPayne3.FusionFix.zip
- asset_name: MaxPayne3.FusionFix.zip
- asset_content_type: application/zip
+ token: ${{ secrets.GITHUB_TOKEN }}
+ allowUpdates: false
+ name: MaxPayne3.FusionFix v${{ steps.versioning.outputs.version }}
+ bodyFile: "release.md"
+ tag: v${{ steps.versioning.outputs.version }}
+ artifacts: MaxPayne3.FusionFix.zip
+
diff --git a/.gitignore b/.gitignore
index 6ef9495..64985ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -329,3 +329,9 @@ ASALocalRun/
# MFractors (Xamarin productivity tool) working folder
.mfractor/
+
+data/**/*.img
+data/update/common/shaders/**/*.fxc
+
+data/plugins/MaxPayne3.FusionFix.asi
+MaxPayne3.FusionFix.zip
diff --git a/data/plugins/MaxPayne3.FusionFix.ini b/data/plugins/MaxPayne3.FusionFix.ini
index 0f0d53f..c3f5635 100644
--- a/data/plugins/MaxPayne3.FusionFix.ini
+++ b/data/plugins/MaxPayne3.FusionFix.ini
@@ -2,3 +2,12 @@
HideSkipButton = 1
DisableGlobalLeaderboards = 1 // Fixes Hoboken Alleys map crash in coop
OutlinesSizeMultiplier = 1.0f
+BorderlessWindowed = 1
+GamepadIcons = 0
+; 0 - Xbox 360
+; 1 - Xbox One
+; 2 - Playstation 3
+; 3 - Playstation 4
+; 4 - Playstation 5
+; 5 - Nintendo Switch
+; 6 - Steam Deck
diff --git a/data/update/platform/textures/buttons_pc.wtd b/data/update/platform/textures/buttons_pc.wtd
new file mode 100644
index 0000000..4b18ea1
Binary files /dev/null and b/data/update/platform/textures/buttons_pc.wtd differ
diff --git a/data/update/platform/textures/controller_360.wtd b/data/update/platform/textures/controller_360.wtd
new file mode 100644
index 0000000..39e701f
Binary files /dev/null and b/data/update/platform/textures/controller_360.wtd differ
diff --git a/premake5.exe b/premake5.exe
index c73da1f..1a637aa 100644
Binary files a/premake5.exe and b/premake5.exe differ
diff --git a/premake5.lua b/premake5.lua
index 787278d..8a62ab3 100644
--- a/premake5.lua
+++ b/premake5.lua
@@ -1,23 +1,60 @@
+newoption {
+ trigger = "with-version",
+ value = "STRING",
+ description = "Current version",
+ default = "1.0",
+}
+
workspace "MaxPayne3.FusionFix"
configurations { "Release", "Debug" }
architecture "x86"
location "build"
- buildoptions {"-std:c++latest"}
+ cppdialect "C++latest"
kind "SharedLib"
language "C++"
targetdir "bin/%{cfg.buildcfg}"
targetextension ".asi"
+ buildoptions { "/dxifcInlineFunctions-" }
defines { "rsc_CompanyName=\"MaxPayne3.FusionFix\"" }
defines { "rsc_LegalCopyright=\"MaxPayne3.FusionFix\""}
- defines { "rsc_FileVersion=\"1.0.0.0\"", "rsc_ProductVersion=\"1.0.0.0\"" }
defines { "rsc_InternalName=\"%{prj.name}\"", "rsc_ProductName=\"%{prj.name}\"", "rsc_OriginalFilename=\"%{prj.name}.dll\"" }
defines { "rsc_FileDescription=\"MaxPayne3.FusionFix\"" }
defines { "rsc_UpdateUrl=\"https://github.com/ThirteenAG/MaxPayne3.FusionFix\"" }
+ local major = 1
+ local minor = 0
+ local build = 0
+ local revision = 0
+ if(_OPTIONS["with-version"]) then
+ local t = {}
+ for i in _OPTIONS["with-version"]:gmatch("([^.]+)") do
+ t[#t + 1], _ = i:gsub("%D+", "")
+ end
+ while #t < 4 do t[#t + 1] = 0 end
+ major = math.min(tonumber(t[1]), 255)
+ minor = math.min(tonumber(t[2]), 255)
+ build = math.min(tonumber(t[3]), 65535)
+ revision = math.min(tonumber(t[4]), 65535)
+ end
+ defines { "rsc_FileVersion_MAJOR=" .. major }
+ defines { "rsc_FileVersion_MINOR=" .. minor }
+ defines { "rsc_FileVersion_BUILD=" .. build }
+ defines { "rsc_FileVersion_REVISION=" .. revision }
+ defines { "rsc_FileVersion=\"" .. major .. "." .. minor .. "." .. build .. "\"" }
+ defines { "rsc_ProductVersion=\"" .. major .. "." .. minor .. "." .. build .. "\"" }
+
+ defines { "_CRT_SECURE_NO_WARNINGS" }
+
includedirs { "source" }
- files { "source/dllmain.cpp" }
+ includedirs { "source/includes" }
+ includedirs { "source/ledsdk" }
+ includedirs { "source/dxsdk" }
+ libdirs { "source/ledsdk" }
+ libdirs { "source/dxsdk" }
+ files { "source/*.h", "source/*.hpp", "source/*.cpp", "source/*.hxx", "source/*.ixx" }
files { "source/resources/Versioninfo.rc" }
+ links { "LogitechLEDLib.lib" }
includedirs { "external/hooking" }
includedirs { "external/injector/include" }
@@ -61,6 +98,6 @@ workspace "MaxPayne3.FusionFix"
defines { "NDEBUG" }
optimize "On"
staticruntime "On"
-
+
project "MaxPayne3.FusionFix"
- setpaths("E:/Games/Steam/steamapps/common/Max Payne 3/Max Payne 3/", "MaxPayne3.exe", "plugins/")
\ No newline at end of file
+ setpaths("Z:/WFP/Games/Max Payne/Max Payne 3/", "MaxPayne3.exe", "plugins/")
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 4f6d802..fa42c3c 100644
--- a/readme.md
+++ b/readme.md
@@ -1,4 +1,4 @@
-[](https://github.com/ThirteenAG/MaxPayne3.FusionFix/actions) [](https://discord.gg/RaZXpKExNj)
+[](https://github.com/ThirteenAG/MaxPayne3.FusionFix/actions)
diff --git a/release.bat b/release.bat
new file mode 100644
index 0000000..c33b59f
--- /dev/null
+++ b/release.bat
@@ -0,0 +1,3 @@
+copy bin\MaxPayne3.FusionFix.asi data\plugins\MaxPayne3.FusionFix.asi
+
+7z a "MaxPayne3.FusionFix.zip" ".\data\*"
diff --git a/release.md b/release.md
new file mode 100644
index 0000000..59b011b
--- /dev/null
+++ b/release.md
@@ -0,0 +1 @@
+[README](https://github.com/ThirteenAG/MaxPayne3.FusionFix#readme)
\ No newline at end of file
diff --git a/source/buttons.ixx b/source/buttons.ixx
new file mode 100644
index 0000000..bddb765
--- /dev/null
+++ b/source/buttons.ixx
@@ -0,0 +1,189 @@
+module;
+
+#include
+#include
+
+export module buttons;
+
+import common;
+import settings;
+
+class Buttons
+{
+private:
+ static inline std::vector btnPrefix = {
+ "", //XBOX360
+ "XBONE_",
+ "PS3_",
+ "PS4_",
+ "PS5_",
+ "SWITCH_",
+ "SD_",
+ };
+
+ static inline std::vector buttons = {
+ "UP_ARROW", "", "", "DOWN_ARROW", "", "", "LEFT_ARROW", "", "", "RIGHT_ARROW", "", "", "DPAD_UP", "", "",
+ "DPAD_DOWN", "", "", "DPAD_LEFT", "", "", "DPAD_RIGHT", "", "", "DPAD_NONE", "", "", "DPAD_ALL", "", "",
+ "DPAD_UPDOWN", "", "", "DPAD_LEFTRIGHT", "", "", "LSTICK_UP", "", "", "LSTICK_DOWN", "", "", "LSTICK_LEFT",
+ "", "", "LSTICK_RIGHT", "", "", "LSTICK_NONE", "", "", "LSTICK_ALL", "", "", "LSTICK_UPDOWN", "", "",
+ "LSTICK_LEFTRIGHT", "", "", "RSTICK_UP", "", "", "RSTICK_DOWN", "", "", "RSTICK_LEFT", "", "", "RSTICK_RIGHT",
+ "", "", "RSTICK_NONE", "", "", "RSTICK_ALL", "", "", "RSTICK_UPDOWN", "", "", "RSTICK_LEFTRIGHT", "", "", "A_BUTT",
+ "", "", "B_BUTT", "", "", "X_BUTT", "", "", "Y_BUTT", "", "", "LB_BUTT", "", "", "LT_BUTT", "", "", "RB_BUTT", "",
+ "", "RT_BUTT", "", "", "START_BUTT", "", "", "BACK_BUTT", "", "", "A_BUTT", "", "", "B_BUTT"
+ };
+
+ static inline std::vector controllerTexPtrs;
+ static inline std::vector controllerTexHashes;
+ static inline std::vector> buttonTexPtrs;
+ static inline void** gameButtonPtrs = nullptr;
+ static inline void** gameControllerPtrs = nullptr;
+ static inline void** controllerDstTexPtr = nullptr;
+ static void ButtonsCallback()
+ {
+ auto prefvalueindex = FusionFixSettings.GetInt("PREF_BUTTONS");
+ if (gameButtonPtrs)
+ {
+ for (auto b = buttons.begin(); b < buttons.end(); b++)
+ {
+ if (!b->empty())
+ {
+ auto i = std::distance(std::begin(buttons), b);
+ auto ptr = buttonTexPtrs[prefvalueindex][i];
+ if (ptr)
+ gameButtonPtrs[i] = ptr;
+ }
+ }
+ }
+ }
+
+ static inline injector::hook_back CTxdStore__LoadTexture;
+ static void __fastcall LoadCustomButtons(void* dst, void* edx, const char* name)
+ {
+ CTxdStore__LoadTexture.fun(dst, edx, name);
+
+ buttonTexPtrs.clear();
+ buttonTexPtrs.resize(btnPrefix.size());
+
+ for (auto& v : buttonTexPtrs)
+ v.resize(buttons.size());
+
+ for (auto prefix = btnPrefix.begin(); prefix < btnPrefix.end(); prefix++)
+ {
+ for (auto b = buttons.begin(); b < buttons.end(); b++)
+ {
+ if (!b->empty())
+ {
+ auto texName = *prefix + *b;
+ CTxdStore__LoadTexture.fun(&buttonTexPtrs[std::distance(std::begin(btnPrefix), prefix)][std::distance(std::begin(buttons), b)], edx, texName.c_str());
+ }
+ }
+ }
+
+ ButtonsCallback();
+ }
+
+ static inline bool bIsController = false;
+ static inline injector::hook_back hbsub_DCFB30;
+ static uint32_t __cdecl sub_DCFB30(const char* name, int a2)
+ {
+ if (iequals(std::string(name), "controller"))
+ {
+ bIsController = true;
+
+ controllerTexHashes.clear();
+ controllerTexHashes.resize(btnPrefix.size());
+ controllerTexPtrs.clear();
+ controllerTexPtrs.resize(btnPrefix.size());
+
+ for (auto prefix = btnPrefix.begin(); prefix < btnPrefix.end(); prefix++)
+ {
+ auto texName = *prefix + "CONTROLLER";
+ controllerTexHashes[std::distance(std::begin(btnPrefix), prefix)] = hbsub_DCFB30.fun(texName.c_str(), a2);
+ }
+ }
+ return hbsub_DCFB30.fun(name, a2);
+ }
+
+ static inline injector::hook_back hbsub_CE8060;
+ static void* __fastcall sub_CE8060(void* _this, void* edx, uint32_t hash)
+ {
+ if (bIsController)
+ {
+ for (auto prefix = btnPrefix.begin(); prefix < btnPrefix.end(); prefix++)
+ {
+ auto texName = *prefix + "CONTROLLER";
+ controllerTexPtrs[std::distance(std::begin(btnPrefix), prefix)] = hbsub_CE8060.fun(_this, edx, controllerTexHashes[std::distance(std::begin(btnPrefix), prefix)]);
+ }
+ }
+ return hbsub_CE8060.fun(_this, edx, hash);
+ }
+
+public:
+ Buttons()
+ {
+ FusionFix::onInitEvent() += []()
+ {
+ auto pattern = hook::pattern("B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? B9");
+ gameButtonPtrs = *pattern.get_first(1);
+
+ pattern = hook::pattern("E8 ? ? ? ? 83 C4 08 EB 02 33 C0 8B 4C 24 08 8B 16");
+ hbsub_DCFB30.fun = injector::MakeCALL(pattern.get_first(0), sub_DCFB30).get();
+
+ pattern = hook::pattern("E8 ? ? ? ? 89 86 ? ? ? ? 85 C0 74 1B");
+ hbsub_CE8060.fun = injector::MakeCALL(pattern.get_first(0), sub_CE8060).get();
+
+ pattern = hook::pattern("E8 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? C6 05 ? ? ? ? ? 5E");
+ CTxdStore__LoadTexture.fun = injector::MakeCALL(pattern.get_first(), LoadCustomButtons).get();
+
+ pattern = hook::pattern("89 86 ? ? ? ? 85 C0 74 1B");
+ struct LoadControllerTexture
+ {
+ void operator()(injector::reg_pack& regs)
+ {
+ *(uint32_t*)(regs.esi + 0x104) = regs.eax;
+ if (bIsController)
+ {
+ controllerDstTexPtr = (void**)(regs.esi + 0x104);
+ auto v = FusionFixSettings.GetInt("PREF_BUTTONS");
+ if (v > 6) v = 0; else if (v < 0) v = 6;
+ if (controllerTexPtrs[v])
+ *controllerDstTexPtr = controllerTexPtrs[v];
+ }
+ bIsController = false;
+ }
+ }; injector::MakeInline(pattern.get_first(0), pattern.get_first(6));
+
+ FusionFix::onIniFileChange() += []()
+ {
+ ButtonsCallback();
+ };
+
+ FusionFix::onMenuOptionChange() += [](std::string_view name, int32_t oldVal, int32_t curVal)
+ {
+ if (name == "MS_Control.ConfigurationList")
+ {
+ if (oldVal == 3 && curVal == 4)
+ {
+ auto v = FusionFixSettings.GetInt("PREF_BUTTONS") + 1;
+ if (v > 6) v = 0; else if (v < 0) v = 6;
+
+ CIniReader iniWriter("");
+ iniWriter.WriteInteger("MAIN", "GamepadIcons", v);
+ if (controllerDstTexPtr && controllerTexPtrs[v])
+ *controllerDstTexPtr = controllerTexPtrs[v];
+ }
+ else if (oldVal == 4 && curVal == 3)
+ {
+ auto v = FusionFixSettings.GetInt("PREF_BUTTONS") - 1;
+ if (v > 6) v = 0; else if (v < 0) v = 6;
+
+ CIniReader iniWriter("");
+ iniWriter.WriteInteger("MAIN", "GamepadIcons", v);
+ if (controllerDstTexPtr && controllerTexPtrs[v])
+ *controllerDstTexPtr = controllerTexPtrs[v];
+ }
+ }
+ };
+ };
+ }
+} Buttons;
\ No newline at end of file
diff --git a/source/common.hxx b/source/common.hxx
new file mode 100644
index 0000000..0c32eb1
--- /dev/null
+++ b/source/common.hxx
@@ -0,0 +1,22 @@
+#pragma once
+#define WIN32_LEAN_AND_MEAN
+#include
+#include
+#include "IniReader.h"
+#include "injector/injector.hpp"
+#include "injector/calling.hpp"
+#include "injector/hooking.hpp"
+#include "injector/assembly.hpp"
+#include "injector/utility.hpp"
+#include "Hooking.Patterns.h"
+#include "ModuleList.hpp"
+#include
+#include
+#include
+#include