diff --git a/source-code/data/plugins/ShaderPatch.ini b/source-code/data/plugins/ShaderPatch.ini index e47fad8..ae8e4e8 100644 --- a/source-code/data/plugins/ShaderPatch.ini +++ b/source-code/data/plugins/ShaderPatch.ini @@ -1,3 +1,33 @@ -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 +[Config] +# Patches to remove glitching with modern GPUs. +# Appropriate patches will automatically be selected based on your GPU. +Compat=1 + +# Lyb's toon shader improvements (24 sept 2019) +# Toon: Set to `0.9, -0.5` (or another pair of values) to use +Toon=0 +# Toon_Eyes: Set to anything except 0 to use +Toon_Eyes=0 +# Toon_Hair: Set to `1.25` (or another value) to use +Toon_Hair=0 +# Toon_Lines1: Set to `0.75` (or another value) to use +Toon_Lines1=0 +# Toon_Lines2: Set to `0.75, 0.75` (or another pair of values) to use +Toon_Lines2=0 + +[Patches] +# Compatibility patches: +# Star Story (and etc.) stage corruption (Maxwell+) +blinn_per_vert\.0010010[23]00[01]\.fp=arch:GP,TU,GV,GM||cfg:compat||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 +# Turing shading lines +esm_gauss\.0\.fp=arch:TU||cfg:compat||from:RCP sum.y, sum.y;(\r?\n) MUL||to:RCP sum.y, sum.y; ADD sum.x, sum.x, 0.000300;$1 MUL +# Turing NPR cloth noise +cloth_npr1\.fp=arch:TU||cfg:compat||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; + +# Lyb's toon shader improvements: +.*cloth_npr.*=cfg:toon||from:MAD tmp.y, lc.z, 1.2, -0.5;||to:MAD tmp.y, lc.z, ; +.*skin_default.*=cfg:toon||from:MAD diff.xyz, spec, spec_ratio, diff;||to: +.*eye.*=cfg:toon_eyes||from:ADD o_color_f0.w, diff.x, diff.y;||to: +.*hair_npr1.*=cfg:toon_hair||from:MUL spec.x, tmp.x, 0.7;||to:MUL spec.x, tmp.x, 0.7; MUL diff, diff, ; +.*tone_map_npr1.*=cfg:toon_lines1||from:MUL density.y, density.y, 0.25;||to:MUL density.y, density.y, ; +.*tone_map_npr1.*=cfg:toon_lines2||from:MAD density.w, density.x, 0.7, 0.25;||to:MAD density.w, density.x, ; \ 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 index 1cb1293..91bf6ae 100644 --- a/source-code/source/plugins/ShaderPatch/src/dllmain.cpp +++ b/source-code/source/plugins/ShaderPatch/src/dllmain.cpp @@ -29,6 +29,139 @@ void NopBytes(void* address, unsigned int num) } +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; +} + + +void LoadConfig() +{ + std::ifstream fileStream(CONFIG_FILE_STRING); + + if (!fileStream.is_open()) + return; + + std::string line; + std::string section = "patches"; + + // 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)) + { + if (line.size() <= 0 || line[0] == '#' || (line.size() >= 2 && line.rfind("//", 0) == 0)) + continue; + + if (line[0] == '[') // section name + { + size_t endIdx = line.find(']'); + section = line.substr(1, endIdx - 1); + std::transform(section.begin(), section.end(), section.begin(), ::tolower); + continue; + } + + std::vector equalSplit = SplitString(line, "="); + if (equalSplit.size() < 2) continue; + + if (section == "patches") + { + 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() >= 4 && rule.rfind("cfg:", 0) == 0) + { + rule.erase(0, 4); + // force cfg key to lower because ini shouldn't be case sensitive + std::transform(rule.begin(), rule.end(), rule.begin(), ::tolower); + patch.cfg = 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); + } + else if (section == "config") + { + equalSplit[1] = TrimString(equalSplit[1], " \t"); + if (equalSplit[1] != "0") // ignore when == 0 to allow disabling via checkbox + { + // force cfg key to lower because ini shouldn't be case sensitive + std::transform(equalSplit[0].begin(), equalSplit[0].end(), equalSplit[0].begin(), ::tolower); + configMap.insert(std::pair(equalSplit[0], equalSplit[1])); + } + } + } + + fileStream.close(); +} + + void hookedLoadFromFarcThunk(FArchivedFile** ppFile) { loadFromFarcThunk(ppFile); @@ -46,22 +179,39 @@ void hookedLoadFromFarcThunk(FArchivedFile** ppFile) for (ShaderPatchInfo &patch : patchesVec) { bool archMatches = false; - - for (std::string &arch : patch.archs) + if (patch.archs.size() > 0) { - if (gpuName.size() >= arch.size() && gpuName.rfind(arch, 0) == 0) + for (std::string &arch : patch.archs) { - archMatches = true; - break; + if (gpuName.size() >= arch.size() && gpuName.rfind(arch, 0) == 0) + { + archMatches = true; + break; + } } } + else + { + archMatches = true; + } - if (archMatches && std::regex_match(file.fileName.GetCharBuf(), patch.fileRegex)) + bool cfgMatches = false; + if (patch.cfg.length() == 0 || configMap.find(patch.cfg) != configMap.end()) + { + cfgMatches = true; + } + + if (archMatches && cfgMatches && 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 (patch.cfg.length() > 0) + { + modifiedStr = StringReplace(modifiedStr, "", configMap[patch.cfg]); + } } } diff --git a/source-code/source/plugins/ShaderPatch/src/framework.h b/source-code/source/plugins/ShaderPatch/src/framework.h index 4f476a2..3849a38 100644 --- a/source-code/source/plugins/ShaderPatch/src/framework.h +++ b/source-code/source/plugins/ShaderPatch/src/framework.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -13,10 +14,13 @@ struct ShaderPatchInfo { std::regex dataRegex; std::string dataReplace; std::vector archs; + std::string cfg; }; std::vector patchesVec; +std::map configMap; + #pragma pack(push, 1) struct MsString { union { @@ -57,52 +61,6 @@ 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) @@ -126,6 +84,36 @@ std::vector SplitString(const std::string& str, const std::string& return tokens; } +std::string TrimString(const std::string& str, const std::string& whitespace) +{ + const size_t strBegin = str.find_first_not_of(whitespace); + + if (strBegin == std::string::npos) + return ""; + + const size_t strEnd = str.find_last_not_of(whitespace); + const size_t strRange = strEnd - strBegin + 1; + + return str.substr(strBegin, strRange); +} + +std::string StringReplace(const std::string& str, const std::string& srch, const std::string& repl) +{ + size_t pos = 0; + std::string outstr = str; + + while (true) + { + pos = outstr.find(srch, pos); + if (pos == std::string::npos) + break; + + outstr.replace(pos, srch.length(), repl); + } + + return outstr; +} + std::wstring ExePath() { WCHAR buffer[MAX_PATH]; @@ -142,64 +130,3 @@ std::wstring DirPath() { 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