|
| 1 | +"""Execute the production module hook in both build modes, and Vulkan menu gates. |
| 2 | +
|
| 3 | +Win32 calls and UI state are stand-ins: this does not establish loader/GPU safety. |
| 4 | +""" |
| 5 | +import argparse |
| 6 | +from pathlib import Path |
| 7 | +import re |
| 8 | +import subprocess |
| 9 | +import tempfile |
| 10 | +from test_shutdown_routing import function |
| 11 | + |
| 12 | +ROOT = Path(__file__).resolve().parents[1] |
| 13 | + |
| 14 | + |
| 15 | +def main(): |
| 16 | + parser = argparse.ArgumentParser() |
| 17 | + parser.add_argument('--compiler', required=True) |
| 18 | + parser.add_argument('--driver') |
| 19 | + args = parser.parse_args() |
| 20 | + hook = function((ROOT/'OptiScaler/hooks/Kernel_Hooks.cpp').read_text(encoding='utf-8'), |
| 21 | + 'BOOL WINAPI KernelHooks::hk_K32_GetModuleHandleExA(') |
| 22 | + menu = (ROOT/'OptiScaler/menu/menu_common.cpp').read_text(encoding='utf-8') |
| 23 | + gates = [] |
| 24 | + for key in ['Ffx', 'Combo']: |
| 25 | + gate = re.search(r'nvngxOptions\[fgNvngx'+key+r'Index\]\.set_disabled\((state.swapchainApi[^,]+),', menu) |
| 26 | + assert gate, key |
| 27 | + gates.append(gate[1]) |
| 28 | + render = function(menu, 'void MenuCommon::RenderFakenvapiSettings(') |
| 29 | + start = render.index(' const bool showLatencyFlex') |
| 30 | + end = render.index(' ImGui::SeparatorText(', start) |
| 31 | + prefix = render[start:end] |
| 32 | + assert 'if (showForceXell)' in render and render.index('if (showForceXell)') < render.index('bool forceXell') |
| 33 | + # No arbitrary export patching in the non-input initialization path. |
| 34 | + xell = (ROOT/'OptiScaler/proxies/XeLL_Proxy.h').read_text(encoding='utf-8') |
| 35 | + assert 'RedirectAllExports' not in function(xell, 'static bool InitXeLLProper(') |
| 36 | + cpp = r''' |
| 37 | +#include <cstring> |
| 38 | +#include <cstdlib> |
| 39 | +#include <iostream> |
| 40 | +using BOOL=int;using DWORD=unsigned;using HMODULE=void*;using LPCSTR=const char*; |
| 41 | +#define WINAPI |
| 42 | +constexpr unsigned GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS=4; |
| 43 | +int dllStorage,selectedStorage,otherStorage; |
| 44 | +HMODULE dllModule=&dllStorage; |
| 45 | +struct XeLLProxy {static inline HMODULE selected=nullptr;static HMODULE Module(){return selected;}}; |
| 46 | +int calls=0,refs=0;DWORD lastFlags;LPCSTR lastName;HMODULE* lastOut;bool succeed=true; |
| 47 | +BOOL original(DWORD flags,LPCSTR name,HMODULE* out){ |
| 48 | + ++calls;lastFlags=flags;lastName=name;lastOut=out; |
| 49 | + if(!out||!succeed)return 0; |
| 50 | + *out=flags==4?const_cast<char*>(name):static_cast<void*>(&otherStorage);++refs;return 1; |
| 51 | +} |
| 52 | +struct KernelHooks {static inline auto o_K32_GetModuleHandleExA=&original; |
| 53 | + static BOOL hk_K32_GetModuleHandleExA(DWORD,LPCSTR,HMODULE*);}; |
| 54 | +enum class API {DX11,DX12,Vulkan};enum class FGOutput {NoFG,XeFG}; |
| 55 | +struct State {API swapchainApi;FGOutput activeFgOutput;bool reflexLimitsFps;}; |
| 56 | +namespace fakenvapi {bool main=false;bool isUsingAsMainNvapi(){return main;}} |
| 57 | +int section=0;bool visibleForce=false; |
| 58 | +void visibility(State state){ |
| 59 | +''' + prefix + '++section;visibleForce=showForceXell;}\n' + hook |
| 60 | + for i, gate in enumerate(gates): |
| 61 | + cpp += f'bool gate{i}(State state){{return {gate};}}\n' |
| 62 | + cpp += r''' |
| 63 | +int main(){int checks=0;auto check=[&](bool v){++checks;if(!v){std::cerr<<"FAIL "<<checks;std::exit(2);}}; |
| 64 | +const char* name="libxell.dll";HMODULE out=nullptr; |
| 65 | +for(bool selected:{false,true}){ |
| 66 | + XeLLProxy::selected=selected?&selectedStorage:nullptr; |
| 67 | + calls=refs=0;succeed=true;out=nullptr; |
| 68 | + check(KernelHooks::hk_K32_GetModuleHandleExA(0,name,&out)==1); |
| 69 | +#ifdef LOW_LATENCY_INPUTS |
| 70 | + check(out==dllModule&&calls==0); |
| 71 | +#else |
| 72 | + check(calls==1&&refs==1);check(out==(selected?&selectedStorage:&otherStorage)); |
| 73 | + check(lastFlags==(selected?4u:0u));check(lastName==(selected?reinterpret_cast<LPCSTR>(&selectedStorage):name)); |
| 74 | + succeed=false;out=nullptr;calls=refs=0; |
| 75 | + check(!KernelHooks::hk_K32_GetModuleHandleExA(0,name,&out));check(calls==1&&refs==0&&out==nullptr); |
| 76 | +#endif |
| 77 | +} |
| 78 | +// Unrelated names, invalid output/name, and flagged/address requests forward untouched. |
| 79 | +succeed=true; |
| 80 | +for(DWORD flags:{1u,2u,4u,6u}){calls=0;KernelHooks::hk_K32_GetModuleHandleExA(flags,name,&out); |
| 81 | + check(calls==1&&lastFlags==flags&&lastName==name&&lastOut==&out);} |
| 82 | +for(auto other:{"other.dll","C:\\game\\libxell.dll"}){calls=0; |
| 83 | + KernelHooks::hk_K32_GetModuleHandleExA(0,other,&out);check(calls==1&&lastName==other);} |
| 84 | +calls=0;KernelHooks::hk_K32_GetModuleHandleExA(0,nullptr,&out);check(calls==1&&lastName==nullptr); |
| 85 | +calls=0;KernelHooks::hk_K32_GetModuleHandleExA(0,name,nullptr);check(calls==1&&lastOut==nullptr); |
| 86 | +for(API api:{API::DX11,API::DX12,API::Vulkan})for(bool main:{false,true}) |
| 87 | +for(FGOutput fg:{FGOutput::NoFG,FGOutput::XeFG})for(bool reflex:{false,true}){ |
| 88 | + State state{api,fg,reflex};fakenvapi::main=main;section=0;visibleForce=false;visibility(state); |
| 89 | + check(section==(api!=API::Vulkan||main||(fg==FGOutput::XeFG&&reflex))); |
| 90 | + check(visibleForce==(api!=API::Vulkan)); |
| 91 | + check(gate0(state)==(api==API::Vulkan));check(gate1(state)==(api==API::Vulkan)); |
| 92 | +} |
| 93 | +std::cout<<"PASS: "<<checks<<" XeLL/menu checks\n";} |
| 94 | +''' |
| 95 | + with tempfile.TemporaryDirectory(prefix='aurora-xell-routing-') as directory: |
| 96 | + path = Path(directory) |
| 97 | + test = path/'test.cpp' |
| 98 | + test.write_text(cpp, encoding='utf-8') |
| 99 | + for low_latency in [False, True]: |
| 100 | + exe = path/('input.exe' if low_latency else 'normal.exe') |
| 101 | + command = [args.compiler] + ([args.driver] if args.driver else []) |
| 102 | + if Path(args.compiler).stem.lower() == 'cl': |
| 103 | + command += ['/nologo','/EHsc','/std:c++20',str(test),'/Fe:'+str(exe)] |
| 104 | + if low_latency: command += ['/DLOW_LATENCY_INPUTS'] |
| 105 | + else: |
| 106 | + command += ['-std=c++20',str(test),'-o',str(exe)] |
| 107 | + if low_latency: command += ['-DLOW_LATENCY_INPUTS'] |
| 108 | + subprocess.run(command,cwd=path,check=True) |
| 109 | + subprocess.run([str(exe)],cwd=path,check=True,timeout=25) |
| 110 | + |
| 111 | + |
| 112 | +if __name__ == '__main__': |
| 113 | + main() |
0 commit comments