From 6ff8399d73b2cef20f2b4331aeb011e1e1826374 Mon Sep 17 00:00:00 2001 From: Elisha Riedlinger Date: Mon, 20 Nov 2017 12:45:57 -0800 Subject: [PATCH 1/8] Convert to ps_1_4 Convert PixelShader to version 1.4 to better handle modifiers on constants --- source/d3d8to9_device.cpp | 257 +++++++++++++++++++++++++++++++++++++- 1 file changed, 255 insertions(+), 2 deletions(-) diff --git a/source/d3d8to9_device.cpp b/source/d3d8to9_device.cpp index c573279..9cebedb 100644 --- a/source/d3d8to9_device.cpp +++ b/source/d3d8to9_device.cpp @@ -1721,32 +1721,285 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct SourceCode = std::regex_replace(SourceCode, std::regex(" \\/\\/ ps\\.1\\.[1-4]\\n((?! ).+\\n)+"), ""); + // Fix '-' modifier for constant values when using 'add' arithmetic by changing it to use 'sub' SourceCode = std::regex_replace(SourceCode, std::regex("(add)([_satxd248]*) (r[0-9][\\.wxyz]*), ((1-|)[crtv][0-9][\\.wxyz_abdis2]*), (-)(c[0-9][\\.wxyz]*)(_bx2|_bias|_x2|_d[zbwa]|)(?![_\\.wxyz])"), "sub$2 $3, $4, $7$8 /* changed 'add' to 'sub' removed modifier $6 */"); + + // Create a temporary varables for ps_1_4 + std::string SourceCode14 = SourceCode + "\n"; + int ArithmeticCount14 = ArithmeticCount; + // Fix modifiers for constant values by using any remaining arithmetic places to add an instruction to move the constant value to a temporary register for (int x = 8 - ArithmeticCount; x > 0; x--) { + // Make sure that the dest register is not already being used + const std::string destReg = std::regex_replace(SourceCode, std::regex(" [a-z_\\.0-9]* (r[0-9]).*(-c[0-9]|c[0-9][\\.wxyz]*_).*"),"$1", std::regex_constants::format_first_only); + const std::string sourceReg = std::regex_replace(SourceCode, std::regex(" [a-z_\\.0-9]* r[0-9](.*)(-c[0-9]|c[0-9][\\.wxyz]*_)(.*)"), "$1$3", std::regex_constants::format_first_only); + if (sourceReg.find(destReg) != std::string::npos) + { + break; + } + + // Replace one constant modifier using the dest register for a temporary register const size_t beforeReplace = SourceCode.size(); - // Only replace one match SourceCode = std::regex_replace(SourceCode, std::regex(" (...)(_[_satxd248]*|) (r[0-9][\\.wxyz]*), (1?-?[crtv][0-9][\\.wxyz_abdis2]*, )?(1?-?[crtv][0-9][\\.wxyz_abdis2]*, )?(1?-?[crtv][0-9][\\.wxyz_abdis2]*, )?((1?-)(c[0-9])([\\.wxyz]*)(_bx2|_bias|_x2|_d[zbwa]|)|(1?-?)(c[0-9])([\\.wxyz]*)(_bx2|_bias|_x2|_d[zbwa]))(?![_\\.wxyz])"), " mov $3, $9$10$13$14 /* added line */\n $1$2 $3, $4$5$8$12$3$11$15 /* changed $9$10$13$14 to $3 */", std::regex_constants::format_first_only); + // Check if string was replaced - if (SourceCode.size() - beforeReplace == 0) + if (SourceCode.size() == beforeReplace) { break; } + else + { + ArithmeticCount++; + } + } + + // Check if this should be converted to ps_1_4 + if (std::regex_search(SourceCode, std::regex("-c[0-9]|c[0-9][\\.wxyz]*_")) && // Check for modifiers on constants + !std::regex_search(SourceCode, std::regex("tex[bcdmr]")) && // Verify unsupported instructions are not used + std::regex_search(SourceCode, std::regex("ps_1_[0-3]"))) // Verify PixelShader is using version 1.0 to 1.3 + { + bool RegisterUsed[6] = { false }; + struct MyStrings + { + std::string dest; + std::string source; + }; + std::vector ReplaceReg; + std::string NewSourceCode = " ps_1_4\n"; + + // Ensure at least one command will be above the phase marker + bool PhaseMarkerSet = (ArithmeticCount14 >= 8); + if (SourceCode14.find("def c") == std::string::npos && !PhaseMarkerSet) + { + for (size_t j = 0; j < 8; j++) + { + const std::string reg = "c" + std::to_string(j); + + if (SourceCode14.find(reg) == std::string::npos) + { + PhaseMarkerSet = true; + NewSourceCode.append(" def " + reg + ", 0, 0, 0, 0\n"); + break; + } + } + } + + // Update registers to use different numbers from textures + size_t FirstReg = 0; + for (size_t j = 0; j < 2; j++) + { + const std::string reg = "r" + std::to_string(j); + + if (std::regex_search(SourceCode14, std::regex(reg))) + { + while (SourceCode14.find("t" + std::to_string(FirstReg)) != std::string::npos) + { + FirstReg++; + } + SourceCode14 = std::regex_replace(SourceCode14, std::regex(reg), "r" + std::to_string(FirstReg)); + FirstReg++; + } + } + + // Set phase location + size_t PhasePosition = NewSourceCode.length(); + size_t TexturePosition = 0; + + // Loop through each line + size_t LinePosition = 1; + std::string NewLine = SourceCode14; + while (true) + { + // Get next line + size_t tmpLinePos = SourceCode14.find("\n", LinePosition) + 1; + if (tmpLinePos == std::string::npos || tmpLinePos < LinePosition) + { + break; + } + LinePosition = tmpLinePos; + NewLine = &SourceCode14[LinePosition]; + tmpLinePos = NewLine.find("\n"); + if (tmpLinePos != std::string::npos) + { + NewLine.resize(tmpLinePos); + } + + // Skip 'ps_x_x' lines + if (std::regex_search(NewLine, std::regex("ps_._."))) + { + // Do nothing + } + + // Check for 'def' and add before 'phase' statment + else if (std::regex_search(NewLine, std::regex("def c[0-9]"))) + { + PhaseMarkerSet = true; + const std::string tmpLine = NewLine + "\n"; + NewSourceCode.insert(PhasePosition, tmpLine); + PhasePosition += tmpLine.length(); + } + + // Check for 'tex' and update to 'texld' + else if (std::regex_search(NewLine, std::regex("tex t[0-9]"))) + { + const std::string regNum = std::regex_replace(NewLine, std::regex(".*tex t([0-9]).*"), "$1"); + const std::string tmpLine = " texld r" + regNum + ", t" + regNum + "\n"; + + RegisterUsed[atoi(regNum.c_str())] = true; + NewSourceCode.insert(PhasePosition, tmpLine); + if (PhaseMarkerSet) + { + TexturePosition += tmpLine.length(); + } + else + { + PhaseMarkerSet = true; + PhasePosition += tmpLine.length(); + } + } + + // Other instructions + else + { + // Check for constant modifiers and update them to use unused temp register + if (std::regex_search(NewLine, std::regex("-c[0-9]|c[0-9][\\.wxyz]*_"))) + { + for (size_t j = 0; j < 6; j++) + { + std::string reg = "r" + std::to_string(j); + + if (NewSourceCode.find(reg) == std::string::npos) + { + const std::string constNum = std::regex_replace(NewLine, std::regex(".*(c[0-9])_.*|.*-(c[0-9]).*"), "$1$2"); + + if (std::regex_search(SourceCode14.substr(LinePosition + NewLine.length(), SourceCode14.length()), std::regex("-" + constNum + "|" + constNum + "[\\.wxyz]*_"))) + { + while (j < 6 && + NewSourceCode.find("r" + std::to_string(j)) != std::string::npos && + SourceCode14.find("r" + std::to_string(j)) != std::string::npos) + { + j++; + } + if (j < 6) + { + reg = "r" + std::to_string(j); + SourceCode14 = std::regex_replace(SourceCode14, std::regex(constNum), reg); + } + } + + const std::string tmpLine = " mov " + reg + ", " + constNum + "\n"; + + NewLine = std::regex_replace(NewLine, std::regex(constNum), reg); + if (ArithmeticCount14 < 8) + { + NewSourceCode.insert(PhasePosition + TexturePosition, tmpLine); + ArithmeticCount14++; + } + else + { + PhaseMarkerSet = true; + NewSourceCode.insert(PhasePosition, tmpLine); + PhasePosition += tmpLine.length(); + } + break; + } + } + } + + // Update registers + if (ReplaceReg.size() > 0) + { + for (size_t x = 0; x < ReplaceReg.size(); x++) + { + if (NewLine.find(ReplaceReg[x].dest) != std::string::npos) + { + size_t start = LinePosition + NewLine.length(); + start = (SourceCode14.substr(start, 4).find("+") == std::string::npos) ? start : SourceCode14.find("\n", start + 1); + + if (SourceCode14.find(ReplaceReg[x].dest, start) == std::string::npos) + { + NewLine = std::regex_replace(NewLine, std::regex("([ \\+]+[a-z_\\.0-9]* )r[0-9](.*)"), "$1" + ReplaceReg[x].source + "$2"); + ReplaceReg.erase(ReplaceReg.begin() + x); + break; + } + } + } + } + + // Check if texture is no longer being used and update the dest register + if (std::regex_search(NewLine, std::regex("t[0-9]"))) + { + const std::string texNum = std::regex_replace(NewLine, std::regex(".*t([0-9]).*"), "$1"); + + size_t start = LinePosition + NewLine.length(); + start = (SourceCode14.substr(start, 4).find("+") == std::string::npos) ? start : SourceCode14.find("\n", start + 1); + + if (SourceCode14.find("t" + texNum, start) == std::string::npos) + { + const std::string destRegNum = std::regex_replace(NewLine, std::regex("[ \\+]+[a-z_\\.0-9]* r([0-9]).*"), "$1"); + + if (!RegisterUsed[atoi(destRegNum.c_str())]) + { + if (NewSourceCode.find("r" + destRegNum) == std::string::npos || + SourceCode14.find("r" + destRegNum, start) == std::string::npos) + { + NewLine = std::regex_replace(NewLine, std::regex("([ \\+]+[a-z_\\.0-9]* )r[0-9](.*)"), "$1r" + texNum + "$2"); + const std::string tempSourceCode = std::regex_replace(&SourceCode14[start], std::regex("r" + destRegNum), "r" + texNum); + SourceCode14.resize(start); + SourceCode14.append(tempSourceCode); + } + else + { + RegisterUsed[atoi(destRegNum.c_str())] = true; + MyStrings tempReplaceReg; + tempReplaceReg.dest = "r" + destRegNum; + tempReplaceReg.source = "r" + texNum; + ReplaceReg.push_back(tempReplaceReg); + } + } + } + } + + // Add line to SourceCode + NewLine = std::regex_replace(NewLine, std::regex("t([0-9])"), "r$1") + "\n"; + NewSourceCode.append(NewLine); + } + } + + // Add 'phase' instruction + NewSourceCode.insert(PhasePosition, " phase\n"); + + // Test if ps_1_4 assembles + if (SUCCEEDED(D3DXAssembleShader(NewSourceCode.data(), static_cast(NewSourceCode.size()), nullptr, nullptr, 0, &Assembly, &ErrorBuffer))) + { + SourceCode = NewSourceCode; + } + else + { +#ifndef D3D8TO9NOLOG + LOG << "> Failed to convert shader to ps_1_4" << std::endl; + LOG << "> Dumping translated shader assembly:" << std::endl << std::endl << NewSourceCode << std::endl; + LOG << "> Failed to reassemble shader:" << std::endl << std::endl << static_cast(ErrorBuffer->GetBufferPointer()) << std::endl; +#endif + } } + // Change '-' modifier for constant values when using 'mad' arithmetic by changing it to use 'sub' SourceCode = std::regex_replace(SourceCode, std::regex("(mad)([_satxd248]*) (r[0-9][\\.wxyz]*), (1?-?[crtv][0-9][\\.wxyz_abdis2]*), (1?-?[crtv][0-9][\\.wxyz_abdis2]*), (-)(c[0-9][\\.wxyz]*)(_bx2|_bias|_x2|_d[zbwa]|)(?![_\\.wxyz])"), "sub$2 $3, $4, $7$8 /* changed 'mad' to 'sub' removed $5 removed modifier $6 */"); + // Remove trailing modifiers for constant values SourceCode = std::regex_replace(SourceCode, std::regex("(c[0-9][\\.wxyz]*)(_bx2|_bias|_x2|_d[zbwa])"), "$1 /* removed modifier $2 */"); + // Remove remaining modifiers for constant values SourceCode = std::regex_replace(SourceCode, std::regex("(1?-)(c[0-9][\\.wxyz]*(?![\\.wxyz]))"), From fa7a87e39ecd81c5cf17f42966a7586350b2a623 Mon Sep 17 00:00:00 2001 From: Elisha Riedlinger Date: Mon, 20 Nov 2017 16:46:58 -0800 Subject: [PATCH 2/8] Update regex_search to use std::string.find --- source/d3d8to9_device.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/d3d8to9_device.cpp b/source/d3d8to9_device.cpp index 9cebedb..187e5f5 100644 --- a/source/d3d8to9_device.cpp +++ b/source/d3d8to9_device.cpp @@ -1796,7 +1796,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct { const std::string reg = "r" + std::to_string(j); - if (std::regex_search(SourceCode14, std::regex(reg))) + if (SourceCode14.find(reg) != std::string::npos) { while (SourceCode14.find("t" + std::to_string(FirstReg)) != std::string::npos) { @@ -1837,7 +1837,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct } // Check for 'def' and add before 'phase' statment - else if (std::regex_search(NewLine, std::regex("def c[0-9]"))) + else if (NewLine.find("def c") != std::string::npos) { PhaseMarkerSet = true; const std::string tmpLine = NewLine + "\n"; @@ -1846,7 +1846,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct } // Check for 'tex' and update to 'texld' - else if (std::regex_search(NewLine, std::regex("tex t[0-9]"))) + else if (NewLine.find("tex t") != std::string::npos) { const std::string regNum = std::regex_replace(NewLine, std::regex(".*tex t([0-9]).*"), "$1"); const std::string tmpLine = " texld r" + regNum + ", t" + regNum + "\n"; From a11371fdf2de7c3c9ded1ccd8944812fee0e7227 Mon Sep 17 00:00:00 2001 From: Elisha Riedlinger Date: Mon, 20 Nov 2017 23:58:03 -0800 Subject: [PATCH 3/8] Fix logic in while statments --- source/d3d8to9_device.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/source/d3d8to9_device.cpp b/source/d3d8to9_device.cpp index 187e5f5..17fc76b 100644 --- a/source/d3d8to9_device.cpp +++ b/source/d3d8to9_device.cpp @@ -1798,7 +1798,8 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct if (SourceCode14.find(reg) != std::string::npos) { - while (SourceCode14.find("t" + std::to_string(FirstReg)) != std::string::npos) + while (SourceCode14.find("t" + std::to_string(FirstReg)) != std::string::npos || + (SourceCode14.find("t" + std::to_string(FirstReg)) != std::string::npos && j != FirstReg)) { FirstReg++; } @@ -1851,7 +1852,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct const std::string regNum = std::regex_replace(NewLine, std::regex(".*tex t([0-9]).*"), "$1"); const std::string tmpLine = " texld r" + regNum + ", t" + regNum + "\n"; - RegisterUsed[atoi(regNum.c_str())] = true; + RegisterUsed[strtol(regNum.c_str(), nullptr, 10)] = true; NewSourceCode.insert(PhasePosition, tmpLine); if (PhaseMarkerSet) { @@ -1881,8 +1882,8 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct if (std::regex_search(SourceCode14.substr(LinePosition + NewLine.length(), SourceCode14.length()), std::regex("-" + constNum + "|" + constNum + "[\\.wxyz]*_"))) { while (j < 6 && - NewSourceCode.find("r" + std::to_string(j)) != std::string::npos && - SourceCode14.find("r" + std::to_string(j)) != std::string::npos) + (NewSourceCode.find("r" + std::to_string(j)) != std::string::npos || + SourceCode14.find("r" + std::to_string(j)) != std::string::npos)) { j++; } @@ -1944,7 +1945,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct { const std::string destRegNum = std::regex_replace(NewLine, std::regex("[ \\+]+[a-z_\\.0-9]* r([0-9]).*"), "$1"); - if (!RegisterUsed[atoi(destRegNum.c_str())]) + if (!RegisterUsed[strtol(destRegNum.c_str(), nullptr, 10)]) { if (NewSourceCode.find("r" + destRegNum) == std::string::npos || SourceCode14.find("r" + destRegNum, start) == std::string::npos) @@ -1956,7 +1957,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct } else { - RegisterUsed[atoi(destRegNum.c_str())] = true; + RegisterUsed[strtol(destRegNum.c_str(), nullptr, 10)] = true; MyStrings tempReplaceReg; tempReplaceReg.dest = "r" + destRegNum; tempReplaceReg.source = "r" + texNum; From 736b2445b2944f96394c098efc0c35aaf3badbf5 Mon Sep 17 00:00:00 2001 From: Elisha Riedlinger Date: Tue, 21 Nov 2017 22:35:51 -0800 Subject: [PATCH 4/8] Fix a few issues with ps_1_4 conversion --- source/d3d8to9_device.cpp | 45 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/source/d3d8to9_device.cpp b/source/d3d8to9_device.cpp index 17fc76b..5be4c97 100644 --- a/source/d3d8to9_device.cpp +++ b/source/d3d8to9_device.cpp @@ -1727,7 +1727,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct std::regex("(add)([_satxd248]*) (r[0-9][\\.wxyz]*), ((1-|)[crtv][0-9][\\.wxyz_abdis2]*), (-)(c[0-9][\\.wxyz]*)(_bx2|_bias|_x2|_d[zbwa]|)(?![_\\.wxyz])"), "sub$2 $3, $4, $7$8 /* changed 'add' to 'sub' removed modifier $6 */"); - // Create a temporary varables for ps_1_4 + // Create temporary varables for ps_1_4 std::string SourceCode14 = SourceCode + "\n"; int ArithmeticCount14 = ArithmeticCount; @@ -1735,14 +1735,14 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct for (int x = 8 - ArithmeticCount; x > 0; x--) { // Make sure that the dest register is not already being used - const std::string destReg = std::regex_replace(SourceCode, std::regex(" [a-z_\\.0-9]* (r[0-9]).*(-c[0-9]|c[0-9][\\.wxyz]*_).*"),"$1", std::regex_constants::format_first_only); - const std::string sourceReg = std::regex_replace(SourceCode, std::regex(" [a-z_\\.0-9]* r[0-9](.*)(-c[0-9]|c[0-9][\\.wxyz]*_)(.*)"), "$1$3", std::regex_constants::format_first_only); + const std::string destReg = std::regex_replace(SourceCode, std::regex("[ \\+]+[a-z_\\.0-9]+ (r[0-9]).*(-c[0-9]|c[0-9][\\.wxyz]*_).*"),"$1"); + const std::string sourceReg = std::regex_replace(SourceCode, std::regex("[ \\+]+[a-z_\\.0-9]+ r[0-9](.*)(-c[0-9]|c[0-9][\\.wxyz]*_)(.*)"), "$1$3"); if (sourceReg.find(destReg) != std::string::npos) { break; } - // Replace one constant modifier using the dest register for a temporary register + // Replace one constant modifier using the dest register as a temporary register const size_t beforeReplace = SourceCode.size(); SourceCode = std::regex_replace(SourceCode, std::regex(" (...)(_[_satxd248]*|) (r[0-9][\\.wxyz]*), (1?-?[crtv][0-9][\\.wxyz_abdis2]*, )?(1?-?[crtv][0-9][\\.wxyz_abdis2]*, )?(1?-?[crtv][0-9][\\.wxyz_abdis2]*, )?((1?-)(c[0-9])([\\.wxyz]*)(_bx2|_bias|_x2|_d[zbwa]|)|(1?-?)(c[0-9])([\\.wxyz]*)(_bx2|_bias|_x2|_d[zbwa]))(?![_\\.wxyz])"), @@ -1764,14 +1764,15 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct !std::regex_search(SourceCode, std::regex("tex[bcdmr]")) && // Verify unsupported instructions are not used std::regex_search(SourceCode, std::regex("ps_1_[0-3]"))) // Verify PixelShader is using version 1.0 to 1.3 { - bool RegisterUsed[6] = { false }; + bool RegisterUsed[7] = { false }; + RegisterUsed[6] = true; struct MyStrings { std::string dest; std::string source; }; std::vector ReplaceReg; - std::string NewSourceCode = " ps_1_4\n"; + std::string NewSourceCode = " ps_1_4 /* converted */\n"; // Ensure at least one command will be above the phase marker bool PhaseMarkerSet = (ArithmeticCount14 >= 8); @@ -1784,7 +1785,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct if (SourceCode14.find(reg) == std::string::npos) { PhaseMarkerSet = true; - NewSourceCode.append(" def " + reg + ", 0, 0, 0, 0\n"); + NewSourceCode.append(" def " + reg + ", 0, 0, 0, 0 /* added line */\n"); break; } } @@ -1799,7 +1800,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct if (SourceCode14.find(reg) != std::string::npos) { while (SourceCode14.find("t" + std::to_string(FirstReg)) != std::string::npos || - (SourceCode14.find("t" + std::to_string(FirstReg)) != std::string::npos && j != FirstReg)) + (SourceCode14.find("r" + std::to_string(FirstReg)) != std::string::npos && j != FirstReg)) { FirstReg++; } @@ -1824,7 +1825,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct break; } LinePosition = tmpLinePos; - NewLine = &SourceCode14[LinePosition]; + NewLine = SourceCode14.substr(LinePosition, SourceCode14.length()); tmpLinePos = NewLine.find("\n"); if (tmpLinePos != std::string::npos) { @@ -1852,7 +1853,8 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct const std::string regNum = std::regex_replace(NewLine, std::regex(".*tex t([0-9]).*"), "$1"); const std::string tmpLine = " texld r" + regNum + ", t" + regNum + "\n"; - RegisterUsed[strtol(regNum.c_str(), nullptr, 10)] = true; + const unsigned long Num = strtoul(regNum.c_str(), nullptr, 10); + RegisterUsed[(Num < 6) ? Num : 6] = true; NewSourceCode.insert(PhasePosition, tmpLine); if (PhaseMarkerSet) { @@ -1877,9 +1879,9 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct if (NewSourceCode.find(reg) == std::string::npos) { - const std::string constNum = std::regex_replace(NewLine, std::regex(".*(c[0-9])_.*|.*-(c[0-9]).*"), "$1$2"); + const std::string constReg = std::regex_replace(NewLine, std::regex(".*-(c[0-9]).*|.*(c[0-9])[\\.wxyz]*_.*"), "$1$2"); - if (std::regex_search(SourceCode14.substr(LinePosition + NewLine.length(), SourceCode14.length()), std::regex("-" + constNum + "|" + constNum + "[\\.wxyz]*_"))) + if (std::regex_search(SourceCode14.substr(LinePosition + NewLine.length(), SourceCode14.length()), std::regex("-" + constReg + "|" + constReg + "[\\.wxyz]*_"))) { while (j < 6 && (NewSourceCode.find("r" + std::to_string(j)) != std::string::npos || @@ -1890,13 +1892,13 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct if (j < 6) { reg = "r" + std::to_string(j); - SourceCode14 = std::regex_replace(SourceCode14, std::regex(constNum), reg); + SourceCode14 = std::regex_replace(SourceCode14, std::regex(constReg), reg); } } - const std::string tmpLine = " mov " + reg + ", " + constNum + "\n"; + const std::string tmpLine = " mov " + reg + ", " + constReg + "\n"; - NewLine = std::regex_replace(NewLine, std::regex(constNum), reg); + NewLine = std::regex_replace(NewLine, std::regex(constReg), reg); if (ArithmeticCount14 < 8) { NewSourceCode.insert(PhasePosition + TexturePosition, tmpLine); @@ -1925,7 +1927,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct if (SourceCode14.find(ReplaceReg[x].dest, start) == std::string::npos) { - NewLine = std::regex_replace(NewLine, std::regex("([ \\+]+[a-z_\\.0-9]* )r[0-9](.*)"), "$1" + ReplaceReg[x].source + "$2"); + NewLine = std::regex_replace(NewLine, std::regex("([ \\+]+[a-z_\\.0-9]+ )r[0-9](.*)"), "$1" + ReplaceReg[x].source + "$2"); ReplaceReg.erase(ReplaceReg.begin() + x); break; } @@ -1943,21 +1945,22 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct if (SourceCode14.find("t" + texNum, start) == std::string::npos) { - const std::string destRegNum = std::regex_replace(NewLine, std::regex("[ \\+]+[a-z_\\.0-9]* r([0-9]).*"), "$1"); + const std::string destRegNum = std::regex_replace(NewLine, std::regex("[ \\+]+[a-z_\\.0-9]+ r([0-9]).*"), "$1"); - if (!RegisterUsed[strtol(destRegNum.c_str(), nullptr, 10)]) + const unsigned long Num = strtoul(destRegNum.c_str(), nullptr, 10); + if (!RegisterUsed[(Num < 6) ? Num : 6]) { if (NewSourceCode.find("r" + destRegNum) == std::string::npos || SourceCode14.find("r" + destRegNum, start) == std::string::npos) { - NewLine = std::regex_replace(NewLine, std::regex("([ \\+]+[a-z_\\.0-9]* )r[0-9](.*)"), "$1r" + texNum + "$2"); - const std::string tempSourceCode = std::regex_replace(&SourceCode14[start], std::regex("r" + destRegNum), "r" + texNum); + NewLine = std::regex_replace(NewLine, std::regex("([ \\+]+[a-z_\\.0-9]+ )r[0-9](.*)"), "$1r" + texNum + "$2"); + const std::string tempSourceCode = std::regex_replace(SourceCode14.substr(start, SourceCode14.length()), std::regex("r" + destRegNum), "r" + texNum); SourceCode14.resize(start); SourceCode14.append(tempSourceCode); } else { - RegisterUsed[strtol(destRegNum.c_str(), nullptr, 10)] = true; + RegisterUsed[(Num < 6) ? Num : 6] = true; MyStrings tempReplaceReg; tempReplaceReg.dest = "r" + destRegNum; tempReplaceReg.source = "r" + texNum; From 6f730aacc003beda7ef60bdf73ace19dd4ff43e5 Mon Sep 17 00:00:00 2001 From: Elisha Riedlinger Date: Wed, 22 Nov 2017 10:55:06 -0800 Subject: [PATCH 5/8] Update how dest values are checked --- source/d3d8to9_device.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/source/d3d8to9_device.cpp b/source/d3d8to9_device.cpp index 5be4c97..fc98d41 100644 --- a/source/d3d8to9_device.cpp +++ b/source/d3d8to9_device.cpp @@ -1732,31 +1732,24 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct int ArithmeticCount14 = ArithmeticCount; // Fix modifiers for constant values by using any remaining arithmetic places to add an instruction to move the constant value to a temporary register - for (int x = 8 - ArithmeticCount; x > 0; x--) + while (std::regex_search(SourceCode, std::regex("-c[0-9]|c[0-9][\\.wxyz]*_")) && ArithmeticCount < 8) { // Make sure that the dest register is not already being used - const std::string destReg = std::regex_replace(SourceCode, std::regex("[ \\+]+[a-z_\\.0-9]+ (r[0-9]).*(-c[0-9]|c[0-9][\\.wxyz]*_).*"),"$1"); - const std::string sourceReg = std::regex_replace(SourceCode, std::regex("[ \\+]+[a-z_\\.0-9]+ r[0-9](.*)(-c[0-9]|c[0-9][\\.wxyz]*_)(.*)"), "$1$3"); + std::string SingleLine = "\n" + std::regex_replace(SourceCode, std::regex("1?-(c[0-9])[\\._a-z0-9]*|(c[0-9])[\\.wxyz]*_[a-z0-9]*"), "-$1$2") + "\n"; + size_t StartLine = SingleLine.substr(0, SingleLine.find("-c")).rfind("\n") + 1; + SingleLine = SingleLine.substr(StartLine, SingleLine.find("\n", StartLine) - StartLine); + const std::string destReg = std::regex_replace(SingleLine, std::regex("[ \\+]+[a-z_\\.0-9]+ (r[0-9]).*-c[0-9].*"),"$1"); + const std::string sourceReg = std::regex_replace(SingleLine, std::regex("[ \\+]+[a-z_\\.0-9]+ r[0-9][\\._a-z0-9]*, (.*)-c[0-9](.*)"), "$1$2"); if (sourceReg.find(destReg) != std::string::npos) { break; } // Replace one constant modifier using the dest register as a temporary register - const size_t beforeReplace = SourceCode.size(); SourceCode = std::regex_replace(SourceCode, std::regex(" (...)(_[_satxd248]*|) (r[0-9][\\.wxyz]*), (1?-?[crtv][0-9][\\.wxyz_abdis2]*, )?(1?-?[crtv][0-9][\\.wxyz_abdis2]*, )?(1?-?[crtv][0-9][\\.wxyz_abdis2]*, )?((1?-)(c[0-9])([\\.wxyz]*)(_bx2|_bias|_x2|_d[zbwa]|)|(1?-?)(c[0-9])([\\.wxyz]*)(_bx2|_bias|_x2|_d[zbwa]))(?![_\\.wxyz])"), " mov $3, $9$10$13$14 /* added line */\n $1$2 $3, $4$5$8$12$3$11$15 /* changed $9$10$13$14 to $3 */", std::regex_constants::format_first_only); - - // Check if string was replaced - if (SourceCode.size() == beforeReplace) - { - break; - } - else - { - ArithmeticCount++; - } + ArithmeticCount++; } // Check if this should be converted to ps_1_4 From 2950e34b83ee6ccf63e91b51a3143d58b8461647 Mon Sep 17 00:00:00 2001 From: Elisha Riedlinger Date: Wed, 22 Nov 2017 13:17:44 -0800 Subject: [PATCH 6/8] Add ps_1_4 conversion error checking --- source/d3d8to9_device.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/source/d3d8to9_device.cpp b/source/d3d8to9_device.cpp index fc98d41..58e7009 100644 --- a/source/d3d8to9_device.cpp +++ b/source/d3d8to9_device.cpp @@ -1728,7 +1728,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct "sub$2 $3, $4, $7$8 /* changed 'add' to 'sub' removed modifier $6 */"); // Create temporary varables for ps_1_4 - std::string SourceCode14 = SourceCode + "\n"; + std::string SourceCode14 = SourceCode; int ArithmeticCount14 = ArithmeticCount; // Fix modifiers for constant values by using any remaining arithmetic places to add an instruction to move the constant value to a temporary register @@ -1757,6 +1757,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct !std::regex_search(SourceCode, std::regex("tex[bcdmr]")) && // Verify unsupported instructions are not used std::regex_search(SourceCode, std::regex("ps_1_[0-3]"))) // Verify PixelShader is using version 1.0 to 1.3 { + bool ConvertError = false; bool RegisterUsed[7] = { false }; RegisterUsed[6] = true; struct MyStrings @@ -1943,6 +1944,11 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct const unsigned long Num = strtoul(destRegNum.c_str(), nullptr, 10); if (!RegisterUsed[(Num < 6) ? Num : 6]) { + if (std::regex_search(std::regex_replace(NewLine, std::regex("t" + texNum), "r" + texNum), std::regex("t[0-9]"))) + { + ConvertError = true; + break; + } if (NewSourceCode.find("r" + destRegNum) == std::string::npos || SourceCode14.find("r" + destRegNum, start) == std::string::npos) { @@ -1972,18 +1978,21 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct // Add 'phase' instruction NewSourceCode.insert(PhasePosition, " phase\n"); - // Test if ps_1_4 assembles - if (SUCCEEDED(D3DXAssembleShader(NewSourceCode.data(), static_cast(NewSourceCode.size()), nullptr, nullptr, 0, &Assembly, &ErrorBuffer))) - { - SourceCode = NewSourceCode; - } - else + if (!ConvertError) { + // Test if ps_1_4 assembles + if (SUCCEEDED(D3DXAssembleShader(NewSourceCode.data(), static_cast(NewSourceCode.size()), nullptr, nullptr, 0, &Assembly, &ErrorBuffer))) + { + SourceCode = NewSourceCode; + } + else + { #ifndef D3D8TO9NOLOG - LOG << "> Failed to convert shader to ps_1_4" << std::endl; - LOG << "> Dumping translated shader assembly:" << std::endl << std::endl << NewSourceCode << std::endl; - LOG << "> Failed to reassemble shader:" << std::endl << std::endl << static_cast(ErrorBuffer->GetBufferPointer()) << std::endl; + LOG << "> Failed to convert shader to ps_1_4" << std::endl; + LOG << "> Dumping translated shader assembly:" << std::endl << std::endl << NewSourceCode << std::endl; + LOG << "> Failed to reassemble shader:" << std::endl << std::endl << static_cast(ErrorBuffer->GetBufferPointer()) << std::endl; #endif + } } } From 7fda1f66f3f1ec03832f915ca1412cc920459d53 Mon Sep 17 00:00:00 2001 From: Elisha Riedlinger Date: Wed, 22 Nov 2017 17:17:01 -0800 Subject: [PATCH 7/8] Update how Instruction count is computed --- source/d3d8to9_device.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/source/d3d8to9_device.cpp b/source/d3d8to9_device.cpp index 58e7009..70ffd1d 100644 --- a/source/d3d8to9_device.cpp +++ b/source/d3d8to9_device.cpp @@ -1295,7 +1295,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreateVertexShader(const DWORD *pDecl // Get number of arithmetic instructions used const size_t InstructionPosition = SourceCode.find("instruction"); - int InstructionCount = InstructionPosition > 2 && InstructionPosition < SourceCode.size() ? atoi(&SourceCode[InstructionPosition - 4]) : 0; + size_t InstructionCount = InstructionPosition > 2 && InstructionPosition < SourceCode.size() ? strtoul(SourceCode.substr(InstructionPosition - 4, 4).c_str(), nullptr, 10) : 0; for (size_t j = 0; j < 8; j++) { @@ -1711,11 +1711,8 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct // Get number of arithmetic instructions used const size_t ArithmeticPosition = SourceCode.find("arithmetic"); - int ArithmeticCount = ArithmeticPosition > 2 && ArithmeticPosition < SourceCode.size() ? atoi(&SourceCode[ArithmeticPosition - 2]) : 0; - if (ArithmeticCount == 0) - { - ArithmeticCount = 10; // Default to 10 - } + size_t ArithmeticCount = ArithmeticPosition > 2 && ArithmeticPosition < SourceCode.size() ? strtoul(SourceCode.substr(ArithmeticPosition - 2, 2).c_str(), nullptr, 10) : 0; + ArithmeticCount = (ArithmeticCount != 0) ? ArithmeticCount : 10; // Default to 10 // Remove lines when " // ps.1.1" string is found and the next line does not start with a space SourceCode = std::regex_replace(SourceCode, @@ -1735,11 +1732,11 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct while (std::regex_search(SourceCode, std::regex("-c[0-9]|c[0-9][\\.wxyz]*_")) && ArithmeticCount < 8) { // Make sure that the dest register is not already being used - std::string SingleLine = "\n" + std::regex_replace(SourceCode, std::regex("1?-(c[0-9])[\\._a-z0-9]*|(c[0-9])[\\.wxyz]*_[a-z0-9]*"), "-$1$2") + "\n"; - size_t StartLine = SingleLine.substr(0, SingleLine.find("-c")).rfind("\n") + 1; - SingleLine = SingleLine.substr(StartLine, SingleLine.find("\n", StartLine) - StartLine); - const std::string destReg = std::regex_replace(SingleLine, std::regex("[ \\+]+[a-z_\\.0-9]+ (r[0-9]).*-c[0-9].*"),"$1"); - const std::string sourceReg = std::regex_replace(SingleLine, std::regex("[ \\+]+[a-z_\\.0-9]+ r[0-9][\\._a-z0-9]*, (.*)-c[0-9](.*)"), "$1$2"); + std::string tmpLine = "\n" + std::regex_replace(SourceCode, std::regex("1?-(c[0-9])[\\._a-z0-9]*|(c[0-9])[\\.wxyz]*_[a-z0-9]*"), "-$1$2") + "\n"; + size_t start = tmpLine.substr(0, tmpLine.find("-c")).rfind("\n") + 1; + tmpLine = tmpLine.substr(start, tmpLine.find("\n", start) - start); + const std::string destReg = std::regex_replace(tmpLine, std::regex("[ \\+]+[a-z_\\.0-9]+ (r[0-9]).*-c[0-9].*"),"$1"); + const std::string sourceReg = std::regex_replace(tmpLine, std::regex("[ \\+]+[a-z_\\.0-9]+ r[0-9][\\._a-z0-9]*, (.*)-c[0-9](.*)"), "$1$2"); if (sourceReg.find(destReg) != std::string::npos) { break; From eb8374fe4e267bbee5f936dcbea4014a458873c8 Mon Sep 17 00:00:00 2001 From: Elisha Riedlinger Date: Thu, 23 Nov 2017 09:57:49 -0800 Subject: [PATCH 8/8] Add remarks for p_1_4 conversion --- source/d3d8to9_device.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/source/d3d8to9_device.cpp b/source/d3d8to9_device.cpp index 70ffd1d..4f5d45b 100644 --- a/source/d3d8to9_device.cpp +++ b/source/d3d8to9_device.cpp @@ -1829,7 +1829,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct // Do nothing } - // Check for 'def' and add before 'phase' statment + // Check for 'def' and add before 'phase' statement else if (NewLine.find("def c") != std::string::npos) { PhaseMarkerSet = true; @@ -1844,6 +1844,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct const std::string regNum = std::regex_replace(NewLine, std::regex(".*tex t([0-9]).*"), "$1"); const std::string tmpLine = " texld r" + regNum + ", t" + regNum + "\n"; + // Mark as a texture register and add 'texld' statement before or after the 'phase' statement const unsigned long Num = strtoul(regNum.c_str(), nullptr, 10); RegisterUsed[(Num < 6) ? Num : 6] = true; NewSourceCode.insert(PhasePosition, tmpLine); @@ -1872,14 +1873,17 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct { const std::string constReg = std::regex_replace(NewLine, std::regex(".*-(c[0-9]).*|.*(c[0-9])[\\.wxyz]*_.*"), "$1$2"); + // Check if this constant has modifiers in more than one line if (std::regex_search(SourceCode14.substr(LinePosition + NewLine.length(), SourceCode14.length()), std::regex("-" + constReg + "|" + constReg + "[\\.wxyz]*_"))) { + // Find an unused register while (j < 6 && (NewSourceCode.find("r" + std::to_string(j)) != std::string::npos || SourceCode14.find("r" + std::to_string(j)) != std::string::npos)) { j++; } + // Replace all constants with the unused register if (j < 6) { reg = "r" + std::to_string(j); @@ -1889,6 +1893,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct const std::string tmpLine = " mov " + reg + ", " + constReg + "\n"; + // Update the constant in this line and add 'mov' statement before or after the 'phase' statement NewLine = std::regex_replace(NewLine, std::regex(constReg), reg); if (ArithmeticCount14 < 8) { @@ -1906,18 +1911,23 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct } } - // Update registers + // Update register from vector once it is used for the last time if (ReplaceReg.size() > 0) { for (size_t x = 0; x < ReplaceReg.size(); x++) { + // Check if register is used in this line if (NewLine.find(ReplaceReg[x].dest) != std::string::npos) { + // Get position of all lines after this line size_t start = LinePosition + NewLine.length(); + // Move position to next line if the first line is a co-issed command start = (SourceCode14.substr(start, 4).find("+") == std::string::npos) ? start : SourceCode14.find("\n", start + 1); + // Check if register is used in the code after this position if (SourceCode14.find(ReplaceReg[x].dest, start) == std::string::npos) { + // Update dest register using source register from the vector NewLine = std::regex_replace(NewLine, std::regex("([ \\+]+[a-z_\\.0-9]+ )r[0-9](.*)"), "$1" + ReplaceReg[x].source + "$2"); ReplaceReg.erase(ReplaceReg.begin() + x); break; @@ -1931,31 +1941,40 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct { const std::string texNum = std::regex_replace(NewLine, std::regex(".*t([0-9]).*"), "$1"); + // Get position of all lines after this line size_t start = LinePosition + NewLine.length(); + // Move position to next line if the first line is a co-issed command start = (SourceCode14.substr(start, 4).find("+") == std::string::npos) ? start : SourceCode14.find("\n", start + 1); + // Check if texture is used in the code after this position if (SourceCode14.find("t" + texNum, start) == std::string::npos) { const std::string destRegNum = std::regex_replace(NewLine, std::regex("[ \\+]+[a-z_\\.0-9]+ r([0-9]).*"), "$1"); + // Check if destination register is already being used by a texture register const unsigned long Num = strtoul(destRegNum.c_str(), nullptr, 10); if (!RegisterUsed[(Num < 6) ? Num : 6]) { + // Check if line is using more than one texture and error out if (std::regex_search(std::regex_replace(NewLine, std::regex("t" + texNum), "r" + texNum), std::regex("t[0-9]"))) { ConvertError = true; break; } + // Check if this is the first or last time the register is used if (NewSourceCode.find("r" + destRegNum) == std::string::npos || SourceCode14.find("r" + destRegNum, start) == std::string::npos) { + // Update dest register using texture register NewLine = std::regex_replace(NewLine, std::regex("([ \\+]+[a-z_\\.0-9]+ )r[0-9](.*)"), "$1r" + texNum + "$2"); + // Update code replacing all regsiters after the marked position with the texture register const std::string tempSourceCode = std::regex_replace(SourceCode14.substr(start, SourceCode14.length()), std::regex("r" + destRegNum), "r" + texNum); SourceCode14.resize(start); SourceCode14.append(tempSourceCode); } else { + // If register is still being used then add registers to vector to be replaced later RegisterUsed[(Num < 6) ? Num : 6] = true; MyStrings tempReplaceReg; tempReplaceReg.dest = "r" + destRegNum; @@ -1975,6 +1994,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct // Add 'phase' instruction NewSourceCode.insert(PhasePosition, " phase\n"); + // If no errors were encountered then check if code assembles if (!ConvertError) { // Test if ps_1_4 assembles