From 0e60da7e0f05660858d90be70f3939cbb75c6b73 Mon Sep 17 00:00:00 2001
From: somewhatlurker <52014015+somewhatlurker@users.noreply.github.com>
Date: Fri, 4 Oct 2019 15:43:05 +1000
Subject: [PATCH] add ShaderPatch plugin
---
source-code/PD-Loader.sln | 10 +
source-code/data/plugins/ShaderPatch.ini | 3 +
.../plugins/ShaderPatch/ShaderPatch.vcxproj | 132 +++++++++++
.../ShaderPatch/ShaderPatch.vcxproj.filters | 27 +++
.../plugins/ShaderPatch/src/dllmain.cpp | 117 ++++++++++
.../plugins/ShaderPatch/src/framework.h | 205 ++++++++++++++++++
6 files changed, 494 insertions(+)
create mode 100644 source-code/data/plugins/ShaderPatch.ini
create mode 100644 source-code/source/plugins/ShaderPatch/ShaderPatch.vcxproj
create mode 100644 source-code/source/plugins/ShaderPatch/ShaderPatch.vcxproj.filters
create mode 100644 source-code/source/plugins/ShaderPatch/src/dllmain.cpp
create mode 100644 source-code/source/plugins/ShaderPatch/src/framework.h
diff --git a/source-code/PD-Loader.sln b/source-code/PD-Loader.sln
index ee6d287..ae1f02a 100644
--- a/source-code/PD-Loader.sln
+++ b/source-code/PD-Loader.sln
@@ -19,6 +19,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DivaSound", "source\plugins
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DivaMovie", "source\plugins\DivaMovie\DivaMovie.vcxproj", "{1994C586-F73B-4EF2-BA3E-2A0FC3034B1A}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShaderPatch", "source\plugins\ShaderPatch\ShaderPatch.vcxproj", "{C07E495E-A16F-48FA-80E4-0296B5234F0B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -87,6 +89,14 @@ Global
{1994C586-F73B-4EF2-BA3E-2A0FC3034B1A}.Release|x64.ActiveCfg = Release|x64
{1994C586-F73B-4EF2-BA3E-2A0FC3034B1A}.Release|x64.Build.0 = Release|x64
{1994C586-F73B-4EF2-BA3E-2A0FC3034B1A}.Release|x86.ActiveCfg = Release|x64
+ {C07E495E-A16F-48FA-80E4-0296B5234F0B}.Debug|x64.ActiveCfg = Debug|x64
+ {C07E495E-A16F-48FA-80E4-0296B5234F0B}.Debug|x64.Build.0 = Debug|x64
+ {C07E495E-A16F-48FA-80E4-0296B5234F0B}.Debug|x86.ActiveCfg = Debug|Win32
+ {C07E495E-A16F-48FA-80E4-0296B5234F0B}.Debug|x86.Build.0 = Debug|Win32
+ {C07E495E-A16F-48FA-80E4-0296B5234F0B}.Release|x64.ActiveCfg = Release|x64
+ {C07E495E-A16F-48FA-80E4-0296B5234F0B}.Release|x64.Build.0 = Release|x64
+ {C07E495E-A16F-48FA-80E4-0296B5234F0B}.Release|x86.ActiveCfg = Release|Win32
+ {C07E495E-A16F-48FA-80E4-0296B5234F0B}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/source-code/data/plugins/ShaderPatch.ini b/source-code/data/plugins/ShaderPatch.ini
new file mode 100644
index 0000000..e47fad8
--- /dev/null
+++ b/source-code/data/plugins/ShaderPatch.ini
@@ -0,0 +1,3 @@
+blinn_per_vert\.0010010[23]00[01]\.fp=arch:GP,TU,GV,GM||from:TEX tex_col, a_tex_color0, texture\[0\], 2D; ALIAS||to:TEX tex_col, a_tex_color0, texture[0], 2D; MUL tex_col, 1, tex_col; ALIAS
+esm_gauss\.0\.fp=arch:TU||from:RCP sum.y, sum.y;(\r?\n) MUL||to:RCP sum.y, sum.y; ADD sum.x, sum.x, 0.000300;$1 MUL
+cloth_npr1\.fp=arch:TU||from:SSG _tmp0.yw, ybr.xxzz;||to:TEMP _tmpForSSG; SGE _tmpForSSG.xz, ybr.xxzz, 0; SUB _tmpForSSG.xz, _tmpForSSG.xxzz, 1; SGT _tmpForSSG.yw, ybr.xxzz, 0; ADD _tmp0.yw, _tmpForSSG.xxzz, _tmpForSSG.yyww;
\ No newline at end of file
diff --git a/source-code/source/plugins/ShaderPatch/ShaderPatch.vcxproj b/source-code/source/plugins/ShaderPatch/ShaderPatch.vcxproj
new file mode 100644
index 0000000..4f10798
--- /dev/null
+++ b/source-code/source/plugins/ShaderPatch/ShaderPatch.vcxproj
@@ -0,0 +1,132 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {C07E495E-A16F-48FA-80E4-0296B5234F0B}
+ ShaderPatch
+ 10.0
+
+
+
+ Application
+ true
+ v142
+ MultiByte
+
+
+ Application
+ false
+ v142
+ true
+ MultiByte
+
+
+ DynamicLibrary
+ true
+ v142
+ MultiByte
+
+
+ DynamicLibrary
+ false
+ v142
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .dva
+
+
+ .dva
+
+
+
+ Level3
+ Disabled
+ true
+
+
+
+
+ Level3
+ Disabled
+ true
+ ..\..\..\dependencies\detours\include
+
+
+ ..\..\..\dependencies\detours\lib
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+
+
+ true
+ true
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ ..\..\..\dependencies\detours\include
+
+
+ true
+ true
+ ..\..\..\dependencies\detours\lib
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source-code/source/plugins/ShaderPatch/ShaderPatch.vcxproj.filters b/source-code/source/plugins/ShaderPatch/ShaderPatch.vcxproj.filters
new file mode 100644
index 0000000..3d3bf67
--- /dev/null
+++ b/source-code/source/plugins/ShaderPatch/ShaderPatch.vcxproj.filters
@@ -0,0 +1,27 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/source-code/source/plugins/ShaderPatch/src/dllmain.cpp b/source-code/source/plugins/ShaderPatch/src/dllmain.cpp
new file mode 100644
index 0000000..1cb1293
--- /dev/null
+++ b/source-code/source/plugins/ShaderPatch/src/dllmain.cpp
@@ -0,0 +1,117 @@
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#include
+
+#include "framework.h"
+//#include "PluginConfigApi.h"
+#include "detours.h"
+#pragma comment(lib, "detours.lib")
+
+#include
+#include
+
+void InjectCode(void* address, const std::vector data)
+{
+ const size_t byteCount = data.size() * sizeof(uint8_t);
+
+ DWORD oldProtect;
+ VirtualProtect(address, byteCount, PAGE_EXECUTE_READWRITE, &oldProtect);
+ memcpy(address, data.data(), byteCount);
+ VirtualProtect(address, byteCount, oldProtect, nullptr);
+}
+
+void NopBytes(void* address, unsigned int num)
+{
+ std::vector newbytes = {};
+
+ for (unsigned int i = 0; i < num; ++i) newbytes.push_back(0x90);
+
+ InjectCode(address, newbytes);
+}
+
+
+void hookedLoadFromFarcThunk(FArchivedFile** ppFile)
+{
+ loadFromFarcThunk(ppFile);
+
+ if (ppFile == nullptr || *ppFile == nullptr)
+ return;
+
+ FArchivedFile &file = **ppFile;
+
+ if (file.notLoadedFlag != 0)
+ return;
+
+ std::string modifiedStr = "";
+
+ for (ShaderPatchInfo &patch : patchesVec)
+ {
+ bool archMatches = false;
+
+ for (std::string &arch : patch.archs)
+ {
+ if (gpuName.size() >= arch.size() && gpuName.rfind(arch, 0) == 0)
+ {
+ archMatches = true;
+ break;
+ }
+ }
+
+ if (archMatches && std::regex_match(file.fileName.GetCharBuf(), patch.fileRegex))
+ {
+ if (modifiedStr.length() == 0)
+ modifiedStr = std::string(file.data, file.dataSize);
+
+ modifiedStr = std::regex_replace(modifiedStr, patch.dataRegex, patch.dataReplace);
+ }
+ }
+
+ if (modifiedStr.length() > 0)
+ {
+ file.data = _strdup(modifiedStr.c_str());
+ file.dataSize = modifiedStr.size();
+ printf("[ShaderPatch] Patched %s\n", file.fileName.GetCharBuf());
+ }
+}
+
+
+BOOL APIENTRY DllMain(HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+)
+{
+ if (ul_reason_for_call == DLL_PROCESS_ATTACH)
+ {
+ // install a thunk to hook
+ InjectCode((void*)0x1405e5991, { 0x48, 0x83, 0xec, 0x20 }); // SUB RSP, 0x20
+ InjectCode((void*)0x1405e5995, { 0xe8, 0x56, 0xe8, 0xb9, 0xff }); // CALL 0x1401841f0
+ InjectCode((void*)0x1405e599a, { 0x48, 0x83, 0xc4, 0x20 }); // ADD RSP, 0x20
+ InjectCode((void*)0x1405e599e, { 0xc3 }); // RET
+ InjectCode((void*)0x1405e534a, { 0xe8, 0x42, 0x06, 0x00, 0x00 }); // CALL 0x1405e5991
+
+ DisableThreadLibraryCalls(hModule);
+ DetourTransactionBegin();
+ DetourUpdateThread(GetCurrentThread());
+ DetourAttach(&(PVOID&)loadFromFarcThunk, (PVOID)hookedLoadFromFarcThunk);
+ DetourTransactionCommit();
+
+ loadGpuName();
+ printf("[ShaderPatch] Detected GPU: %s\n", gpuName);
+ //Sleep(2000);
+
+ LoadConfig();
+ }
+ return TRUE;
+}
+
+
+//using namespace PluginConfig;
+//
+//extern "C" __declspec(dllexport) LPCWSTR GetPluginName(void)
+//{
+// return L"ShaderPatch";
+//}
+//
+//extern "C" __declspec(dllexport) LPCWSTR GetPluginDescription(void)
+//{
+// return L"Automatically patches shaders";
+//}
\ No newline at end of file
diff --git a/source-code/source/plugins/ShaderPatch/src/framework.h b/source-code/source/plugins/ShaderPatch/src/framework.h
new file mode 100644
index 0000000..4f476a2
--- /dev/null
+++ b/source-code/source/plugins/ShaderPatch/src/framework.h
@@ -0,0 +1,205 @@
+#pragma once
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+struct ShaderPatchInfo {
+ std::regex fileRegex;
+ std::regex dataRegex;
+ std::string dataReplace;
+ std::vector archs;
+};
+
+std::vector patchesVec;
+
+#pragma pack(push, 1)
+struct MsString {
+ union {
+ char* string_ptr;
+ char string_buf[16];
+ };
+ uint64_t unk;
+ uint64_t size;
+
+ char* GetCharBuf()
+ {
+ if (size > 0xf && string_ptr != nullptr)
+ return string_ptr;
+ else
+ return string_buf;
+ };
+};
+
+struct FArchivedFile {
+ uint8_t padding00[0x10];
+ uint8_t notLoadedFlag;
+ uint8_t padding11[0x07];
+ MsString farcPath;
+ MsString fileName;
+ uint8_t padding58[0x38];
+ uint64_t dataSize;
+ char* data;
+};
+#pragma pack(pop)
+
+void (__stdcall* loadFromFarcThunk)(FArchivedFile** file) = (void(__stdcall*)(FArchivedFile** file))0x1405e5991;
+
+
+std::string gpuName;
+void*(*NvAPI_QueryInterface)(unsigned int offset) = NULL;
+int(*NvAPI_Initialize)() = NULL;
+int(*NvAPI_Unload)() = NULL;
+int(*NvAPI_EnumPhysicalGPUs)(int64_t** handle, int* count) = NULL;
+int(*NvAPI_GPU_GetShortName)(int64_t* handle, char* name) = NULL;
+
+void loadGpuName()
+{
+ NvAPI_QueryInterface = (void*(*)(unsigned int offset))GetProcAddress(LoadLibrary("nvapi64.dll"), "nvapi_QueryInterface");
+
+ if (NvAPI_QueryInterface == nullptr)
+ {
+ gpuName = "Other";
+ return;
+ }
+
+ NvAPI_Initialize = (int(*)())NvAPI_QueryInterface(0x0150E828);
+ NvAPI_Unload = (int(*)()) NvAPI_QueryInterface(0xD22BDD7E);
+ NvAPI_EnumPhysicalGPUs = (int(*)(int64_t** handle, int* count))NvAPI_QueryInterface(0xE5AC921F);
+ NvAPI_GPU_GetShortName = (int(*)(int64_t* handle, char* name))NvAPI_QueryInterface(0xD988F0F3);
+
+ if (NvAPI_Initialize == nullptr ||
+ NvAPI_Unload == nullptr ||
+ NvAPI_EnumPhysicalGPUs == nullptr ||
+ NvAPI_GPU_GetShortName == nullptr)
+ {
+ gpuName = "Unknown";
+ return;
+ }
+
+ int64_t* hdlGpu[64] = { 0 };
+ int nGpu = 0;
+
+ NvAPI_Initialize();
+ NvAPI_EnumPhysicalGPUs(hdlGpu, &nGpu);
+
+ //printf("[ShaderPatch] nGpu: %d\n", nGpu);
+ if (nGpu < 1)
+ {
+ gpuName = "Unknown";
+ NvAPI_Unload();
+ return;
+ }
+
+ char nameBuf[64];
+ NvAPI_GPU_GetShortName(hdlGpu[0], nameBuf);
+ NvAPI_Unload();
+
+ gpuName = nameBuf;
+ return;
+}
+
+
+
+std::vector SplitString(const std::string& str, const std::string& delim)
+{
+ std::vector tokens;
+ size_t prev = 0, pos = 0;
+ do
+ {
+ pos = str.find(delim, prev);
+ if (pos == std::string::npos)
+ pos = str.length();
+
+ std::string token = str.substr(prev, pos - prev);
+
+ if (!token.empty())
+ tokens.push_back(token);
+
+ prev = pos + delim.length();
+ } while (pos < str.length() && prev < str.length());
+
+ return tokens;
+}
+
+
+std::wstring ExePath() {
+ WCHAR buffer[MAX_PATH];
+ GetModuleFileNameW(NULL, buffer, MAX_PATH);
+ return std::wstring(buffer);
+}
+
+std::wstring DirPath() {
+ std::wstring exepath = ExePath();
+ std::wstring::size_type pos = exepath.find_last_of(L"\\/");
+ return exepath.substr(0, pos);
+}
+
+std::wstring CONFIG_FILE_STRING = DirPath() + L"\\plugins\\ShaderPatch.ini";
+LPCWSTR CONFIG_FILE = CONFIG_FILE_STRING.c_str();
+
+void LoadConfig()
+{
+ std::ifstream fileStream(CONFIG_FILE_STRING);
+
+ if (!fileStream.is_open())
+ return;
+
+ std::string line;
+
+ // check for BOM
+ std::getline(fileStream, line);
+ if (line.size() >= 3 && line.rfind("\xEF\xBB\xBF", 0) == 0)
+ fileStream.seekg(3);
+ else
+ fileStream.seekg(0);
+
+ while (std::getline(fileStream, line))
+ {
+ try {
+ if (line.size() <= 0 || line[0] == '#' || line[0] == '[' || (line.size() >= 2 && line.rfind("//", 0) == 0))
+ continue;
+
+ std::vector equalSplit = SplitString(line, "=");
+ if (equalSplit.size() < 2) continue;
+
+ ShaderPatchInfo patch = ShaderPatchInfo();
+
+ patch.fileRegex = equalSplit[0];
+
+ std::vector rules = SplitString(equalSplit[1], "||");
+ if (rules.size() < 1) continue; // probably will never trigger but whatever
+
+ for (std::string &rule : rules)
+ {
+ if (rule.size() >= 5 && rule.rfind("arch:", 0) == 0)
+ {
+ rule.erase(0, 5);
+ patch.archs = SplitString(rule, ",");
+ }
+ else if (rule.size() >= 5 && rule.rfind("from:", 0) == 0)
+ {
+ rule.erase(0, 5);
+ patch.dataRegex = rule;
+ }
+ else if (rule.size() >= 3 && rule.rfind("to:", 0) == 0)
+ {
+ rule.erase(0, 3);
+ patch.dataReplace = rule;
+ }
+ }
+
+ patchesVec.push_back(patch);
+ }
+ catch (int e)
+ {
+
+ }
+ }
+
+ fileStream.close();
+}
\ No newline at end of file