From 6a9c03c650f15c64abf230343fdca720ef7c7a5f 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