├── .clang-format ├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── standard-issue.md └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── BUILD.md ├── CODE_GUIDELINES.md ├── LICENSE ├── README.md ├── ThirdParty_LICENSES ├── clang_format.py ├── fonts ├── NotoSans-Regular.ttf ├── NotoSansJP-Regular.otf ├── NotoSansKR-Regular.otf ├── NotoSansMono-Regular.ttf ├── NotoSansSC-Regular.otf ├── NotoSansTC-Regular.otf ├── NotoSansThai-Regular.ttf └── materialdesignicons.ttf ├── scripts ├── IconGlyphs │ └── icons.lua └── json │ ├── LICENSE │ ├── README.md │ └── json.lua ├── src ├── CET.cpp ├── CET.h ├── Image.cpp ├── Image.h ├── Options.cpp ├── Options.h ├── Paths.cpp ├── Paths.h ├── PersistentState.cpp ├── PersistentState.h ├── Utils.cpp ├── Utils.h ├── VKBindings.cpp ├── VKBindings.h ├── common │ ├── D3D12Downlevel.h │ ├── FontMaterialDesignIcons.h │ ├── ImGuiNotify.h │ ├── Logging.h │ ├── Meta.h │ └── ScopeGuard.h ├── d3d12 │ ├── D3D12.cpp │ ├── D3D12.h │ ├── D3D12_Functions.cpp │ └── D3D12_Hooks.cpp ├── dllmain.cpp ├── imgui_impl │ ├── dx12.cpp │ ├── dx12.h │ ├── imgui_user_config.cpp │ ├── imgui_user_config.h │ ├── win32.cpp │ └── win32.h ├── lsqlite3 │ ├── HISTORY │ ├── README │ ├── lsqlite3.cpp │ └── lsqlite3.h ├── overlay │ ├── Overlay.cpp │ ├── Overlay.h │ └── widgets │ │ ├── Bindings.cpp │ │ ├── Bindings.h │ │ ├── Console.cpp │ │ ├── Console.h │ │ ├── GameLog.cpp │ │ ├── GameLog.h │ │ ├── ImGuiDebug.cpp │ │ ├── ImGuiDebug.h │ │ ├── LogWindow.cpp │ │ ├── LogWindow.h │ │ ├── Settings.cpp │ │ ├── Settings.h │ │ ├── TweakDBEditor.cpp │ │ ├── TweakDBEditor.h │ │ ├── Widget.cpp │ │ └── Widget.h ├── patches │ ├── DisableBoundaries.cpp │ ├── DisableVignette.cpp │ └── OptionsPatch.cpp ├── reverse │ ├── Addresses.h │ ├── Array.h │ ├── BasicTypes.cpp │ ├── BasicTypes.h │ ├── ClassReference.cpp │ ├── ClassReference.h │ ├── ClassStatic.cpp │ ├── ClassStatic.h │ ├── Converter.cpp │ ├── Converter.h │ ├── Enum.cpp │ ├── Enum.h │ ├── EnumStatic.cpp │ ├── EnumStatic.h │ ├── LuaRED.h │ ├── NativeProxy.cpp │ ├── NativeProxy.h │ ├── RTTIExtender.cpp │ ├── RTTIExtender.h │ ├── RTTIHelper.cpp │ ├── RTTIHelper.h │ ├── RTTILocator.cpp │ ├── RTTILocator.h │ ├── RTTIMapper.cpp │ ├── RTTIMapper.h │ ├── Relocation.h │ ├── RenderContext.cpp │ ├── RenderContext.h │ ├── ResourceAsyncReference.cpp │ ├── ResourceAsyncReference.h │ ├── SingletonReference.cpp │ ├── SingletonReference.h │ ├── StrongReference.cpp │ ├── StrongReference.h │ ├── TweakDB │ │ ├── FlatPool.cpp │ │ ├── FlatPool.h │ │ ├── ResourcesList.cpp │ │ ├── ResourcesList.h │ │ ├── TweakDB.cpp │ │ └── TweakDB.h │ ├── Type.cpp │ ├── Type.h │ ├── WeakReference.cpp │ └── WeakReference.h ├── scripting │ ├── FunctionOverride.cpp │ ├── FunctionOverride.h │ ├── GameDump.cpp │ ├── GameDump.h │ ├── GameHooks.cpp │ ├── GameHooks.h │ ├── GameOptions.cpp │ ├── GameOptions.h │ ├── LuaSandbox.cpp │ ├── LuaSandbox.h │ ├── LuaVM.cpp │ ├── LuaVM.h │ ├── LuaVM_Hooks.cpp │ ├── Sandbox.cpp │ ├── Sandbox.h │ ├── ScriptContext.cpp │ ├── ScriptContext.h │ ├── ScriptStore.cpp │ ├── ScriptStore.h │ ├── Scripting.cpp │ ├── Scripting.h │ ├── Texture.cpp │ └── Texture.h ├── sol_imgui │ ├── LICENSE │ ├── README.md │ └── sol_imgui.h ├── stdafx.h ├── window │ ├── window.cpp │ └── window.h └── xmake │ ├── CETVersion.h.in │ └── Resource.rc.in ├── vendor └── asiloader │ ├── global.ini │ └── version.dll └── xmake.lua /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: AlwaysBreak 5 | AlignEscapedNewlines: Left 6 | AlignOperands: true 7 | AllowShortCaseLabelsOnASingleLine: true 8 | AllowShortFunctionsOnASingleLine: InlineOnly 9 | AllowShortLambdasOnASingleLine: Inline 10 | BraceWrapping: 11 | AfterCaseLabel: true 12 | IndentBraces: true 13 | BreakBeforeBraces: Allman 14 | BreakConstructorInitializers: BeforeComma 15 | ColumnLimit: 180 16 | IndentWidth: 4 17 | PenaltyBreakFirstLessLess: 180 18 | PointerAlignment: Left 19 | SortIncludes: Never 20 | SpaceBeforeParensOptions: 21 | AfterControlStatements: false -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{cpp,hpp}] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_size = 4 8 | indent_style = space 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.h text 3 | *.c text 4 | *.hpp text 5 | *.cpp text 6 | 7 | *.bat text eol=crlf 8 | *.sln text eol=crlf 9 | *.vcxproj text eol=crlf 10 | *.vcxproj.filters text eol=crlf 11 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: tiltedphoques 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/standard-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Standard issue 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | # README 11 | 12 | Github is NOT here for support and general questions, it's designed for developers to discuss development and code. If your issue is unrelated to anything technical, do not make an issue, you will be blocked and your issue closed without an answer. 13 | 14 | # Bug 15 | 16 | Operating system: 17 | Game version: 18 | CET version: 19 | GPU: 20 | 21 | ## Description 22 | 23 | Write here a description of the issue. 24 | 25 | ## Expected behaviour 26 | 27 | Write here what you expect to happen instead. 28 | 29 | ## Reproduction 30 | 31 | Write here step by step how to reproduce the issue if applicable. 32 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | push: 6 | 7 | jobs: 8 | build: 9 | strategy: 10 | matrix: 11 | os: [windows-latest] 12 | arch: [x64] 13 | mode: [release] 14 | 15 | runs-on: ${{ matrix.os }} 16 | if: "!contains(github.event.head_commit.message, 'ci skip')" 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - run: | 21 | git fetch --prune --unshallow --tags 22 | 23 | - name: Checkout submodules 24 | run: | 25 | git submodule sync --recursive 26 | git submodule update --init --force --recursive --depth=1 27 | 28 | # Force xmake to a specific folder (for cache) 29 | #- name: Set xmake env 30 | # run: echo "XMAKE_GLOBALDIR=$(pwd)/xmake-global" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 31 | 32 | # Install xmake 33 | - name: Setup xmake 34 | uses: xmake-io/github-action-setup-xmake@v1 35 | 36 | # Update xmake repository (in order to have the file that will be cached) 37 | - name: Update xmake repository 38 | run: xmake.exe repo --update 39 | 40 | # Setup compilation mode and install project dependencies 41 | - name: Configure xmake and install dependencies 42 | run: xmake.exe config --arch=${{ matrix.arch }} --mode=${{ matrix.mode }} --yes 43 | 44 | # Install the result files 45 | - name: Build and create package 46 | run: xmake.exe package 47 | 48 | # Upload artifacts 49 | - uses: actions/upload-artifact@v4 50 | with: 51 | name: ${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.mode }} 52 | path: | 53 | package/** 54 | !package/**/*.pdb 55 | 56 | - uses: actions/upload-artifact@v4 57 | with: 58 | name: ${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.mode }}-pdb 59 | path: package/**/*.pdb -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tmp/ 2 | .vs/ 3 | .*/ 4 | *.opensdf 5 | *.sdf 6 | *.suo 7 | *.user 8 | x64/ 9 | Win32/ 10 | vsxmake2019/* 11 | vs2019/* 12 | build/* 13 | .xmake/* 14 | *.asi 15 | src/CETVersion.h 16 | package/* 17 | vs2022/* 18 | vsxmake*/ 19 | *.pyc 20 | src/config/* -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/RED4ext.SDK"] 2 | path = vendor/RED4ext.SDK 3 | url = https://github.com/WopsS/RED4ext.SDK 4 | [submodule "vendor/luasocket"] 5 | path = vendor/luasocket 6 | url = https://github.com/diegonehab/luasocket.git 7 | -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | # Build 2 | 3 | ## What's this? 4 | Building instructions for the plugin. 5 | 6 | ### Requirements 7 | - [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/) 8 | - (You can use VS2022, just make sure you're on at least MSVC 14.34) 9 | - [xmake](https://github.com/xmake-io/xmake/releases) 10 | - [git](https://git-scm.com/downloads) 11 | 12 | ### Build 13 | 1. Navigate to the repository using a command prompt. 14 | 2. Check out the correct branch/tag if you're not working against master 15 | 3. Run `git submodule update --init` to pull in vendored dependencies 16 | 4. Run `xmake -y` (add `-v` for verbose output) 17 | 18 | 19 | ### Visual Studio 20 | 21 | If you want visual studio projects execute `xmake project -k vsxmake` and you will find the sln in the newly created `vsxmake` folder. 22 | 23 | 24 | ### Additional configuration / troubleshooting 25 | 26 | - You can specify the VS version with `xmake f --vs=2019|2022` in case it's not detected right 27 | - Use Visual Studio Installer to install any missing components in the toolset if needed 28 | 29 | ### Install 30 | 31 | 1. Configure the install path, run: `xmake f --installpath=\bin\x64\plugins` (not the base dir!) 32 | 2. `xmake install` (or `xmake i` for short.) 33 | 34 | 35 | #### ASI Loader 36 | 37 | 1. Download Ultimate-ASI-Loader_x64.zip from [ASI Loader](https://github.com/ThirteenAG/Ultimate-ASI-Loader/releases/latest) 38 | 2. Put the dinput8.dll in `\bin\x64\` 39 | 3. Rename `dinput8.dll` as `version.dll`, you should have `\bin\x64\version.dll` 40 | 4. Create a `global.ini` file in `\bin\x64\` 41 | 5. Paste the following lines in `global.ini`, save and you are all set! 42 | 43 | ``` 44 | [GlobalSets] 45 | LoadPlugins=1 46 | LoadFromScriptsOnly=1 47 | DontLoadFromDllMain=0 48 | FindModule=0 49 | UseD3D8to9=0 50 | DisableCrashDumps=0 51 | Direct3D8DisableMaximizedWindowedModeShim=0 52 | ``` 53 | 54 | -------------------------------------------------------------------------------- /CODE_GUIDELINES.md: -------------------------------------------------------------------------------- 1 | # Code Guidelines 2 | 3 | ## Language 4 | 5 | We are using C++20, any C++20 feature supported by vs2019 is allowed. 6 | 7 | Please try to use templates responsibly, we don't want compilation times to explode and to deal with bloated binaries. 8 | 9 | Try to follow SRP as much as possible, a huge class containing tons of functionnalities is not better than many small components, it's easier to re-use them and to extend. 10 | 11 | ## Naming 12 | 13 | ### Variables 14 | 15 | The first letter is lower case, other words must start with an upper case : ``someVariableName``. 16 | 17 | Function arguments must be prefixed with an ``a`` for 'argument' : ``aFunctionArgument``. 18 | 19 | Const variables must be prefixed with a ``c`` for 'const' : ``const int cSomeInt;`` 20 | 21 | Pointers must be prefixed with a ``p`` for 'pointer' : ``int* pSomePointer``. 22 | 23 | The rules above must be used together for example ``void SomeFunc(const int* acpSomeArgument)``. 24 | 25 | Static variables must be prefixed with ``s_`` : ``static int s_someInt;``. 26 | 27 | Global variables must be prefixed with ``g_`` : ``extern int g_someGlobalInt;``. 28 | 29 | ### Classes 30 | 31 | Class names must start with an upper case : ``class SomeClass``. 32 | 33 | Class attributes must be prefixed with ``m_`` and must use the same rules as variables : ``int* m_pSomeMemberPointer;``. 34 | 35 | ### Functions 36 | 37 | All functions must start with an upper case : ``void SomeFunc();``. 38 | 39 | ## Generalities 40 | 41 | Names must be self explanatory, ``size_t a;`` is not acceptable, ``size_t incomingPacketCount;`` is good. 42 | 43 | ``auto`` is allowed when dealing with long names, it is not accepted for primitive types as we don't want the compiler to give us a signed int when we are using it as unsigned. 44 | 45 | Don't use java style blocks, a ``{`` needs to be on a new line. 46 | 47 | Don't use exceptions, don't use STL code that can throw, use the nothrow version if available or the unsafe version. 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 yamashi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cyber Engine Tweaks 2 | 3 | [![Patreon](https://img.shields.io/badge/Patreon-donate-purple.svg)](https://www.patreon.com/tiltedphoques) [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/) [![Discord](https://img.shields.io/discord/717692382849663036.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/Epkq79kd96) 4 | 5 | * Bitcoin: bc1q0neujk5e8v8sc3934ajn8z8zav7hl6557fjj54 6 | * Bitcoin Cash: qps5ze9p8fxmu4rszyxwy3g0ctlmrhvc3uqq0fzsnl 7 | * Dogecoin: DMoReR33D87D6rYeUkyQb2BsEHJTqfBFva 8 | * Ethereum: 0x7Cd23BE1C507Da85ABF0B05c7A3C03e6d3d0233B 9 | 10 | ## What's this? 11 | 12 | **Cyber Engine Tweaks** is a framework giving modders a way to script mods using [Lua](https://www.lua.org/) with access to all the internal scripting features. 13 | It also comes with a [Dear ImGui](https://github.com/ocornut/imgui/tree/v1.82) to provide GUI for different mods you are using, along with console and TweakDB editor for more advanced usage. 14 | It also adds some patches for quality of life, all of which can be enabled/disabled through the settings menu or config files (requires game restart to apply). 15 | 16 | Cyber Engine Tweaks tracks the current release of Cyberpunk 2077 closely. 17 | The current release of CET is available from the releases page: 18 | [![All Releases](https://img.shields.io/github/downloads/maximegmd/CyberEngineTweaks/total)](https://github.com/maximegmd/CyberEngineTweaks/releases). 19 | 20 | ### Current patches 21 | 22 | | Patch | Description | 23 | | :------------- | :------------------------------ | 24 | | AMD SMT | For AMD CPUs that did not get a performance boost after CDPR's patch | 25 | | Remove pedestrians and traffic | Removes most of the pedestrians and traffic | 26 | | Disable Async Compute | Disables async compute, this can give a boost on older GPUs (Nvidia 10xx series for example)| 27 | | Disable Temporal Antialiasing | Disables antialiasing, not recommended but you do what you want! | 28 | | Skip start menu | Skips the menu asking you to press space bar to continue (Breaching...) | 29 | | Suppress Intro Movies | Disables logos played at the beginning | 30 | | Disable Vignette | Disables vignetting along screen borders | 31 | | Disable Boundary Teleport | Allows players to access out-of-bounds locations | 32 | | Disable Windows 7 VSync | Disables VSync on Windows 7 to bypass the 60 FPS limit | 33 | 34 | ### Current mod development options 35 | | Development | Description | 36 | | :------------- | :------------------------------ | 37 | | Draw ImGui Diagnostics Window | Toggles drawing of internal ImGui diagnostics window to show what is going on behind the scenes (good for mod debugging) | 38 | | Remove Dead Bindings | Removes bindings for mods that were not loaded | 39 | | Enable ImGui Assertions | Enables all ImGui assertions (use this option to check mods for errors before shipping!) | 40 | | Debug Menu | Enables the debug menus in game | 41 | | Dump Game Options | Dumps all game options into main log file | 42 | 43 | ## Usage and configuration 44 | 45 | You first need to install [RED4ext](https://github.com/WopsS/RED4ext). 46 | 47 | [Read the wiki](https://wiki.redmodding.org/cyber-engine-tweaks/) 48 | 49 | [Official mod examples](https://github.com/WolvenKit/cet-examples) 50 | 51 | [Usage with Proton](https://wiki.redmodding.org/cyber-engine-tweaks/getting-started/installing/untitled) 52 | 53 | ## Contributing 54 | 55 | If you wish to contribute to the main repo, try to follow the coding style in the code, otherwise not much to say, don't use code that is not yours unless the license is compatible with MIT. 56 | 57 | As for the wiki, please ask on discord for write permissions. 58 | -------------------------------------------------------------------------------- /clang_format.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import subprocess 4 | 5 | def format_cpp_file(file_path, clang_format_path): 6 | # Run clang-format on the file to format it 7 | subprocess.run(["clang-format", "-i", file_path, "-style=file:" + clang_format_path, "-fallback-style=none"]) 8 | 9 | def recursive_directory_iteration(directory, clang_format_path): 10 | # Iterate over all of the files in the directory 11 | for filename in os.listdir(directory): 12 | # Get the full path of the file 13 | file_path = os.path.join(directory, filename) 14 | 15 | # If the file is a directory, recursively iterate over it 16 | if os.path.isdir(file_path): 17 | recursive_directory_iteration(file_path, clang_format_path) 18 | # If the file is a cpp file, format it 19 | elif file_path.endswith(".cpp") or file_path.endswith(".h") or file_path.endswith(".hpp"): 20 | format_cpp_file(file_path, clang_format_path) 21 | 22 | print("Formatting C++ files...") 23 | 24 | path = os.getcwd() + "\\.clang-format" 25 | if not os.path.exists(path): 26 | print("Could not find clang format config at " + path) 27 | exit() 28 | 29 | print("Found clang format config at " + path) 30 | 31 | start_time = time.time() 32 | # Start the recursive iteration from the current directory 33 | recursive_directory_iteration(os.getcwd() + "/src", path) 34 | 35 | print("Done formatting C++ files after " + str(time.time() - start_time) + " seconds") -------------------------------------------------------------------------------- /fonts/NotoSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximegmd/CyberEngineTweaks/12873cefce4be2aada4722bcd8d6dd9f2026c64e/fonts/NotoSans-Regular.ttf -------------------------------------------------------------------------------- /fonts/NotoSansJP-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximegmd/CyberEngineTweaks/12873cefce4be2aada4722bcd8d6dd9f2026c64e/fonts/NotoSansJP-Regular.otf -------------------------------------------------------------------------------- /fonts/NotoSansKR-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximegmd/CyberEngineTweaks/12873cefce4be2aada4722bcd8d6dd9f2026c64e/fonts/NotoSansKR-Regular.otf -------------------------------------------------------------------------------- /fonts/NotoSansMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximegmd/CyberEngineTweaks/12873cefce4be2aada4722bcd8d6dd9f2026c64e/fonts/NotoSansMono-Regular.ttf -------------------------------------------------------------------------------- /fonts/NotoSansSC-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximegmd/CyberEngineTweaks/12873cefce4be2aada4722bcd8d6dd9f2026c64e/fonts/NotoSansSC-Regular.otf -------------------------------------------------------------------------------- /fonts/NotoSansTC-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximegmd/CyberEngineTweaks/12873cefce4be2aada4722bcd8d6dd9f2026c64e/fonts/NotoSansTC-Regular.otf -------------------------------------------------------------------------------- /fonts/NotoSansThai-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximegmd/CyberEngineTweaks/12873cefce4be2aada4722bcd8d6dd9f2026c64e/fonts/NotoSansThai-Regular.ttf -------------------------------------------------------------------------------- /fonts/materialdesignicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximegmd/CyberEngineTweaks/12873cefce4be2aada4722bcd8d6dd9f2026c64e/fonts/materialdesignicons.ttf -------------------------------------------------------------------------------- /scripts/json/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 rxi 2 | 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /scripts/json/README.md: -------------------------------------------------------------------------------- 1 | # ![json.lua](https://cloud.githubusercontent.com/assets/3920290/9281532/99e5e0cc-42bd-11e5-8fce-eaff2f7fc681.png) 2 | A lightweight JSON library for Lua 3 | 4 | 5 | ## Features 6 | * Implemented in pure Lua: works with 5.1, 5.2, 5.3 and JIT 7 | * Fast: generally outperforms other pure Lua JSON implementations 8 | ([benchmark scripts](bench/)) 9 | * Tiny: around 280sloc, 9kb 10 | * Proper error messages, *eg:* `expected '}' or ',' at line 203 col 30` 11 | 12 | 13 | ## Usage 14 | The [json.lua](json.lua?raw=1) file should be dropped into an existing project 15 | and required by it: 16 | ```lua 17 | json = require "json" 18 | ``` 19 | The library provides the following functions: 20 | 21 | #### json.encode(value) 22 | Returns a string representing `value` encoded in JSON. 23 | ```lua 24 | json.encode({ 1, 2, 3, { x = 10 } }) -- Returns '[1,2,3,{"x":10}]' 25 | ``` 26 | 27 | #### json.decode(str) 28 | Returns a value representing the decoded JSON string. 29 | ```lua 30 | json.decode('[1,2,3,{"x":10}]') -- Returns { 1, 2, 3, { x = 10 } } 31 | ``` 32 | 33 | ## Notes 34 | * Trying to encode values which are unrepresentable in JSON will never result 35 | in type conversion or other magic: sparse arrays, tables with mixed key types 36 | or invalid numbers (NaN, -inf, inf) will raise an error 37 | * `null` values contained within an array or object are converted to `nil` and 38 | are therefore lost upon decoding 39 | * *Pretty* encoding is not supported, `json.encode()` only encodes to a compact 40 | format 41 | 42 | 43 | ## License 44 | This library is free software; you can redistribute it and/or modify it under 45 | the terms of the MIT license. See [LICENSE](LICENSE) for details. 46 | 47 | -------------------------------------------------------------------------------- /src/CET.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "CET.h" 4 | #include "Options.h" 5 | 6 | using namespace std::chrono_literals; 7 | 8 | static std::unique_ptr s_pInstance{nullptr}; 9 | static bool s_isRunning{true}; 10 | 11 | void CET::Initialize() 12 | { 13 | s_pInstance.reset(new CET); 14 | 15 | s_pInstance->GetVM().Prepare(); 16 | } 17 | 18 | void CET::Shutdown() 19 | { 20 | s_pInstance.reset(nullptr); 21 | } 22 | 23 | CET& CET::Get() 24 | { 25 | // we should always call this after initialization, never before! 26 | assert(s_pInstance); 27 | return *s_pInstance; 28 | } 29 | 30 | const Paths& CET::GetPaths() const noexcept 31 | { 32 | return m_paths; 33 | } 34 | 35 | const Options& CET::GetOptions() const noexcept 36 | { 37 | return m_options; 38 | } 39 | 40 | const PersistentState& CET::GetPersistentState() const noexcept 41 | { 42 | return m_persistentState; 43 | } 44 | 45 | D3D12& CET::GetD3D12() noexcept 46 | { 47 | return m_d3d12; 48 | } 49 | 50 | VKBindings& CET::GetBindings() noexcept 51 | { 52 | return m_bindings; 53 | } 54 | 55 | Overlay& CET::GetOverlay() noexcept 56 | { 57 | return m_overlay; 58 | } 59 | 60 | LuaVM& CET::GetVM() noexcept 61 | { 62 | return m_vm; 63 | } 64 | 65 | bool CET::IsRunning() noexcept 66 | { 67 | return s_isRunning; 68 | } 69 | 70 | CET::CET() 71 | : m_options(m_paths) 72 | , m_persistentState(m_paths, m_options) 73 | , m_bindings(m_paths, m_options) 74 | , m_window(&m_bindings, &m_d3d12) 75 | , m_d3d12(m_window, m_paths, m_options) 76 | , m_vm(m_paths, m_options, m_bindings, m_d3d12) 77 | , m_overlay(m_bindings, m_options, m_persistentState, m_vm) 78 | { 79 | } 80 | 81 | CET::~CET() 82 | { 83 | s_isRunning = false; 84 | } 85 | -------------------------------------------------------------------------------- /src/CET.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Options.h" 4 | #include "Paths.h" 5 | #include "PersistentState.h" 6 | #include "VKBindings.h" 7 | #include "d3d12/D3D12.h" 8 | #include "overlay/Overlay.h" 9 | #include "scripting/LuaVM.h" 10 | 11 | struct CET 12 | { 13 | ~CET(); 14 | 15 | static void Initialize(); 16 | static void Shutdown(); 17 | static CET& Get(); 18 | 19 | const Paths& GetPaths() const noexcept; 20 | const Options& GetOptions() const noexcept; 21 | const PersistentState& GetPersistentState() const noexcept; 22 | D3D12& GetD3D12() noexcept; 23 | VKBindings& GetBindings() noexcept; 24 | Overlay& GetOverlay() noexcept; 25 | LuaVM& GetVM() noexcept; 26 | 27 | static bool IsRunning() noexcept; 28 | 29 | private: 30 | CET(); 31 | 32 | Paths m_paths; 33 | Options m_options; 34 | PersistentState m_persistentState; 35 | VKBindings m_bindings; 36 | Window m_window; 37 | D3D12 m_d3d12; 38 | LuaVM m_vm; 39 | Overlay m_overlay; 40 | }; 41 | -------------------------------------------------------------------------------- /src/Image.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "Image.h" 6 | 7 | #include "RED4ext/Api/FileVer.hpp" 8 | 9 | 10 | struct PdbInfo 11 | { 12 | DWORD Signature; 13 | BYTE Guid[16]; 14 | DWORD Age; 15 | char PdbFileName[1]; 16 | }; 17 | 18 | struct ImageVersion 19 | { 20 | uint8_t Guid[16]; 21 | uint64_t Version; 22 | }; 23 | 24 | void Image::Initialize() 25 | { 26 | std::wstring fileName; 27 | TCHAR exePathBuf[MAX_PATH] = {0}; 28 | GetModuleFileNameW(GetModuleHandle(nullptr), exePathBuf, static_cast(std::size(exePathBuf))); 29 | 30 | fileName = exePathBuf; 31 | 32 | auto size = GetFileVersionInfoSize(fileName.c_str(), nullptr); 33 | if (!size) 34 | { 35 | return; 36 | } 37 | 38 | std::unique_ptr data(new (std::nothrow) uint8_t[size]()); 39 | if (!data) 40 | { 41 | return; 42 | } 43 | 44 | if (!GetFileVersionInfo(fileName.c_str(), 0, size, data.get())) 45 | { 46 | return; 47 | } 48 | 49 | struct LangAndCodePage 50 | { 51 | WORD language; 52 | WORD codePage; 53 | }* translations; 54 | uint32_t translationsBytes; 55 | 56 | if (!VerQueryValue(data.get(), L"\\VarFileInfo\\Translation", reinterpret_cast(&translations), &translationsBytes)) 57 | { 58 | return; 59 | } 60 | 61 | bool isGame = false; 62 | 63 | for (uint32_t i = 0; i < (translationsBytes / sizeof(LangAndCodePage)); i++) 64 | { 65 | wchar_t* productName; 66 | auto subBlock = fmt::format(L"\\StringFileInfo\\{:04x}{:04x}\\ProductName", translations[i].language, translations[i].codePage); 67 | 68 | if (VerQueryValue(data.get(), subBlock.c_str(), reinterpret_cast(&productName), &translationsBytes)) 69 | { 70 | constexpr std::wstring_view expectedProductName = L"Cyberpunk 2077"; 71 | if (productName == expectedProductName) 72 | { 73 | isGame = true; 74 | break; 75 | } 76 | } 77 | } 78 | 79 | if (isGame) 80 | { 81 | VS_FIXEDFILEINFO* fileInfo = nullptr; 82 | UINT fileInfoBytes; 83 | 84 | if (!VerQueryValue(data.get(), L"\\", reinterpret_cast(&fileInfo), &fileInfoBytes)) 85 | { 86 | return; 87 | } 88 | 89 | constexpr auto signature = 0xFEEF04BD; 90 | if (fileInfo->dwSignature != signature) 91 | { 92 | return; 93 | } 94 | 95 | { 96 | uint16_t major = (fileInfo->dwFileVersionMS >> 16) & 0xFF; 97 | uint16_t minor = fileInfo->dwFileVersionMS & 0xFFFF; 98 | uint16_t build = (fileInfo->dwFileVersionLS >> 16) & 0xFFFF; 99 | uint16_t revision = fileInfo->dwFileVersionLS & 0xFFFF; 100 | 101 | FileVersion = RED4EXT_FILEVER(major, minor, build, revision); 102 | } 103 | 104 | { 105 | uint8_t major = (fileInfo->dwProductVersionMS >> 16) & 0xFF; 106 | uint16_t minor = fileInfo->dwProductVersionMS & 0xFFFF; 107 | uint32_t patch = (fileInfo->dwProductVersionLS >> 16) & 0xFFFF; 108 | 109 | SemVersion = RED4EXT_SEMVER(major, minor, patch); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/Image.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RED4ext/Api/v0/FileVer.hpp" 4 | #include "RED4ext/Api/SemVer.hpp" 5 | 6 | struct Image 7 | { 8 | void Initialize(); 9 | 10 | static std::tuple GetSupportedVersion() noexcept { return std::make_tuple(2, 21); } 11 | 12 | uintptr_t base_address; 13 | mem::region TextRegion; 14 | RED4ext::v0::FileVer FileVersion{0, 0, 0, 0}; 15 | RED4ext::v0::SemVer SemVersion{0, 0, 0, 0}; 16 | }; 17 | -------------------------------------------------------------------------------- /src/Options.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Image.h" 4 | 5 | struct Paths; 6 | 7 | struct PatchesSettings 8 | { 9 | void Load(const nlohmann::json& aConfig); 10 | nlohmann::json Save() const; 11 | void ResetToDefaults(); 12 | 13 | [[nodiscard]] auto operator<=>(const PatchesSettings&) const = default; 14 | 15 | bool AsyncCompute{false}; 16 | bool Antialiasing{false}; 17 | bool DisableVignette{false}; 18 | bool DisableBoundaryTeleport{false}; 19 | bool DisableWin7Vsync{false}; 20 | }; 21 | 22 | struct FontSettings 23 | { 24 | void Load(const nlohmann::json& aConfig); 25 | nlohmann::json Save() const; 26 | void ResetToDefaults(); 27 | 28 | [[nodiscard]] auto operator<=>(const FontSettings&) const = default; 29 | 30 | std::string Path{}; 31 | std::string Language{"Default"}; 32 | float BaseSize{18.0f}; 33 | int32_t OversampleHorizontal{3}; 34 | int32_t OversampleVertical{1}; 35 | }; 36 | 37 | struct DeveloperSettings 38 | { 39 | void Load(const nlohmann::json& aConfig); 40 | nlohmann::json Save() const; 41 | void ResetToDefaults(); 42 | 43 | [[nodiscard]] auto operator<=>(const DeveloperSettings&) const = default; 44 | 45 | bool RemoveDeadBindings{true}; 46 | bool EnableImGuiAssertions{false}; 47 | bool DumpGameOptions{false}; 48 | uint64_t MaxLinesConsoleHistory{1000}; 49 | bool PersistentConsole{true}; 50 | bool EnableJIT{true}; 51 | }; 52 | 53 | struct Options 54 | { 55 | Options(Paths& aPaths); 56 | ~Options() = default; 57 | 58 | void Load(); 59 | void Save() const; 60 | void ResetToDefaults(); 61 | 62 | Image GameImage; 63 | bool ExeValid{false}; 64 | 65 | PatchesSettings Patches{}; 66 | FontSettings Font{}; 67 | DeveloperSettings Developer{}; 68 | 69 | private: 70 | Paths& m_paths; 71 | }; 72 | -------------------------------------------------------------------------------- /src/Paths.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const std::filesystem::path& Paths::Executable() const 4 | { 5 | return m_exe; 6 | } 7 | 8 | const std::filesystem::path& Paths::GameRoot() const 9 | { 10 | return m_gameRoot; 11 | } 12 | 13 | const std::filesystem::path& Paths::CETRoot() const 14 | { 15 | return m_cetRoot; 16 | } 17 | 18 | const std::filesystem::path& Paths::Config() const 19 | { 20 | return m_config; 21 | } 22 | 23 | const std::filesystem::path& Paths::PersistentState() const 24 | { 25 | return m_persistentState; 26 | } 27 | 28 | const std::filesystem::path& Paths::VKBindings() const 29 | { 30 | return m_vkBindings; 31 | } 32 | 33 | const std::filesystem::path& Paths::ModsRoot() const 34 | { 35 | return m_modsRoot; 36 | } 37 | 38 | const std::filesystem::path& Paths::R6CacheModdedRoot() const 39 | { 40 | return m_r6CacheModdedRoot; 41 | } 42 | 43 | const std::filesystem::path& Paths::Fonts() const 44 | { 45 | return m_fonts; 46 | } 47 | 48 | const std::filesystem::path& Paths::TweakDB() const 49 | { 50 | return m_tweakdb; 51 | } 52 | 53 | Paths::Paths() 54 | { 55 | TCHAR exePathBuf[MAX_PATH] = {0}; 56 | GetModuleFileName(GetModuleHandle(nullptr), exePathBuf, static_cast(std::size(exePathBuf))); 57 | m_exe = exePathBuf; 58 | 59 | m_gameRoot = m_exe.parent_path(); 60 | 61 | m_cetRoot = m_gameRoot; 62 | m_cetRoot /= L"plugins"; 63 | m_cetRoot /= L"cyber_engine_tweaks"; 64 | create_directories(m_cetRoot); 65 | 66 | m_config = m_cetRoot / L"config.json"; 67 | // remove empty config.json 68 | if (exists(m_config) && !file_size(m_config)) 69 | std::filesystem::remove(m_config); 70 | 71 | m_persistentState = m_cetRoot / L"persistent.json"; 72 | // remove empty persistentState.json 73 | if (exists(m_persistentState) && !file_size(m_persistentState)) 74 | std::filesystem::remove(m_persistentState); 75 | 76 | m_vkBindings = m_cetRoot / L"bindings.json"; 77 | // remove empty vkbindings.json 78 | if (exists(m_vkBindings) && !file_size(m_vkBindings)) 79 | std::filesystem::remove(m_vkBindings); 80 | 81 | m_modsRoot = m_cetRoot / L"mods"; 82 | create_directories(m_modsRoot); 83 | 84 | m_r6CacheModdedRoot = m_gameRoot; 85 | m_r6CacheModdedRoot /= L".."; 86 | m_r6CacheModdedRoot /= L".."; 87 | m_r6CacheModdedRoot /= L"r6"; 88 | m_r6CacheModdedRoot /= L"cache"; 89 | m_r6CacheModdedRoot /= L"modded"; 90 | 91 | m_fonts = m_cetRoot / L"fonts"; 92 | 93 | m_tweakdb = m_cetRoot / L"tweakdb"; 94 | } 95 | -------------------------------------------------------------------------------- /src/Paths.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Paths 4 | { 5 | ~Paths() = default; 6 | 7 | const std::filesystem::path& Executable() const; 8 | const std::filesystem::path& GameRoot() const; 9 | const std::filesystem::path& CETRoot() const; 10 | const std::filesystem::path& Config() const; 11 | const std::filesystem::path& PersistentState() const; 12 | const std::filesystem::path& VKBindings() const; 13 | const std::filesystem::path& ModsRoot() const; 14 | const std::filesystem::path& R6CacheModdedRoot() const; 15 | const std::filesystem::path& Fonts() const; 16 | const std::filesystem::path& TweakDB() const; 17 | 18 | private: 19 | friend struct CET; 20 | 21 | Paths(); 22 | 23 | std::filesystem::path m_exe{}; 24 | std::filesystem::path m_gameRoot{}; 25 | std::filesystem::path m_cetRoot{}; 26 | std::filesystem::path m_config{}; 27 | std::filesystem::path m_persistentState{}; 28 | std::filesystem::path m_vkBindings{}; 29 | std::filesystem::path m_modsRoot{}; 30 | std::filesystem::path m_r6CacheModdedRoot{}; 31 | std::filesystem::path m_fonts{}; 32 | std::filesystem::path m_tweakdb{}; 33 | }; -------------------------------------------------------------------------------- /src/PersistentState.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Paths.h" 4 | #include "Utils.h" 5 | 6 | void OverlayPersistentState::Load(const nlohmann::json& aConfig) 7 | { 8 | ConsoleToggled = aConfig.value("console_toggled", ConsoleToggled); 9 | BindingsToggled = aConfig.value("bindings_toggled", BindingsToggled); 10 | SettingsToggled = aConfig.value("settings_toggled", SettingsToggled); 11 | TweakDBEditorToggled = aConfig.value("tweakdbeditor_toggled", TweakDBEditorToggled); 12 | GameLogToggled = aConfig.value("gamelog_toggled", GameLogToggled); 13 | ImGuiDebugToggled = aConfig.value("imguidebug_toggled", ImGuiDebugToggled); 14 | } 15 | 16 | nlohmann::json OverlayPersistentState::Save() const 17 | { 18 | return {{"console_toggled", ConsoleToggled}, {"bindings_toggled", BindingsToggled}, {"settings_toggled", SettingsToggled}, {"tweakdbeditor_toggled", TweakDBEditorToggled}, 19 | {"gamelog_toggled", GameLogToggled}, {"imguidebug_toggled", ImGuiDebugToggled}}; 20 | } 21 | 22 | void ConsolePersistentState::Load(Options& aOptions, const nlohmann::json& aConfig) 23 | { 24 | History = aConfig.value("history", History); 25 | 26 | if (History.size() > aOptions.Developer.MaxLinesConsoleHistory) 27 | History.erase(History.begin(), History.begin() + History.size() - aOptions.Developer.MaxLinesConsoleHistory); 28 | } 29 | 30 | nlohmann::json ConsolePersistentState::Save() const 31 | { 32 | return {{"history", History}}; 33 | } 34 | 35 | void PersistentState::Load() 36 | { 37 | const auto path = GetAbsolutePath(m_paths.PersistentState(), "", false); 38 | if (path.empty()) 39 | return; 40 | 41 | std::ifstream configFile(path); 42 | if (!configFile) 43 | return; 44 | 45 | auto state = nlohmann::json::parse(configFile, nullptr, false); 46 | 47 | if (state.is_discarded()) 48 | return; 49 | 50 | const auto& overlayState = state["overlay"]; 51 | if (!overlayState.empty()) 52 | Overlay.Load(overlayState); 53 | 54 | if (!m_options.Developer.PersistentConsole) 55 | return; 56 | 57 | const auto& consoleState = state["console"]; 58 | if (!consoleState.empty()) 59 | Console.Load(m_options, consoleState); 60 | } 61 | 62 | void PersistentState::Save() const 63 | { 64 | nlohmann::json state = {{"overlay", Overlay.Save()}}; 65 | 66 | if (m_options.Developer.PersistentConsole) 67 | state["console"] = Console.Save(); 68 | 69 | const auto path = GetAbsolutePath(m_paths.PersistentState(), "", true); 70 | std::ofstream o(path); 71 | o << state.dump(4) << std::endl; 72 | } 73 | 74 | PersistentState::PersistentState(Paths& aPaths, Options& aOptions) 75 | : m_paths(aPaths) 76 | , m_options(aOptions) 77 | { 78 | Load(); 79 | Save(); 80 | 81 | GameMainThread::Get().AddShutdownTask( 82 | [this] 83 | { 84 | Save(); 85 | return true; 86 | }); 87 | } 88 | -------------------------------------------------------------------------------- /src/PersistentState.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Paths; 4 | struct Options; 5 | 6 | struct OverlayPersistentState 7 | { 8 | void Load(const nlohmann::json& aConfig); 9 | nlohmann::json Save() const; 10 | 11 | [[nodiscard]] auto operator<=>(const OverlayPersistentState&) const = default; 12 | 13 | bool ConsoleToggled = false; 14 | bool BindingsToggled = false; 15 | bool SettingsToggled = false; 16 | bool TweakDBEditorToggled = false; 17 | bool GameLogToggled = false; 18 | bool ImGuiDebugToggled = false; 19 | }; 20 | 21 | struct ConsolePersistentState 22 | { 23 | void Load(Options& aOptions, const nlohmann::json& aConfig); 24 | nlohmann::json Save() const; 25 | 26 | //[[nodiscard]] auto operator<=>(const ConsolePersistentState&) const = default; 27 | 28 | TiltedPhoques::Vector History; 29 | }; 30 | 31 | struct PersistentState 32 | { 33 | PersistentState(Paths& aPaths, Options& aOptions); 34 | ~PersistentState() = default; 35 | 36 | void Load(); 37 | void Save() const; 38 | 39 | OverlayPersistentState Overlay{}; 40 | ConsolePersistentState Console{}; 41 | 42 | private: 43 | Paths& m_paths; 44 | Options& m_options; 45 | }; 46 | -------------------------------------------------------------------------------- /src/Utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scripting/LuaSandbox.h" 4 | 5 | #include 6 | 7 | void ltrim(std::string& s); 8 | void rtrim(std::string& s); 9 | void trim(std::string& s); 10 | 11 | std::string UTF16ToUTF8(std::wstring_view utf16); 12 | std::wstring UTF8ToUTF16(std::string_view utf8); 13 | 14 | spdlog::sink_ptr CreateCustomSinkST(const std::function& acpSinkItHandler, const std::function& acpFlushHandler = nullptr); 15 | spdlog::sink_ptr CreateCustomSinkMT(const std::function& acpSinkItHandler, const std::function& acpFlushHandler = nullptr); 16 | std::shared_ptr CreateLogger( 17 | const std::filesystem::path& acpPath, const std::string& acpID, const spdlog::sink_ptr& acpExtraSink = nullptr, 18 | const std::string& acpPattern = "[%Y-%m-%d %H:%M:%S UTC%z] [%t] %v", const size_t acMaxFileSize = 5 * 1024 * 1024, const size_t acMaxFileCount = 3); 19 | 20 | // deep copies sol object (doesnt take into account potential duplicates) 21 | sol::object DeepCopySolObject(const sol::object& acpObj, const sol::state_view& acpStateView); 22 | 23 | // makes sol usertype or userdata immutable when accessed from lua 24 | void MakeSolUsertypeImmutable(const sol::object& acpObj, const sol::state_view& acpStateView); 25 | 26 | // Add unnamed function to the Lua registry 27 | template sol::function MakeSolFunction(sol::state& aState, F aFunc) 28 | { 29 | // This is slightly better than wrapping lambdas in sol::object: 30 | // 1. When the lambda is wrapped in an object sol registers additional usertype for the lambda type. 31 | // 2. Calling a lambda as an object has a tiny overhead of dealing with metatables. 32 | // 3. In Lua `type(f)` for a lambda as an object will return "userdata" instead of the expected "function". 33 | 34 | static constexpr auto s_cTempFuncName = "___func_temp_holder_"; 35 | 36 | aState[s_cTempFuncName] = aFunc; 37 | sol::function luaFunc = aState[s_cTempFuncName]; 38 | aState[s_cTempFuncName] = sol::nil; 39 | 40 | return luaFunc; 41 | } 42 | 43 | // Check if Lua object is of cdata type 44 | bool IsLuaCData(const sol::object& acpObject); 45 | 46 | float GetAlignedItemWidth(const int64_t acItemsCount); 47 | 48 | float GetCenteredOffsetForText(const char* acpText); 49 | 50 | TChangedCBResult UnsavedChangesPopup( 51 | const std::string& acpOwnerName, bool& aFirstTime, const bool acMadeChanges, const TWidgetCB& acpSaveCB, const TWidgetCB& acpLoadCB, const TWidgetCB& acpCancelCB = nullptr); 52 | 53 | [[nodiscard]] std::filesystem::path 54 | GetAbsolutePath(const std::string& acFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting, const bool acAllowSymlink = true); 55 | [[nodiscard]] std::filesystem::path 56 | GetAbsolutePath(std::filesystem::path aFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting, const bool acAllowSymlink = true); 57 | 58 | [[nodiscard]] std::vector ReadWholeBinaryFile(const std::filesystem::path& acpPath); 59 | [[nodiscard]] std::string ReadWholeTextFile(const std::filesystem::path& acpPath); 60 | -------------------------------------------------------------------------------- /src/VKBindings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using TVKBindHotkeyCallback = void(); 4 | using TVKBindInputCallback = void(bool); 5 | 6 | using VKCodeBindDecoded = std::array; 7 | 8 | struct VKModBind 9 | { 10 | std::string ModName; 11 | std::string ID; 12 | 13 | [[nodiscard]] auto operator<=>(const VKModBind&) const = default; 14 | }; 15 | 16 | struct VKBind 17 | { 18 | std::string ID{}; 19 | std::string DisplayName{}; 20 | std::variant> Description{}; 21 | std::variant, std::function> Handler{}; 22 | 23 | [[nodiscard]] std::function DelayedCall(const bool acIsDown) const; 24 | void Call(const bool acIsDown) const; 25 | 26 | [[nodiscard]] bool IsHotkey() const; 27 | [[nodiscard]] bool IsInput() const; 28 | 29 | [[nodiscard]] bool HasSimpleDescription() const; 30 | [[nodiscard]] bool HasComplexDescription() const; 31 | 32 | [[nodiscard]] bool operator==(const std::string& acpId) const; 33 | }; 34 | 35 | constexpr USHORT VKBC_MWHEELUP{RI_MOUSE_WHEEL | 1}; 36 | constexpr USHORT VKBC_MWHEELDOWN{RI_MOUSE_WHEEL | 0}; 37 | constexpr USHORT VKBC_MWHEELRIGHT{RI_MOUSE_HWHEEL | 1}; 38 | constexpr USHORT VKBC_MWHEELLEFT{RI_MOUSE_HWHEEL | 0}; 39 | 40 | struct Options; 41 | struct Overlay; 42 | struct D3D12; 43 | struct LuaVM; 44 | struct VKBindings 45 | { 46 | VKBindings(Paths& aPaths, const Options& acOptions); 47 | ~VKBindings() = default; 48 | 49 | [[nodiscard]] bool IsInitialized() const noexcept; 50 | 51 | void InitializeMods(const TiltedPhoques::Map>>& acVKBinds); 52 | 53 | [[nodiscard]] static VKCodeBindDecoded DecodeVKCodeBind(const uint64_t acVKCodeBind); 54 | [[nodiscard]] static uint64_t EncodeVKCodeBind(VKCodeBindDecoded aVKCodeBindDecoded); 55 | [[nodiscard]] static const char* GetSpecialKeyName(const USHORT acVKCode); 56 | 57 | void Load(); 58 | void Save(); 59 | 60 | void Update(); 61 | 62 | bool Bind(const uint64_t acVKCodeBind, const VKModBind& acVKModBind); 63 | bool UnBind(const uint64_t acVKCodeBind); 64 | bool UnBind(const VKModBind& acVKModBind); 65 | [[nodiscard]] bool IsBound(const uint64_t acVKCodeBind) const; 66 | [[nodiscard]] bool IsBound(const VKModBind& acVKModBind) const; 67 | [[nodiscard]] bool IsFirstKeyUsed(const uint64_t acVKCodeBind) const; 68 | 69 | [[nodiscard]] static std::string GetBindString(const uint64_t acVKCodeBind); 70 | [[nodiscard]] std::string GetBindString(const VKModBind& acVKModBind) const; 71 | 72 | [[nodiscard]] uint64_t GetBindCodeForModBind(const VKModBind& acVKModBind, const bool acIncludeDead = false) const; 73 | [[nodiscard]] const VKModBind* GetModBindForBindCode(const uint64_t acVKCodeBind) const; 74 | [[nodiscard]] const VKModBind* GetModBindStartingWithBindCode(const uint64_t acVKCodeBind) const; 75 | 76 | bool StartRecordingBind(const VKModBind& acVKModBind); 77 | bool StopRecordingBind(); 78 | 79 | [[nodiscard]] bool IsRecordingBind() const; 80 | [[nodiscard]] uint64_t GetLastRecordingResult() const; 81 | 82 | LRESULT OnWndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam); 83 | 84 | void SetVM(const LuaVM* acpVm); 85 | 86 | private: 87 | [[nodiscard]] LRESULT HandleRAWInput(HRAWINPUT achRAWInput); 88 | 89 | LRESULT RecordKeyDown(const USHORT acVKCode); 90 | LRESULT RecordKeyUp(const USHORT acVKCode); 91 | 92 | void ExecuteSingleInput(const USHORT acVKCode, const bool acKeyDown); 93 | void ExecuteRecording(); 94 | void ClearRecording(const bool acClearBind); 95 | 96 | std::map m_binds{}; // this map needs to be ordered! 97 | TiltedPhoques::Map> m_modIdToBinds{}; 98 | TiltedPhoques::TaskQueue m_queuedCallbacks{}; 99 | 100 | std::bitset<1 << 16> m_keyStates{}; 101 | 102 | VKCodeBindDecoded m_recording{}; 103 | uint64_t m_recordingResult{0}; 104 | size_t m_recordingLength{0}; 105 | bool m_recordingWasKeyPressed{false}; 106 | 107 | VKModBind m_recordingModBind{}; 108 | bool m_isBindRecording{false}; 109 | 110 | bool m_initialized{false}; 111 | 112 | const LuaVM* m_cpVm{nullptr}; 113 | Paths& m_paths; 114 | const Options& m_cOptions; 115 | }; 116 | -------------------------------------------------------------------------------- /src/common/Logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Log 4 | { 5 | using source_location = std::source_location; 6 | [[nodiscard]] constexpr auto get_log_source_location(const source_location& location) 7 | { 8 | return spdlog::source_loc{location.file_name(), static_cast(location.line()), location.function_name()}; 9 | } 10 | 11 | struct format_with_location 12 | { 13 | std::string_view value; 14 | spdlog::source_loc loc; 15 | 16 | template 17 | format_with_location(const String& s, const source_location& location = source_location::current()) 18 | : value{s} 19 | , loc{get_log_source_location(location)} 20 | { 21 | } 22 | }; 23 | 24 | template void Warn(format_with_location fmt, Args&&... args) 25 | { 26 | spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::warn, fmt::runtime(fmt.value), std::forward(args)...); 27 | } 28 | 29 | template void Info(format_with_location fmt, Args&&... args) 30 | { 31 | spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::info, fmt::runtime(fmt.value), std::forward(args)...); 32 | } 33 | 34 | template void Debug(format_with_location fmt, Args&&... args) 35 | { 36 | spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::debug, fmt::runtime(fmt.value), std::forward(args)...); 37 | } 38 | 39 | template void Error(format_with_location fmt, Args&&... args) 40 | { 41 | spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::err, fmt::runtime(fmt.value), std::forward(args)...); 42 | } 43 | 44 | template void Critical(format_with_location fmt, Args&&... args) 45 | { 46 | spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::critical, fmt::runtime(fmt.value), std::forward(args)...); 47 | } 48 | 49 | template void Trace(format_with_location fmt, Args&&... args) 50 | { 51 | spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::trace, fmt::runtime(fmt.value), std::forward(args)...); 52 | } 53 | } // namespace Log -------------------------------------------------------------------------------- /src/common/Meta.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Source: https://www.reddit.com/r/cpp/comments/bhxx49/c20_string_literals_as_nontype_template/elwmdjp/ 4 | template struct FixedString 5 | { 6 | char buf[N + 1]{}; 7 | constexpr FixedString(char const* s) 8 | { 9 | for (unsigned i = 0; i != N; ++i) 10 | buf[i] = s[i]; 11 | } 12 | constexpr operator char const*() const { return buf; } 13 | }; 14 | template FixedString(char const (&)[N]) -> FixedString; -------------------------------------------------------------------------------- /src/common/ScopeGuard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Stolen from: https://stackoverflow.com/questions/10270328/the-simplest-and-neatest-c11-scopeguard 4 | class ScopeGuard 5 | { 6 | public: 7 | template ScopeGuard(Callable&& undo_func) 8 | try : f(std::forward(undo_func)) 9 | { 10 | } 11 | catch (...) 12 | { 13 | undo_func(); 14 | throw; 15 | } 16 | 17 | ScopeGuard(ScopeGuard&& other) noexcept 18 | : f(std::move(other.f)) 19 | { 20 | other.f = nullptr; 21 | } 22 | 23 | ~ScopeGuard() 24 | { 25 | if (f) 26 | f(); // must not throw 27 | } 28 | 29 | void dismiss() noexcept { f = nullptr; } 30 | 31 | ScopeGuard(const ScopeGuard&) = delete; 32 | void operator=(const ScopeGuard&) = delete; 33 | 34 | private: 35 | std::function f; 36 | }; -------------------------------------------------------------------------------- /src/d3d12/D3D12.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "D3D12.h" 4 | #include "CET.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void D3D12::SetTrapInputInImGui(const bool acEnabled) 11 | { 12 | static const RED4ext::CName cReason = "ImGui"; 13 | static RED4ext::UniversalRelocFunc 14 | forceCursor(CyberEngineTweaks::AddressHashes::InputSystemWin32Base_ForceCursor); 15 | 16 | forceCursor(RED4ext::CGameEngine::Get()->unkD0, cReason, acEnabled); 17 | 18 | m_trapInputInImGui = acEnabled; 19 | } 20 | 21 | void D3D12::DelayedSetTrapInputInImGui(const bool acEnabled) 22 | { 23 | m_delayedTrapInputState = acEnabled; 24 | m_delayedTrapInput = true; 25 | } 26 | 27 | LRESULT D3D12::OnWndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam) const 28 | { 29 | auto& d3d12 = CET::Get().GetD3D12(); 30 | 31 | if (d3d12.IsInitialized()) 32 | { 33 | if (const auto res = ImGui_ImplWin32_WndProcHandler(ahWnd, auMsg, awParam, alParam)) 34 | return res; 35 | 36 | if (d3d12.m_delayedTrapInput) 37 | { 38 | d3d12.SetTrapInputInImGui(m_delayedTrapInputState); 39 | d3d12.m_delayedTrapInput = false; 40 | } 41 | 42 | if (d3d12.m_trapInputInImGui) // TODO: look into io.WantCaptureMouse and io.WantCaptureKeyboard 43 | { 44 | // ignore mouse & keyboard events 45 | if ((auMsg >= WM_MOUSEFIRST && auMsg <= WM_MOUSELAST) || (auMsg >= WM_KEYFIRST && auMsg <= WM_KEYLAST)) 46 | return 1; 47 | 48 | // ignore input messages 49 | if (auMsg == WM_INPUT) 50 | return 1; 51 | } 52 | } 53 | 54 | return 0; 55 | } 56 | 57 | D3D12::D3D12(Window& aWindow, Paths& aPaths, Options& aOptions) 58 | : m_paths(aPaths) 59 | , m_window(aWindow) 60 | , m_options(aOptions) 61 | { 62 | Hook(); 63 | 64 | // add repeated task which prepares next ImGui frame for update 65 | GameMainThread::Get().AddGenericTask( 66 | [this] 67 | { 68 | PrepareUpdate(); 69 | return false; 70 | }); 71 | } 72 | 73 | D3D12::~D3D12() 74 | { 75 | assert(!m_initialized); 76 | } 77 | -------------------------------------------------------------------------------- /src/d3d12/D3D12.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/D3D12Downlevel.h" 4 | #include "window/Window.h" 5 | 6 | using TCRenderNode_Present_InternalPresent = void*(int32_t*, uint8_t, UINT); 7 | using TCRenderGlobal_Resize = void*(uint32_t a1, uint32_t a2, uint32_t a3, uint8_t a4, int32_t* a5); 8 | using TCRenderGlobal_Shutdown = void*(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4); 9 | 10 | struct D3D12 11 | { 12 | inline static const uint32_t g_numDownlevelBackbuffersRequired = 3; // Windows 7 only: number of buffers needed before we start rendering 13 | 14 | D3D12(Window& aWindow, Paths& aPaths, Options& aOptions); 15 | ~D3D12(); 16 | 17 | void ReloadFonts(); 18 | 19 | void SetTrapInputInImGui(const bool acEnabled); 20 | void DelayedSetTrapInputInImGui(const bool acEnabled); 21 | [[nodiscard]] bool IsTrapInputInImGui() const noexcept { return m_trapInputInImGui; } 22 | [[nodiscard]] bool IsInitialized() const noexcept { return m_initialized; } 23 | [[nodiscard]] SIZE GetResolution() const noexcept { return m_outSize; } 24 | 25 | LRESULT OnWndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam) const; 26 | 27 | TiltedPhoques::Signal OnInitialized; 28 | 29 | ID3D12Device* GetDevice() const; 30 | std::tuple CreateTextureDescriptor(); 31 | 32 | protected: 33 | void Hook(); 34 | 35 | struct FrameContext 36 | { 37 | Microsoft::WRL::ComPtr CommandAllocator; 38 | Microsoft::WRL::ComPtr CommandList{}; 39 | Microsoft::WRL::ComPtr BackBuffer; 40 | D3D12_CPU_DESCRIPTOR_HANDLE MainRenderTargetDescriptor{0}; 41 | }; 42 | 43 | bool ResetState(const bool acDestroyContext = false); 44 | bool Initialize(); 45 | bool InitializeImGui(size_t aBuffersCounts); 46 | 47 | void PrepareUpdate(); 48 | void Update(); 49 | 50 | static void* CRenderNode_Present_InternalPresent(int32_t* apSomeInt, uint8_t aSomeSync, UINT aSyncInterval); 51 | static void* CRenderGlobal_Resize(uint32_t a1, uint32_t a2, uint32_t a3, uint8_t a4, int32_t* a5); 52 | static void* CRenderGlobal_Shutdown(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4); 53 | 54 | private: 55 | TCRenderNode_Present_InternalPresent* m_realInternalPresent{nullptr}; 56 | TCRenderGlobal_Resize* m_realInternalResize{nullptr}; 57 | TCRenderGlobal_Shutdown* m_realInternalShutdown{nullptr}; 58 | 59 | bool m_initialized{false}; 60 | 61 | TiltedPhoques::Vector m_frameContexts; 62 | 63 | Microsoft::WRL::ComPtr m_pd3d12Device{}; 64 | Microsoft::WRL::ComPtr m_pd3dRtvDescHeap{}; 65 | Microsoft::WRL::ComPtr m_pd3dSrvDescHeap{}; 66 | 67 | // borrowed resources from game, do not manipulate reference counts on these! 68 | Microsoft::WRL::ComPtr m_pdxgiSwapChain{nullptr}; 69 | Microsoft::WRL::ComPtr m_pCommandQueue{nullptr}; 70 | 71 | SIZE m_outSize{}; 72 | 73 | std::atomic_bool m_trapInputInImGui{false}; 74 | ImGuiStyle m_styleReference{}; 75 | 76 | Paths& m_paths; 77 | Window& m_window; 78 | Options& m_options; 79 | 80 | std::recursive_mutex m_imguiLock; 81 | std::array m_imguiDrawDataBuffers; 82 | std::atomic_bool m_delayedTrapInput{false}; 83 | std::atomic_bool m_delayedTrapInputState{false}; 84 | }; 85 | -------------------------------------------------------------------------------- /src/d3d12/D3D12_Hooks.cpp: -------------------------------------------------------------------------------- 1 | #include "CET.h" 2 | 3 | #include 4 | 5 | #include "D3D12.h" 6 | #include "reverse/Addresses.h" 7 | #include "reverse/RenderContext.h" 8 | 9 | #include 10 | 11 | void* D3D12::CRenderNode_Present_InternalPresent(int32_t* apDeviceIndex, uint8_t aSomeSync, UINT aSyncInterval) 12 | { 13 | auto& d3d12 = CET::Get().GetD3D12(); 14 | 15 | const auto* pContext = RenderContext::GetInstance(); 16 | auto* pSwapChain = pContext->devices[*apDeviceIndex - 1].pSwapChain; 17 | if (d3d12.m_initialized) 18 | d3d12.Update(); 19 | else 20 | { 21 | // NOTE: checking against Windows 8 as Windows 10 requires specific compatibility manifest to be detected by 22 | // these 23 | // DX12 does not work on Windows 8 and 8.1 so we should be safe with this check 24 | if (IsWindows8OrGreater()) 25 | { 26 | d3d12.m_pCommandQueue = pContext->pDirectQueue; 27 | d3d12.m_pdxgiSwapChain = pSwapChain; 28 | d3d12.Initialize(); 29 | } 30 | else 31 | { 32 | Log::Error("Unsupported OS!"); 33 | } 34 | } 35 | 36 | return d3d12.m_realInternalPresent(apDeviceIndex, aSomeSync, aSyncInterval); 37 | } 38 | 39 | void* D3D12::CRenderGlobal_Resize(uint32_t aWidth, uint32_t aHeight, uint32_t a3, uint8_t a4, int* apDeviceIndex) 40 | { 41 | auto& d3d12 = CET::Get().GetD3D12(); 42 | 43 | // TODO - ideally find a way to not call this on each minimize/maximize/etc. which causes this to be called 44 | // it can get called multiple times even when there was no resolution change or swapchain invalidation 45 | if (d3d12.m_initialized) 46 | { 47 | Log::Info("CRenderGlobal::Resize() called with initialized D3D12, triggering D3D12::ResetState."); 48 | d3d12.ResetState(); 49 | } 50 | 51 | return d3d12.m_realInternalResize(aWidth, aHeight, a3, a4, apDeviceIndex); 52 | } 53 | 54 | // NOTE - this is called 32 times, as it seems to be called for each device object in RendererContext 55 | void* D3D12::CRenderGlobal_Shutdown(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4) 56 | { 57 | auto& d3d12 = CET::Get().GetD3D12(); 58 | 59 | d3d12.ResetState(true); 60 | 61 | return d3d12.m_realInternalShutdown(a1, a2, a3, a4); 62 | } 63 | 64 | ID3D12Device* D3D12::GetDevice() const 65 | { 66 | return m_pd3d12Device.Get(); 67 | } 68 | 69 | std::tuple D3D12::CreateTextureDescriptor() 70 | { 71 | const UINT handle_increment = m_pd3d12Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); 72 | static std::atomic descriptor_index = 1; // 0 is used for ImGui font texture 73 | 74 | const auto index = descriptor_index++; 75 | 76 | // We allocate 200 descriptors 77 | if (index >= 200) 78 | return {{}, {}}; 79 | 80 | D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = m_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(); 81 | cpuHandle.ptr += handle_increment * index; 82 | D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = m_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart(); 83 | gpuHandle.ptr += handle_increment * index; 84 | 85 | return {cpuHandle, gpuHandle}; 86 | } 87 | 88 | void D3D12::Hook() 89 | { 90 | const RED4ext::UniversalRelocPtr presentInternal(CyberEngineTweaks::AddressHashes::CRenderNode_Present_DoInternal); 91 | const RED4ext::UniversalRelocPtr resizeInternal(CyberEngineTweaks::AddressHashes::CRenderGlobal_Resize); 92 | const RED4ext::UniversalRelocPtr shutdownInternal(CyberEngineTweaks::AddressHashes::CRenderGlobal_Shutdown); 93 | 94 | if (MH_CreateHook(presentInternal.GetAddr(), reinterpret_cast(&CRenderNode_Present_InternalPresent), reinterpret_cast(&m_realInternalPresent)) != MH_OK || 95 | MH_EnableHook(presentInternal.GetAddr()) != MH_OK) 96 | Log::Error("Could not hook CRenderNode_Present_InternalPresent function!"); 97 | else 98 | Log::Info("CRenderNode_Present_InternalPresent function hook complete!"); 99 | 100 | if (MH_CreateHook(resizeInternal.GetAddr(), reinterpret_cast(&CRenderGlobal_Resize), reinterpret_cast(&m_realInternalResize)) != MH_OK || 101 | MH_EnableHook(resizeInternal.GetAddr()) != MH_OK) 102 | Log::Error("Could not hook CRenderGlobal_Resize function!"); 103 | else 104 | Log::Info("CRenderGlobal_Resize function hook complete!"); 105 | 106 | if (MH_CreateHook(shutdownInternal.GetAddr(), reinterpret_cast(&CRenderGlobal_Shutdown), reinterpret_cast(&m_realInternalShutdown)) != MH_OK || 107 | MH_EnableHook(shutdownInternal.GetAddr()) != MH_OK) 108 | Log::Error("Could not hook CRenderGlobal_Shutdown function!"); 109 | else 110 | Log::Info("CRenderGlobal_Shutdown function hook complete!"); 111 | } 112 | -------------------------------------------------------------------------------- /src/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "CET.h" 4 | 5 | #include "Options.h" 6 | 7 | void OptionsInitHook(); 8 | void DisableVignettePatch(); 9 | void DisableBoundaryTeleportPatch(); 10 | 11 | static HANDLE s_modInstanceMutex = nullptr; 12 | 13 | using namespace std::chrono_literals; 14 | 15 | static void Initialize() 16 | { 17 | try 18 | { 19 | MH_Initialize(); 20 | 21 | CET::Initialize(); 22 | 23 | const auto& options = CET::Get().GetOptions(); 24 | 25 | // single instance check 26 | s_modInstanceMutex = CreateMutex(nullptr, TRUE, TEXT("Cyber Engine Tweaks Module Instance")); 27 | if (s_modInstanceMutex == nullptr) 28 | return; 29 | 30 | // initialize patches 31 | 32 | if (options.Patches.DisableVignette) 33 | DisableVignettePatch(); 34 | 35 | if (options.Patches.DisableBoundaryTeleport) 36 | DisableBoundaryTeleportPatch(); 37 | 38 | OptionsInitHook(); 39 | 40 | MH_EnableHook(nullptr); 41 | } 42 | catch (...) 43 | { 44 | } 45 | } 46 | 47 | static void Shutdown() 48 | { 49 | bool inGameProcess = false; 50 | 51 | if (s_modInstanceMutex) 52 | { 53 | inGameProcess = CET::Get().GetOptions().ExeValid; 54 | 55 | MH_DisableHook(nullptr); 56 | MH_Uninitialize(); 57 | 58 | CET::Shutdown(); 59 | 60 | ReleaseMutex(s_modInstanceMutex); 61 | } 62 | 63 | if (inGameProcess) 64 | { 65 | // flush main log (== default logger) 66 | spdlog::default_logger()->flush(); 67 | spdlog::get("scripting")->flush(); 68 | } 69 | } 70 | 71 | BOOL APIENTRY DllMain(HMODULE mod, DWORD ul_reason_for_call, LPVOID) 72 | { 73 | // Not safe to do this, the DLL uses thread_local storage 74 | //DisableThreadLibraryCalls(mod); 75 | 76 | switch (ul_reason_for_call) 77 | { 78 | case DLL_PROCESS_ATTACH: Initialize(); break; 79 | case DLL_PROCESS_DETACH: Shutdown(); break; 80 | default: break; 81 | } 82 | 83 | return TRUE; 84 | } 85 | -------------------------------------------------------------------------------- /src/imgui_impl/dx12.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for DirectX12 2 | // This needs to be used along with a Platform Backend (e.g. Win32) 3 | 4 | // Implemented features: 5 | // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! 6 | // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. 7 | // [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. 8 | 9 | // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. 10 | // See imgui_impl_dx12.cpp file for details. 11 | 12 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 13 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 14 | // Learn about Dear ImGui: 15 | // - FAQ https://dearimgui.com/faq 16 | // - Getting Started https://dearimgui.com/getting-started 17 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). 18 | // - Introduction, links and more at the top of imgui.cpp 19 | 20 | #pragma once 21 | #include "imgui.h" // IMGUI_IMPL_API 22 | #ifndef IMGUI_DISABLE 23 | #include // DXGI_FORMAT 24 | 25 | struct ID3D12Device; 26 | struct ID3D12DescriptorHeap; 27 | struct ID3D12GraphicsCommandList; 28 | struct D3D12_CPU_DESCRIPTOR_HANDLE; 29 | struct D3D12_GPU_DESCRIPTOR_HANDLE; 30 | 31 | // Follow "Getting Started" link and check examples/ folder to learn about using backends! 32 | 33 | // cmd_list is the command list that the implementation will use to render imgui draw lists. 34 | // Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate 35 | // render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle. 36 | // font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture. 37 | IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap, 38 | D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle); 39 | IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown(); 40 | IMGUI_IMPL_API void ImGui_ImplDX12_NewFrame(ID3D12CommandQueue* apCommandQueue); 41 | IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list); 42 | 43 | // Use if you want to reset your rendering device without losing Dear ImGui state. 44 | IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects(); 45 | IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects(ID3D12CommandQueue* apCommandQueue); 46 | 47 | #endif // #ifndef IMGUI_DISABLE -------------------------------------------------------------------------------- /src/imgui_impl/imgui_user_config.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // NOTE: imgui_user_config.h is included by imgui.h which is included with precompiled header, so no need to include it 4 | // here once more 5 | 6 | // global definition "Enable ImGui Assertions Logging" 7 | bool g_ImGuiAssertionsEnabled{false}; 8 | 9 | #ifdef NDEBUG 10 | // inline _wassert decl for NDEBUG as it is not emitted inside assert.h header in this case 11 | extern "C" _ACRTIMP void __cdecl _wassert(wchar_t const* _Message, wchar_t const* _File, unsigned _Line); 12 | #endif 13 | 14 | // runtime assertions which can be enabled/disabled inside CET options, always logged into main log file when they 15 | // happen 16 | void ImGuiAssert(wchar_t const* acpMessage, wchar_t const* acpFile, unsigned aLine) 17 | { 18 | // TODO - make this log to log of the one who caused assertion instead of default log! 19 | spdlog::error(L"ImGui assertion failed in file \"{}\" at line {}! Expression ({}) evaluates to false!", acpFile, aLine, acpMessage); 20 | 21 | #ifdef CET_DEBUG 22 | // we want to truly assert only in debug build, as it is causing confusion when users enable it... 23 | _wassert(acpMessage, acpFile, aLine); 24 | #endif 25 | } 26 | -------------------------------------------------------------------------------- /src/imgui_impl/imgui_user_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // global declaration "Enable ImGui Assertions" 4 | extern bool g_ImGuiAssertionsEnabled; 5 | 6 | #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD 7 | 8 | // runtime assertions which can be enabled/disabled inside CET options 9 | void ImGuiAssert(wchar_t const* acpMessage, wchar_t const* acpFile, unsigned aLine); 10 | 11 | // custom assertion function macro for ImGui 12 | #define IM_ASSERT(expression) (void)((g_ImGuiAssertionsEnabled && ((!!(expression)) || (ImGuiAssert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0)))) -------------------------------------------------------------------------------- /src/imgui_impl/win32.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications) 2 | // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) 3 | 4 | // Implemented features: 5 | // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) 6 | // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. 7 | // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] 8 | // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. 10 | // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. 11 | 12 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 13 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 14 | // Learn about Dear ImGui: 15 | // - FAQ https://dearimgui.com/faq 16 | // - Getting Started https://dearimgui.com/getting-started 17 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). 18 | // - Introduction, links and more at the top of imgui.cpp 19 | 20 | #pragma once 21 | #include "imgui.h" // IMGUI_IMPL_API 22 | #ifndef IMGUI_DISABLE 23 | 24 | // Follow "Getting Started" link and check examples/ folder to learn about using backends! 25 | IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); 26 | IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd); 27 | IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); 28 | IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(SIZE aOutSize); 29 | 30 | // Win32 message handler your application need to call. 31 | extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 32 | 33 | // DPI-related helpers (optional) 34 | // - Use to enable DPI awareness without having to create an application manifest. 35 | // - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. 36 | // - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. 37 | // but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, 38 | // neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. 39 | IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness(); 40 | IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd 41 | IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor 42 | 43 | // Transparency related helpers (optional) [experimental] 44 | // - Use to enable alpha compositing transparency with the desktop. 45 | // - Use together with e.g. clearing your framebuffer with zero-alpha. 46 | IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd 47 | 48 | #endif // #ifndef IMGUI_DISABLE -------------------------------------------------------------------------------- /src/lsqlite3/README: -------------------------------------------------------------------------------- 1 | 2 | LuaSQLite3 provides a means to manipulate SQLite3 3 | databases directly from lua using Lua 5. 4 | 5 | There are two modules, identical except that one links 6 | SQLite3 dynamically, the other statically. 7 | 8 | The module lsqlite3 links SQLite3 dynamically. 9 | To use this module you need the SQLite3 library. 10 | You can get it from http://www.sqlite.org/ 11 | 12 | The module lsqlite3complete links SQLite3 statically. 13 | The SQLite3 amalgamation source code is included in 14 | the LuaSQLite3 distribution. 15 | 16 | Lua 5 is available from http://www.lua.org/ 17 | -------------------------------------------------------------------------------- /src/lsqlite3/lsqlite3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | LUALIB_API int luaopen_lsqlite3(lua_State* L); 6 | -------------------------------------------------------------------------------- /src/overlay/Overlay.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/Console.h" 4 | #include "widgets/Bindings.h" 5 | #include "widgets/Settings.h" 6 | #include "widgets/TweakDBEditor.h" 7 | #include "widgets/GameLog.h" 8 | #include "widgets/ImGuiDebug.h" 9 | 10 | struct Overlay 11 | { 12 | Overlay(VKBindings& aBindings, Options& aOptions, PersistentState& aPersistentState, LuaVM& aVm); 13 | ~Overlay(); 14 | 15 | void PostInitialize(); 16 | 17 | [[nodiscard]] bool IsInitialized() const noexcept; 18 | 19 | [[nodiscard]] Console& GetConsole(); 20 | [[nodiscard]] Bindings& GetBindings(); 21 | [[nodiscard]] Settings& GetSettings(); 22 | 23 | void Toggle(); 24 | [[nodiscard]] bool IsEnabled() const noexcept; 25 | 26 | void Update(); 27 | 28 | private: 29 | void DrawToolbar(); 30 | 31 | Console m_console; 32 | Bindings m_bindings; 33 | Settings m_settings; 34 | TweakDBEditor m_tweakDBEditor; 35 | GameLog m_gameLog; 36 | ImGuiDebug m_imguiDebug; 37 | 38 | std::atomic_bool m_enabled{false}; 39 | std::atomic_bool m_toggled{false}; 40 | bool m_initialized{false}; 41 | 42 | Options& m_options; 43 | PersistentState& m_persistentState; 44 | LuaVM& m_vm; 45 | }; 46 | -------------------------------------------------------------------------------- /src/overlay/widgets/Bindings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.h" 4 | 5 | struct VKBindInfo 6 | { 7 | const VKBind& Bind; 8 | uint64_t CodeBind{0}; 9 | uint64_t SavedCodeBind{0}; 10 | bool IsBinding{false}; 11 | 12 | bool operator==(const std::string& id) const; 13 | }; 14 | 15 | struct LuaVM; 16 | struct Bindings : Widget 17 | { 18 | Bindings(VKBindings& aBindings, LuaVM& aVm); 19 | ~Bindings() override = default; 20 | 21 | WidgetResult OnEnable() override; 22 | WidgetResult OnDisable() override; 23 | 24 | void Save(); 25 | void ResetChanges(); 26 | 27 | [[nodiscard]] static bool IsFirstTimeSetup(); 28 | [[nodiscard]] bool FirstTimeSetup(); 29 | 30 | [[nodiscard]] static const VKModBind& GetOverlayToggleModBind() noexcept; 31 | [[nodiscard]] static const VKBind& GetOverlayToggleBind() noexcept; 32 | 33 | protected: 34 | void OnUpdate() override; 35 | WidgetResult OnPopup() override; 36 | 37 | private: 38 | void Initialize(); 39 | void UpdateAndDrawBinding(const VKModBind& acModBind, VKBindInfo& aVKBindInfo); 40 | void UpdateAndDrawModBindings(const std::string& acModName, TiltedPhoques::Vector& aVKBindInfos, size_t aHotkeyCount, bool aSimplified = false); 41 | 42 | TiltedPhoques::Map, size_t>> m_vkBindInfos{}; 43 | VKBindings& m_bindings; 44 | LuaVM& m_vm; 45 | 46 | TChangedCBResult m_popupResult{TChangedCBResult::APPLY}; 47 | bool m_madeChanges{false}; 48 | bool m_openChangesModal{true}; 49 | }; 50 | -------------------------------------------------------------------------------- /src/overlay/widgets/Console.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Console.h" 4 | 5 | #include 6 | #include 7 | 8 | Console::Console(Options& aOptions, PersistentState& aPersistentState, LuaVM& aVm) 9 | : Widget("Console") 10 | , m_options(aOptions) 11 | , m_persistentState(aPersistentState) 12 | , m_vm(aVm) 13 | , m_logWindow("scripting") 14 | { 15 | m_command.resize(255); 16 | m_historyIndex = m_persistentState.Console.History.empty() ? 0 : m_persistentState.Console.History.size() - 1; 17 | } 18 | 19 | WidgetResult Console::OnDisable() 20 | { 21 | m_command.clear(); 22 | m_command.resize(255); 23 | 24 | return Widget::OnDisable(); 25 | } 26 | 27 | int Console::HandleConsoleHistory(ImGuiInputTextCallbackData* apData) 28 | { 29 | auto* pConsole = static_cast(apData->UserData); 30 | auto& pHistory = pConsole->m_persistentState.Console.History; 31 | 32 | std::string_view pHistoryStr; 33 | 34 | if (pConsole->m_newHistory) 35 | { 36 | if (!pHistory.empty()) 37 | pHistoryStr = pHistory.back(); 38 | } 39 | else if (apData->EventKey == ImGuiKey_UpArrow && pConsole->m_historyIndex > 0) 40 | { 41 | pHistoryStr = pHistory[--pConsole->m_historyIndex]; 42 | } 43 | else if (apData->EventKey == ImGuiKey_DownArrow && pConsole->m_historyIndex + 1 < pHistory.size()) 44 | { 45 | pHistoryStr = pHistory[++pConsole->m_historyIndex]; 46 | } 47 | 48 | pConsole->m_newHistory = false; 49 | 50 | if (!pHistoryStr.empty()) 51 | { 52 | apData->DeleteChars(0, apData->BufTextLen); 53 | apData->InsertChars(0, pHistoryStr.data()); 54 | apData->SelectAll(); 55 | } 56 | 57 | return 0; 58 | } 59 | 60 | int Console::HandleConsoleResize(ImGuiInputTextCallbackData* apData) 61 | { 62 | auto* pConsole = static_cast(apData->UserData); 63 | 64 | if (apData->BufTextLen + 1 >= apData->BufSize) 65 | { 66 | pConsole->m_command.resize((apData->BufSize * 3) / 2); 67 | apData->Buf = pConsole->m_command.data(); 68 | } 69 | pConsole->m_commandLength = apData->BufTextLen; 70 | 71 | return 0; 72 | } 73 | 74 | int Console::HandleConsole(ImGuiInputTextCallbackData* apData) 75 | { 76 | if (apData->EventFlag & ImGuiInputTextFlags_CallbackHistory) 77 | return HandleConsoleHistory(apData); 78 | 79 | if (apData->EventFlag & ImGuiInputTextFlags_CallbackResize) 80 | return HandleConsoleResize(apData); 81 | 82 | return 0; 83 | } 84 | 85 | void Console::OnUpdate() 86 | { 87 | const auto& style = ImGui::GetStyle(); 88 | const auto inputLineHeight = ImGui::GetTextLineHeight() + style.ItemInnerSpacing.y * 2; 89 | m_logWindow.Draw({-FLT_MIN, -(inputLineHeight + style.ItemSpacing.y)}); 90 | 91 | ImGui::SetNextItemWidth(-FLT_MIN); 92 | constexpr auto flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackResize; 93 | const auto execute = ImGui::InputText("##InputCommand", m_command.data(), m_command.capacity(), flags, &HandleConsole, this); 94 | ImGui::SetItemDefaultFocus(); 95 | if (execute) 96 | { 97 | m_command.resize(m_commandLength); 98 | 99 | // make sure to ignore trailing and beginning whitespaces 100 | trim(m_command); 101 | 102 | const auto consoleLogger = spdlog::get("scripting"); 103 | consoleLogger->info("> {}", m_command); 104 | 105 | // execute command and record it to history if it is not empty 106 | if (!m_command.empty()) 107 | { 108 | if (!m_vm.ExecuteLua(m_command)) 109 | consoleLogger->info("Command failed to execute!"); 110 | 111 | m_command.shrink_to_fit(); 112 | 113 | auto& history = m_persistentState.Console.History; 114 | 115 | if (history.size() >= m_options.Developer.MaxLinesConsoleHistory) 116 | history.erase(history.begin(), history.begin() + history.size() - m_options.Developer.MaxLinesConsoleHistory + 1); 117 | 118 | auto& newHistoryEntry = history.emplace_back(); 119 | newHistoryEntry.swap(m_command); 120 | m_historyIndex = history.size(); 121 | m_newHistory = true; 122 | 123 | m_command.resize(255); 124 | m_commandLength = 0; 125 | } 126 | 127 | ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/overlay/widgets/Console.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.h" 4 | #include "LogWindow.h" 5 | 6 | struct LuaVM; 7 | struct Console : Widget 8 | { 9 | Console(Options& aOptions, PersistentState& aPersistentState, LuaVM& aVm); 10 | ~Console() override = default; 11 | 12 | WidgetResult OnDisable() override; 13 | 14 | protected: 15 | void OnUpdate() override; 16 | 17 | private: 18 | static int HandleConsoleHistory(ImGuiInputTextCallbackData* apData); 19 | static int HandleConsoleResize(ImGuiInputTextCallbackData* apData); 20 | static int HandleConsole(ImGuiInputTextCallbackData* apData); 21 | 22 | Options& m_options; 23 | PersistentState& m_persistentState; 24 | LuaVM& m_vm; 25 | 26 | LogWindow m_logWindow; 27 | 28 | size_t m_historyIndex{0}; 29 | bool m_newHistory{true}; 30 | 31 | std::string m_command; 32 | int m_commandLength{0}; 33 | }; 34 | -------------------------------------------------------------------------------- /src/overlay/widgets/GameLog.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "GameLog.h" 4 | 5 | GameLog::GameLog() 6 | : Widget("Game Log") 7 | , m_logWindow("gamelog") 8 | { 9 | } 10 | 11 | void GameLog::OnUpdate() 12 | { 13 | m_logWindow.Draw({-FLT_MIN, -FLT_MIN}); 14 | } 15 | -------------------------------------------------------------------------------- /src/overlay/widgets/GameLog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.h" 4 | #include "LogWindow.h" 5 | 6 | struct GameLog : Widget 7 | { 8 | GameLog(); 9 | ~GameLog() override = default; 10 | 11 | protected: 12 | void OnUpdate() override; 13 | 14 | private: 15 | LogWindow m_logWindow; 16 | }; 17 | -------------------------------------------------------------------------------- /src/overlay/widgets/ImGuiDebug.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ImGuiDebug.h" 4 | 5 | ImGuiDebug::ImGuiDebug() 6 | : Widget("ImGui Debug", true) 7 | { 8 | } 9 | 10 | void ImGuiDebug::OnUpdate() 11 | { 12 | // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, 13 | // etc. 14 | ImGui::ShowMetricsWindow(); 15 | 16 | // create Debug Log window. display a simplified log of important dear imgui events. 17 | ImGui::ShowDebugLogWindow(); 18 | 19 | // create Stack Tool window. hover items with mouse to query information about the source of their unique ID. 20 | ImGui::ShowStackToolWindow(); 21 | 22 | // create About window. display Dear ImGui version, credits and build/system information. 23 | ImGui::ShowAboutWindow(); 24 | 25 | // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to 26 | // and save to (else it uses the default style) 27 | ImGui::Begin("Dear ImGui Style Editor"); 28 | ImGui::ShowStyleEditor(nullptr); 29 | ImGui::End(); 30 | 31 | // add basic help/info block (not a window): how to manipulate ImGui as a end-user (mouse/keyboard controls). 32 | // ImGui::ShowUserGuide(); 33 | } 34 | -------------------------------------------------------------------------------- /src/overlay/widgets/ImGuiDebug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.h" 4 | 5 | struct D3D12; 6 | struct ImGuiDebug : Widget 7 | { 8 | ImGuiDebug(); 9 | ~ImGuiDebug() override = default; 10 | 11 | protected: 12 | void OnUpdate() override; 13 | }; 14 | -------------------------------------------------------------------------------- /src/overlay/widgets/LogWindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "LogWindow.h" 4 | 5 | #include 6 | 7 | LogWindow::LogWindow(const std::string& acpLoggerName) 8 | : m_loggerName(acpLoggerName) 9 | { 10 | auto logSink = CreateCustomSinkMT([this](const std::string& msg) { Log(msg); }); 11 | logSink->set_pattern("%L;%v"); 12 | spdlog::get(m_loggerName)->sinks().emplace_back(std::move(logSink)); 13 | } 14 | 15 | void LogWindow::Draw(const ImVec2& size) 16 | { 17 | const auto itemWidth = GetAlignedItemWidth(2); 18 | 19 | if (ImGui::Button("Clear output", ImVec2(itemWidth, 0))) 20 | { 21 | m_normalizedWidth = -1.0f; 22 | std::lock_guard _{m_lock}; 23 | m_nextIndexToCheck = 0; 24 | m_lines.clear(); 25 | } 26 | ImGui::SameLine(); 27 | ImGui::Checkbox("Auto-scroll", &m_shouldScroll); 28 | 29 | const auto& style = ImGui::GetStyle(); 30 | 31 | const auto frameId = ImGui::GetID(("##" + m_loggerName).c_str()); 32 | ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0, 0, 0.5f)); 33 | if (ImGui::BeginChildFrame(frameId, size, ImGuiWindowFlags_HorizontalScrollbar)) 34 | { 35 | std::lock_guard _{m_lock}; 36 | 37 | if (!m_lines.empty() && (m_normalizedWidth < 0.0f || m_nextIndexToCheck < m_lines.size())) 38 | { 39 | for (size_t i = m_nextIndexToCheck; i < m_lines.size(); ++i) 40 | { 41 | auto& line = m_lines[i].second; 42 | m_normalizedWidth = std::max(m_normalizedWidth, ImGui::CalcTextSize(line.c_str()).x); 43 | } 44 | m_nextIndexToCheck = m_lines.size(); 45 | } 46 | const auto listItemWidth = std::max(m_normalizedWidth + style.ItemInnerSpacing.x * 2, ImGui::GetContentRegionAvail().x); 47 | 48 | ImGuiListClipper clipper; 49 | clipper.Begin(static_cast(m_lines.size())); 50 | while (clipper.Step()) 51 | { 52 | for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) 53 | { 54 | auto [level, item] = m_lines[i]; 55 | 56 | switch (level) 57 | { 58 | case spdlog::level::level_enum::trace: ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{0.0f, 0.0f, 1.0f, 1.0f}); break; 59 | 60 | case spdlog::level::level_enum::debug: ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{0.0f, 1.0f, 0.0f, 1.0f}); break; 61 | 62 | case spdlog::level::level_enum::warn: ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 1.0f, 0.0f, 1.0f}); break; 63 | 64 | case spdlog::level::level_enum::err: 65 | case spdlog::level::level_enum::critical: ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.1f, 0.2f, 1.0f}); break; 66 | 67 | case spdlog::level::level_enum::info: 68 | default: ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text)); 69 | } 70 | 71 | ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0, 0, 0, 0)); 72 | ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0, 0, 0, 0)); 73 | ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0, 0, 0, 0)); 74 | ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); 75 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); 76 | 77 | ImGui::SetNextItemWidth(listItemWidth); 78 | 79 | ImGui::PushID(i); 80 | ImGui::InputText(("##" + item).c_str(), item.data(), item.size(), ImGuiInputTextFlags_ReadOnly); 81 | ImGui::PopID(); 82 | 83 | ImGui::PopStyleVar(2); 84 | ImGui::PopStyleColor(4); 85 | } 86 | } 87 | 88 | if (m_scroll) 89 | { 90 | if (m_shouldScroll) 91 | { 92 | ImGui::SetScrollHereY(); 93 | ImGui::SetScrollX(0.0f); 94 | } 95 | m_scroll = false; 96 | } 97 | } 98 | ImGui::EndChildFrame(); 99 | ImGui::PopStyleColor(1); // pop ImGuiCol_FrameBg 100 | } 101 | 102 | void LogWindow::Log(const std::string& acpText) 103 | { 104 | assert(!acpText.empty()); 105 | assert(acpText.size() >= 2); 106 | assert(acpText[1] == ';'); 107 | 108 | spdlog::level::level_enum level = spdlog::level::level_enum::off; 109 | switch (acpText[0]) 110 | { 111 | case 'T': // trace 112 | level = spdlog::level::level_enum::trace; 113 | break; 114 | case 'D': // debug 115 | level = spdlog::level::level_enum::debug; 116 | break; 117 | case 'I': // info 118 | level = spdlog::level::level_enum::info; 119 | break; 120 | case 'W': // warning 121 | level = spdlog::level::level_enum::warn; 122 | break; 123 | case 'E': // error 124 | level = spdlog::level::level_enum::err; 125 | break; 126 | case 'C': // critical 127 | level = spdlog::level::level_enum::critical; 128 | break; 129 | } 130 | assert(level != spdlog::level::level_enum::off); 131 | 132 | size_t first = 2; 133 | const size_t size = acpText.size(); 134 | while (first < size) 135 | { 136 | // find_first_of \r or \n 137 | size_t second = std::string::npos; 138 | for (size_t i = first; i != size; ++i) 139 | { 140 | const auto ch = acpText[i]; 141 | if (ch == '\r' || ch == '\n') 142 | { 143 | second = i; 144 | break; 145 | } 146 | } 147 | 148 | if (second == std::string_view::npos) 149 | { 150 | auto text = acpText.substr(first); 151 | std::lock_guard _{m_lock}; 152 | m_lines.emplace_back(level, std::move(text)); 153 | break; 154 | } 155 | 156 | if (first != second) 157 | { 158 | auto text = acpText.substr(first, second - first); 159 | std::lock_guard _{m_lock}; 160 | m_lines.emplace_back(level, std::move(text)); 161 | } 162 | 163 | first = second + 1; 164 | char ch = acpText[first]; 165 | while (ch == '\r' || ch == '\n') 166 | ch = acpText[++first]; 167 | } 168 | 169 | std::lock_guard _{m_lock}; 170 | m_scroll = true; 171 | } 172 | -------------------------------------------------------------------------------- /src/overlay/widgets/LogWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct LogWindow 4 | { 5 | LogWindow(const std::string& acpLoggerName); 6 | 7 | void Draw(const ImVec2& size); 8 | 9 | private: 10 | void Log(const std::string& acpText); 11 | 12 | std::string m_loggerName; 13 | float m_normalizedWidth{-1.0f}; 14 | bool m_shouldScroll{true}; 15 | 16 | std::recursive_mutex m_lock; 17 | TiltedPhoques::Vector> m_lines; 18 | size_t m_nextIndexToCheck{0}; 19 | bool m_scroll{false}; 20 | }; 21 | -------------------------------------------------------------------------------- /src/overlay/widgets/Settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.h" 4 | 5 | #include 6 | 7 | struct LuaVM; 8 | struct Settings : Widget 9 | { 10 | Settings(Options& aOptions, LuaVM& aVm); 11 | ~Settings() override = default; 12 | 13 | WidgetResult OnDisable() override; 14 | 15 | void Load(); 16 | void Save() const; 17 | void ResetToDefaults(); 18 | 19 | protected: 20 | void OnUpdate() override; 21 | WidgetResult OnPopup() override; 22 | 23 | private: 24 | void UpdateAndDrawSetting(const std::string& acLabel, const std::string& acTooltip, bool& aCurrent, const bool& acSaved); 25 | 26 | PatchesSettings m_patches; 27 | DeveloperSettings m_developer; 28 | 29 | Options& m_options; 30 | LuaVM& m_vm; 31 | 32 | TChangedCBResult m_popupResult{TChangedCBResult::APPLY}; 33 | bool m_madeChanges{false}; 34 | bool m_openChangesModal{true}; 35 | }; 36 | -------------------------------------------------------------------------------- /src/overlay/widgets/TweakDBEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.h" 4 | 5 | struct LuaVM; 6 | struct TweakDBEditor : Widget 7 | { 8 | TweakDBEditor(LuaVM& aVm); 9 | ~TweakDBEditor() override = default; 10 | 11 | protected: 12 | void OnUpdate() override; 13 | 14 | void RebuildCache(); 15 | 16 | void RefreshAll(); 17 | void RefreshRecords(); 18 | void RefreshFlats(); 19 | void FilterAll(); 20 | void FilterRecords(bool aFilterTab = true, bool aFilterDropdown = false); 21 | void FilterFlats(); 22 | bool DrawRecordDropdown(const char* acpLabel, RED4ext::TweakDBID& aDBID, float aWidth = 0); 23 | 24 | static std::string GetTweakDBIDStringRecord(RED4ext::TweakDBID aDBID); 25 | static bool GetTweakDBIDStringRecord(RED4ext::TweakDBID aDBID, std::string& aString); 26 | static std::string GetTweakDBIDStringFlat(RED4ext::TweakDBID aDBID); 27 | static bool GetTweakDBIDStringFlat(RED4ext::TweakDBID aDBID, std::string& aString); 28 | static std::string GetTweakDBIDStringQuery(RED4ext::TweakDBID aDBID); 29 | static bool GetTweakDBIDStringQuery(RED4ext::TweakDBID aDBID, std::string& aString); 30 | 31 | bool DrawFlat(RED4ext::TweakDBID aDBID); 32 | bool DrawFlat(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 33 | bool DrawFlatArray(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false, bool aCollapsable = true); 34 | bool DrawFlatTweakDBID(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 35 | static bool DrawFlatQuaternion(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 36 | static bool DrawFlatEulerAngles(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 37 | static bool DrawFlatVector3(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 38 | static bool DrawFlatVector2(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 39 | static bool DrawFlatColor(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 40 | static bool DrawFlatLocKeyWrapper(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 41 | static bool DrawFlatResourceAsyncRef(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 42 | static bool DrawFlatCName(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 43 | static bool DrawFlatBool(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 44 | static bool DrawFlatString(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 45 | static bool DrawFlatFloat(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 46 | static bool DrawFlatInt32(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly = false); 47 | 48 | void DrawRecordsTab(); 49 | void DrawQueriesTab(); 50 | void DrawFlatsTab(); 51 | void DrawAdvancedTab(); 52 | 53 | private: 54 | // like ImGuiListClipper but supports dynamic size 55 | struct ImGuiVisibilityChecker 56 | { 57 | bool IsVisible(bool aClaimSpaceIfInvisible = true) const; 58 | void Begin(); 59 | void End(); 60 | 61 | private: 62 | ImVec2 m_itemSize{0.0f, 0.0f}; 63 | float m_beginCursorY{0.0f}; 64 | }; 65 | 66 | struct CachedFlat 67 | { 68 | bool m_isFiltered = false; 69 | bool m_isMissing = false; 70 | std::string m_name; 71 | RED4ext::TweakDBID m_dbid; 72 | ImGuiVisibilityChecker m_visibilityChecker; 73 | 74 | CachedFlat(std::string aName, RED4ext::TweakDBID aDBID) noexcept; 75 | CachedFlat(CachedFlat&&) noexcept = default; 76 | CachedFlat& operator=(CachedFlat&&) noexcept = default; 77 | void Update(int32_t aTDBOffset = -1); 78 | }; 79 | 80 | struct CachedFlatGroup 81 | { 82 | bool m_isFiltered = false; 83 | bool m_isInitialized = false; 84 | std::string m_name; 85 | TiltedPhoques::Vector m_flats; 86 | ImGuiVisibilityChecker m_visibilityChecker; 87 | 88 | CachedFlatGroup(std::string aName) noexcept; 89 | CachedFlatGroup(CachedFlatGroup&&) noexcept = default; 90 | CachedFlatGroup& operator=(CachedFlatGroup&&) noexcept = default; 91 | void Initialize(); 92 | }; 93 | 94 | struct CachedRecord 95 | { 96 | bool m_isFiltered = false; 97 | bool m_isDropdownFiltered = false; 98 | bool m_isInitialized = false; 99 | std::string m_name; 100 | RED4ext::TweakDBID m_dbid; 101 | TiltedPhoques::Vector m_flats; 102 | ImGuiVisibilityChecker m_visibilityChecker; 103 | 104 | CachedRecord(std::string aName, RED4ext::TweakDBID aDBID) noexcept; 105 | CachedRecord(CachedRecord&&) noexcept = default; 106 | CachedRecord& operator=(CachedRecord&&) noexcept = default; 107 | void Initialize(); 108 | void InitializeFlats(); 109 | void Update() const; 110 | }; 111 | 112 | struct CachedRecordGroup 113 | { 114 | bool m_isFiltered = false; 115 | bool m_isInitialized = false; 116 | std::string m_name; 117 | RED4ext::CName m_typeName; 118 | TiltedPhoques::Vector m_records; 119 | ImGuiVisibilityChecker m_visibilityChecker; 120 | 121 | CachedRecordGroup(RED4ext::CName aTypeName); 122 | CachedRecordGroup(CachedRecordGroup&&) noexcept = default; 123 | CachedRecordGroup& operator=(CachedRecordGroup&&) noexcept = default; 124 | void Initialize(); 125 | }; 126 | 127 | LuaVM& m_vm; 128 | bool m_initialized = false; 129 | bool m_rebuildingCache = false; 130 | std::future m_rebuildCacheFuture; 131 | int32_t m_flatGroupNameDepth = 1; 132 | TiltedPhoques::Vector m_cachedFlatGroups; 133 | TiltedPhoques::Vector m_cachedRecords; 134 | static bool s_recordsFilterIsRegex; 135 | static bool s_flatsFilterIsRegex; 136 | static char s_recordsFilterBuffer[256]; 137 | static char s_flatsFilterBuffer[256]; 138 | static char s_tweakdbidFilterBuffer[256]; 139 | }; 140 | -------------------------------------------------------------------------------- /src/overlay/widgets/Widget.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Widget.h" 4 | 5 | #include 6 | 7 | Widget::Widget(const std::string& acpName, bool aOwnerDraw) 8 | : m_name(acpName) 9 | , m_ownerDraw(aOwnerDraw) 10 | { 11 | } 12 | 13 | WidgetResult Widget::OnEnable() 14 | { 15 | m_enabled = true; 16 | return WidgetResult::ENABLED; 17 | } 18 | 19 | WidgetResult Widget::OnDisable() 20 | { 21 | m_enabled = false; 22 | return WidgetResult::DISABLED; 23 | } 24 | 25 | bool Widget::IsEnabled() const 26 | { 27 | return m_enabled; 28 | } 29 | 30 | void Widget::Toggle() 31 | { 32 | m_toggle = true; 33 | } 34 | 35 | WidgetResult Widget::OnPopup() 36 | { 37 | return m_enabled ? WidgetResult::DISABLED : WidgetResult::ENABLED; 38 | } 39 | 40 | void Widget::OnToggle() 41 | { 42 | if (m_enabled) 43 | { 44 | const auto ret = OnDisable(); 45 | if (ret == WidgetResult::CANCEL) 46 | { 47 | m_toggle = false; 48 | } 49 | if (ret == WidgetResult::DISABLED) 50 | { 51 | m_enabled = false; 52 | m_toggle = false; 53 | } 54 | } 55 | else 56 | { 57 | const auto ret = OnEnable(); 58 | if (ret == WidgetResult::CANCEL) 59 | { 60 | m_toggle = false; 61 | } 62 | if (ret == WidgetResult::ENABLED) 63 | { 64 | m_enabled = true; 65 | m_toggle = false; 66 | } 67 | } 68 | } 69 | 70 | void Widget::Draw() 71 | { 72 | if (m_drawPopup) 73 | { 74 | CET::Get().GetVM().BlockDraw(true); 75 | m_drawPopup = OnPopup() == WidgetResult::ENABLED; 76 | if (!m_drawPopup) 77 | { 78 | CET::Get().GetVM().BlockDraw(false); 79 | ImGui::CloseCurrentPopup(); 80 | } 81 | } 82 | 83 | if (m_toggle) 84 | OnToggle(); 85 | 86 | if (!m_enabled) 87 | return; 88 | 89 | bool newEnabled = m_enabled; 90 | 91 | if (m_ownerDraw) 92 | OnUpdate(); 93 | else 94 | { 95 | const auto [width, height] = CET::Get().GetD3D12().GetResolution(); 96 | ImGui::SetNextWindowPos(ImVec2(width * 0.2f, height * 0.2f), ImGuiCond_FirstUseEver); 97 | ImGui::SetNextWindowSize(ImVec2(width * 0.6f, height * 0.6f), ImGuiCond_FirstUseEver); 98 | ImGui::SetNextWindowSizeConstraints(ImVec2(420, 315), ImVec2(FLT_MAX, FLT_MAX)); 99 | if (ImGui::Begin(m_name.c_str(), &newEnabled)) 100 | OnUpdate(); 101 | ImGui::End(); 102 | } 103 | 104 | if (!newEnabled) 105 | { 106 | Toggle(); 107 | OnToggle(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/overlay/widgets/Widget.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class WidgetResult 4 | { 5 | DISABLED, 6 | ENABLED, 7 | CANCEL 8 | }; 9 | 10 | struct Widget 11 | { 12 | Widget(const std::string& acpName, bool aOwnerDraw = false); 13 | virtual ~Widget() = default; 14 | 15 | virtual WidgetResult OnEnable(); 16 | virtual WidgetResult OnDisable(); 17 | 18 | bool IsEnabled() const; 19 | void Toggle(); 20 | 21 | void Draw(); 22 | 23 | protected: 24 | virtual void OnUpdate() = 0; 25 | 26 | virtual WidgetResult OnPopup(); 27 | virtual void OnToggle(); 28 | 29 | std::string m_name; 30 | bool m_ownerDraw{false}; 31 | bool m_enabled{false}; 32 | bool m_toggle{false}; 33 | bool m_drawPopup{false}; 34 | }; 35 | 36 | using TWidgetCB = std::function; 37 | 38 | enum class TChangedCBResult 39 | { 40 | CHANGED, 41 | APPLY, 42 | DISCARD, 43 | CANCEL 44 | }; 45 | -------------------------------------------------------------------------------- /src/patches/DisableBoundaries.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | void DisableBoundaryTeleportPatch() 5 | { 6 | // Disarm the WorldBoundarySystem/Tick function 7 | // Going out of bounds will still play the glitchy-screen effect that normally happens when game teleports you, but 8 | // the actual teleport won't happen 9 | const RED4ext::UniversalRelocPtr func(CyberEngineTweaks::AddressHashes::CPatches_BoundaryTeleport); 10 | const auto pLocation = func.GetAddr(); 11 | 12 | if (pLocation == nullptr) 13 | { 14 | Log::Warn("Disable boundary teleport: failed, could not be found"); 15 | return; 16 | } 17 | 18 | DWORD oldProtect = 0; 19 | VirtualProtect(pLocation, 32, PAGE_EXECUTE_WRITECOPY, &oldProtect); 20 | pLocation[0] = 0xC3; 21 | VirtualProtect(pLocation, 32, oldProtect, nullptr); 22 | 23 | Log::Info("Disable boundary teleport: success"); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/patches/DisableVignette.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | void DisableVignettePatch() 5 | { 6 | const RED4ext::UniversalRelocPtr func(CyberEngineTweaks::AddressHashes::CPatches_Vignette); 7 | const auto pLocation = func.GetAddr(); 8 | 9 | if (pLocation == nullptr) 10 | { 11 | Log::Warn("Disable vignette patch: failed, could not be found"); 12 | return; 13 | } 14 | 15 | DWORD oldProtect = 0; 16 | VirtualProtect(pLocation, 32, PAGE_EXECUTE_WRITECOPY, &oldProtect); 17 | pLocation[0] = 0x32; 18 | pLocation[1] = 0xC0; 19 | pLocation[2] = 0xC3; 20 | VirtualProtect(pLocation, 32, oldProtect, nullptr); 21 | 22 | Log::Info("Disable vignette patch: success"); 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/patches/OptionsPatch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | using TGameOptionInit = void*(void*); 7 | TGameOptionInit* RealGameOptionInit = nullptr; 8 | 9 | void* HookGameOptionInit(GameOption* apThis) 10 | { 11 | auto& gameOptions = GameOptions::GetList(); 12 | auto& options = CET::Get().GetOptions(); 13 | 14 | if (std::ranges::find(gameOptions, apThis) == gameOptions.end()) 15 | { 16 | gameOptions.emplace_back(apThis); 17 | } 18 | 19 | if (options.Developer.DumpGameOptions) 20 | Log::Info(apThis->GetInfo()); 21 | 22 | if (options.Patches.AsyncCompute && strcmp(apThis->pCategory, "Rendering/AsyncCompute") == 0) 23 | { 24 | if (apThis->SetBool(false)) 25 | Log::Info("Disabled hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); 26 | else 27 | Log::Warn("Failed to disable hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); 28 | } 29 | else if (options.Patches.Antialiasing && (strcmp(apThis->pName, "Antialiasing") == 0 || strcmp(apThis->pName, "ScreenSpaceReflection") == 0)) 30 | { 31 | if (apThis->SetBool(false)) 32 | Log::Info("Disabled hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); 33 | else 34 | Log::Warn("Failed to disable hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); 35 | } 36 | 37 | return RealGameOptionInit(apThis); 38 | } 39 | 40 | 41 | void OptionsInitHook() 42 | { 43 | const RED4ext::UniversalRelocPtr func(CyberEngineTweaks::AddressHashes::CPatches_OptionsInit); 44 | uint8_t* pLocation = func.GetAddr(); 45 | 46 | if (pLocation) 47 | { 48 | MH_CreateHook(pLocation, reinterpret_cast(&HookGameOptionInit), reinterpret_cast(&RealGameOptionInit)); 49 | MH_EnableHook(pLocation); 50 | 51 | Log::Info("Hidden options hook: success"); 52 | } 53 | else 54 | Log::Warn("Hidden options hook: failed"); 55 | } 56 | 57 | -------------------------------------------------------------------------------- /src/reverse/Addresses.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace CyberEngineTweaks::AddressHashes 6 | { 7 | #pragma region CBaseInitializationState 8 | constexpr uint32_t CBaseInitializationState_OnTick = 4233370276UL; // red::GameAppBaseInitializationState::OnTick 9 | #pragma endregion 10 | 11 | #pragma region CGame 12 | constexpr uint32_t CGame_Main = 1852772247UL; // CBaseEngine::ProcessBaseLoopFrame 13 | #pragma endregion 14 | 15 | #pragma region CInitializationState 16 | constexpr uint32_t CInitializationState_OnTick = 2447710505UL; // red::GameAppInitializationState::OnTick 17 | #pragma endregion 18 | 19 | #pragma region CPatches 20 | constexpr uint32_t CPatches_BoundaryTeleport = 887623293UL; // game::WorldBoundarySystem::Tick 21 | constexpr uint32_t CPatches_IntroMovie = 4056423627UL; // 22 | constexpr uint32_t CPatches_Vignette = 1592528795UL; // effect::TrackItemVignette::IsValid 23 | constexpr uint32_t CPatches_OptionsInit = 4089777341UL; // Config::IConfigVar::Register 24 | #pragma endregion 25 | 26 | #pragma region CPhotoMode 27 | constexpr uint32_t CPhotoMode_SetRecordID = 4052428712UL; // 28 | #pragma endregion 29 | 30 | #pragma region CRenderGlobal 31 | constexpr uint32_t CRenderGlobal_InstanceOffset = 1239944840UL; // 32 | constexpr uint32_t CRenderGlobal_Resize = 239671909UL; // GpuApi::ResizeBackbuffer 33 | constexpr uint32_t CRenderGlobal_Shutdown = 3192982283UL; // 34 | #pragma endregion 35 | 36 | #pragma region CRenderNode_Present 37 | constexpr uint32_t CRenderNode_Present_DoInternal = 2468877568UL; // GpuApi::Present 38 | #pragma endregion 39 | 40 | #pragma region CRunningState 41 | constexpr uint32_t CRunningState_OnTick = 3592689218UL; // red::GameAppRunningState::OnTick 42 | #pragma endregion 43 | 44 | #pragma region CScript 45 | constexpr uint32_t CScript_RunPureScript = 3791200470UL; // rtti::Function::InternalCall 46 | constexpr uint32_t CScript_AllocateFunction = 160045886UL; // 47 | constexpr uint32_t CScript_Log = 3455393801UL; // 48 | constexpr uint32_t CScript_LogError = 2135235617UL; // 49 | constexpr uint32_t CScript_LogWarning = 3222609133UL; // 50 | constexpr uint32_t CScript_ToStringDEBUG = 3515162577UL; // 51 | constexpr uint32_t CScript_LogChannel = 1663049434UL; // 52 | constexpr uint32_t CScript_LogChannelWarning = 2841780134UL; // 53 | constexpr uint32_t CScript_TDBIDConstructorDerive = 326438016UL; // 54 | constexpr uint32_t CScript_TranslateBytecode = 3442875632UL; // CScriptDataBinder::LoadOpcodes 55 | constexpr uint32_t CScript_TweakDBLoad = 3602585178UL; // game::data::TweakDB::LoadOptimized 56 | #pragma endregion 57 | 58 | #pragma region CShutdownState 59 | constexpr uint32_t CShutdownState_OnTick = 4069332669UL; // red::GameAppShutdownState::OnTick 60 | #pragma endregion 61 | 62 | #pragma region InputSystemWin32Base 63 | constexpr uint32_t InputSystemWin32Base_ForceCursor = 2130646213UL; // input::InputSystemWin32Base::ForceCursor 64 | #pragma endregion 65 | 66 | #pragma region gameIGameSystem 67 | constexpr uint32_t gameIGameSystem_Initialize = 385618721UL; // -> should probably be 3114931869 (spawn::Set::Initialize) but that implies we do something weird 68 | // overall with this func atm The above would require CET changes as that one wants game instance to be passed at a2+80 69 | constexpr uint32_t gameIGameSystem_UnInitialize = 3313306514UL; // spawn::Set::Deinitialize 70 | constexpr uint32_t gameIGameSystem_Spawn = 2509382878UL; // spawn::Set::SpawnObject 71 | constexpr uint32_t gameIGameSystem_Despawn = 3168866665UL; // spawn::Set::DespawnObject 72 | constexpr uint32_t gameIGameSystem_SpawnCallback = 2840271332UL; // world::RuntimeEntityRegistry::RegisterEntity 73 | #pragma endregion 74 | 75 | #pragma region PlayerSystem 76 | constexpr uint32_t PlayerSystem_OnPlayerSpawned = 2050111212UL; // cp::PlayerSystem::OnPlayerMainObjectSpawned 77 | #pragma endregion 78 | } // namespace CyberEngineTweaks::AddressHashes -------------------------------------------------------------------------------- /src/reverse/Array.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template struct Array 4 | { 5 | T* entries{nullptr}; 6 | uint32_t capacity{0}; 7 | uint32_t count{0}; 8 | }; -------------------------------------------------------------------------------- /src/reverse/BasicTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Vector3 4 | { 5 | Vector3(float aX = 0.f, float aY = 0.f, float aZ = 0.f) 6 | : x(aX) 7 | , y(aY) 8 | , z(aZ) 9 | { 10 | } 11 | 12 | float x; 13 | float y; 14 | float z; 15 | 16 | std::string ToString() const noexcept; 17 | 18 | bool operator==(const Vector3& acRhs) const noexcept; 19 | }; 20 | 21 | struct Vector4 22 | { 23 | Vector4(float aX = 0.f, float aY = 0.f, float aZ = 0.f, float aW = 0.f) 24 | : x(aX) 25 | , y(aY) 26 | , z(aZ) 27 | , w(aW) 28 | { 29 | } 30 | 31 | float x; 32 | float y; 33 | float z; 34 | float w; 35 | 36 | std::string ToString() const noexcept; 37 | 38 | bool operator==(const Vector4& acRhs) const noexcept; 39 | }; 40 | 41 | struct EulerAngles 42 | { 43 | EulerAngles(float aRoll = 0.f, float aPitch = 0.f, float aYaw = 0.f) 44 | : roll(aRoll) 45 | , pitch(aPitch) 46 | , yaw(aYaw) 47 | { 48 | } 49 | 50 | float roll; 51 | float pitch; 52 | float yaw; 53 | 54 | std::string ToString() const noexcept; 55 | 56 | bool operator==(const EulerAngles& acRhs) const noexcept; 57 | }; 58 | 59 | struct Quaternion 60 | { 61 | Quaternion(float aI = 0.f, float aJ = 0.f, float aK = 0.f, float aR = 0.f) 62 | : i(aI) 63 | , j(aJ) 64 | , k(aK) 65 | , r(aR) 66 | { 67 | } 68 | 69 | float i; 70 | float j; 71 | float k; 72 | float r; 73 | 74 | std::string ToString() const noexcept; 75 | 76 | bool operator==(const Quaternion& acRhs) const noexcept; 77 | }; 78 | 79 | struct CName 80 | { 81 | CName(uint64_t aHash = 0) 82 | : hash(aHash) 83 | { 84 | } 85 | 86 | CName(uint32_t aHashLo, uint32_t aHashHi) 87 | : hash_lo(aHashLo) 88 | , hash_hi(aHashHi) 89 | { 90 | } 91 | 92 | CName(const std::string& aName) 93 | { 94 | if (aName != "None") 95 | hash = RED4ext::FNV1a64(aName.c_str()); 96 | else 97 | hash = 0; 98 | } 99 | 100 | union 101 | { 102 | uint64_t hash{0}; 103 | struct 104 | { 105 | uint32_t hash_lo; 106 | uint32_t hash_hi; 107 | }; 108 | }; 109 | 110 | std::string AsString() const noexcept; 111 | std::string ToString() const noexcept; 112 | 113 | bool operator==(const CName& acRhs) const noexcept; 114 | 115 | static void Add(const std::string& aName); 116 | }; 117 | 118 | #pragma pack(push, 1) 119 | struct TweakDBID 120 | { 121 | TweakDBID() = default; 122 | 123 | TweakDBID(uint32_t aNameHash, uint8_t aNameLength) 124 | { 125 | this->name_hash = aNameHash; 126 | this->name_length = aNameLength; 127 | this->unk5 = 0; 128 | this->unk7 = 0; 129 | } 130 | 131 | TweakDBID(uint64_t aValue) { this->value = aValue; } 132 | 133 | TweakDBID(const std::string_view aName) 134 | { 135 | name_hash = RED4ext::CRC32(aName.data(), 0); 136 | name_length = static_cast(aName.size()); 137 | unk5 = unk7 = 0; 138 | } 139 | 140 | TweakDBID(const TweakDBID& aBase, const std::string_view aName) 141 | { 142 | name_hash = RED4ext::CRC32(aName.data(), aBase.name_hash); 143 | name_length = static_cast(aName.size() + aBase.name_length); 144 | unk5 = unk7 = 0; 145 | } 146 | 147 | std::string AsString() const noexcept; 148 | std::string ToString() const noexcept; 149 | 150 | bool operator==(const TweakDBID& acRhs) const noexcept; 151 | TweakDBID operator+(const std::string_view acName) const noexcept; 152 | 153 | union 154 | { 155 | uint64_t value{0}; 156 | struct 157 | { 158 | uint32_t name_hash; 159 | uint8_t name_length; 160 | uint16_t unk5; 161 | uint8_t unk7; 162 | }; 163 | }; 164 | }; 165 | 166 | static_assert(sizeof(TweakDBID) == 8); 167 | 168 | struct ItemID 169 | { 170 | ItemID() = default; 171 | ItemID(const TweakDBID& aId, uint32_t aRngSeed = 2, uint16_t aUnknown = 0, uint8_t aMaybeType = 0) 172 | : id(aId) 173 | , rng_seed(aRngSeed) 174 | , unknown(aUnknown) 175 | , maybe_type(aMaybeType) 176 | , pad(0) 177 | { 178 | } 179 | 180 | std::string ToString() const noexcept; 181 | 182 | bool operator==(const ItemID& acRhs) const noexcept; 183 | 184 | TweakDBID id; 185 | uint32_t rng_seed{2}; 186 | uint16_t unknown{0}; 187 | uint8_t maybe_type{0}; 188 | uint8_t pad{0}; 189 | }; 190 | 191 | static_assert(sizeof(ItemID) == 0x10); 192 | 193 | struct Variant 194 | { 195 | Variant() noexcept = default; 196 | Variant(const RED4ext::CBaseRTTIType* aType); 197 | Variant(const RED4ext::CBaseRTTIType* aType, const RED4ext::ScriptInstance aData); 198 | Variant(const RED4ext::CName& aTypeName, const RED4ext::ScriptInstance aData); 199 | Variant(const RED4ext::CStackType& aStack); 200 | Variant(const Variant& aOther); 201 | ~Variant(); 202 | 203 | bool IsEmpty() const noexcept; 204 | bool IsInlined() const noexcept; 205 | 206 | RED4ext::CBaseRTTIType* GetType() const noexcept; 207 | RED4ext::ScriptInstance GetDataPtr() const noexcept; 208 | 209 | bool Init(const RED4ext::CBaseRTTIType* aType); 210 | bool Fill(const RED4ext::CBaseRTTIType* aType, const RED4ext::ScriptInstance aData); 211 | bool Extract(RED4ext::ScriptInstance aBuffer) const; 212 | void Free(); 213 | 214 | inline static bool CanBeInlined(const RED4ext::CBaseRTTIType* aType) noexcept; 215 | 216 | enum 217 | { 218 | kInlineSize = 16, 219 | kInlineAlignment = 8 220 | }; 221 | 222 | enum : uintptr_t 223 | { 224 | kInlineFlag = 1, 225 | kTypeMask = ~kInlineFlag, 226 | }; 227 | 228 | const RED4ext::CBaseRTTIType* type{nullptr}; 229 | union 230 | { 231 | mutable uint8_t inlined[kInlineSize]{0}; 232 | RED4ext::ScriptInstance instance; 233 | }; 234 | }; 235 | 236 | static_assert(sizeof(Variant) == 0x18); 237 | 238 | struct CRUID 239 | { 240 | CRUID(uint64_t aHash = 0) 241 | : hash(aHash) 242 | { 243 | } 244 | 245 | uint64_t hash; 246 | 247 | std::string ToString() const noexcept; 248 | 249 | bool operator==(const CRUID& acRhs) const noexcept; 250 | }; 251 | 252 | static_assert(sizeof(CRUID) == 0x8); 253 | 254 | struct gamedataLocKeyWrapper 255 | { 256 | gamedataLocKeyWrapper(uint64_t aHash = 0) 257 | : hash(aHash) 258 | { 259 | } 260 | 261 | uint64_t hash; 262 | 263 | std::string ToString() const noexcept; 264 | 265 | bool operator==(const gamedataLocKeyWrapper& acRhs) const noexcept; 266 | }; 267 | 268 | static_assert(sizeof(gamedataLocKeyWrapper) == 0x8); 269 | 270 | struct alignas(8) ScriptGameInstance 271 | { 272 | RED4ext::GameInstance* gameInstance; 273 | int8_t unk8; 274 | int64_t unk10; 275 | }; 276 | 277 | static_assert(sizeof(ScriptGameInstance) == 0x18); 278 | 279 | #pragma pack(pop) -------------------------------------------------------------------------------- /src/reverse/ClassReference.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ClassReference.h" 4 | #include "CET.h" 5 | 6 | ClassReference::ClassReference(const TiltedPhoques::Locked& aView, RED4ext::CBaseRTTIType* apClass, RED4ext::ScriptInstance apInstance) 7 | : ClassType(aView, apClass) 8 | { 9 | m_pInstance = apClass->GetAllocator()->AllocAligned(apClass->GetSize(), apClass->GetAlignment()).memory; 10 | apClass->Construct(m_pInstance); 11 | apClass->Assign(m_pInstance, apInstance); 12 | } 13 | 14 | ClassReference::ClassReference(ClassReference&& aOther) noexcept 15 | : ClassType(aOther) 16 | , m_pInstance(aOther.m_pInstance) 17 | { 18 | aOther.m_pInstance = nullptr; 19 | } 20 | 21 | ClassReference::~ClassReference() 22 | { 23 | if (m_pInstance && CET::IsRunning()) 24 | { 25 | m_pType->Destruct(m_pInstance); 26 | m_pType->GetAllocator()->Free(m_pInstance); 27 | } 28 | } 29 | 30 | RED4ext::ScriptInstance ClassReference::GetHandle() const 31 | { 32 | return m_pInstance; 33 | } 34 | 35 | RED4ext::ScriptInstance ClassReference::GetValuePtr() const 36 | { 37 | return GetHandle(); 38 | } 39 | -------------------------------------------------------------------------------- /src/reverse/ClassReference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Type.h" 4 | 5 | struct ClassReference : ClassType 6 | { 7 | ClassReference(const TiltedPhoques::Locked& aView, RED4ext::CBaseRTTIType* apClass, RED4ext::ScriptInstance apInstance); 8 | ClassReference(ClassReference&& aOther) noexcept; 9 | ~ClassReference() override; 10 | 11 | RED4ext::ScriptInstance GetHandle() const override; 12 | RED4ext::ScriptInstance GetValuePtr() const override; 13 | 14 | private: 15 | RED4ext::ScriptInstance m_pInstance; 16 | }; 17 | -------------------------------------------------------------------------------- /src/reverse/ClassStatic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ClassStatic.h" 4 | 5 | #include "RTTIHelper.h" 6 | #include "StrongReference.h" 7 | #include "Utils.h" 8 | 9 | ClassStatic::ClassStatic(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass) 10 | : ClassType(aView, apClass) 11 | { 12 | } 13 | 14 | ClassStatic::~ClassStatic() = default; 15 | 16 | sol::function ClassStatic::GetFactory() 17 | { 18 | if (!m_factory) 19 | { 20 | auto lockedState = m_lua.Lock(); 21 | auto& luaState = lockedState.Get(); 22 | 23 | m_factory = MakeSolFunction(luaState, [this](sol::optional aProps) { return RTTIHelper::Get().NewHandle(m_pType, aProps); }); 24 | } 25 | 26 | return m_factory; 27 | } 28 | -------------------------------------------------------------------------------- /src/reverse/ClassStatic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Type.h" 4 | 5 | struct ClassStatic : ClassType 6 | { 7 | ClassStatic(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass); 8 | ~ClassStatic() override; 9 | 10 | sol::function GetFactory(); 11 | 12 | private: 13 | sol::function m_factory; 14 | }; 15 | -------------------------------------------------------------------------------- /src/reverse/Converter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Converter.h" 4 | 5 | auto s_metaVisitor = 6 | [](auto... args) 7 | { 8 | return [=](auto&& f) mutable 9 | { 10 | f(args...); 11 | }; 12 | }(LuaRED(), LuaRED(), LuaRED(), LuaRED(), LuaRED(), LuaRED(), 13 | LuaRED(), LuaRED(), LuaRED(), LuaRED(), LuaRED(), LuaRED(), 14 | LuaRED(), LuaRED(), LuaRED(), LuaRED(), LuaRED(), 15 | LuaRED(), LuaRED(), CNameConverter(), TweakDBIDConverter(), EnumConverter(), ClassConverter(), 16 | RawConverter() // Should always be last resort 17 | ); 18 | 19 | size_t Converter::Size(RED4ext::CBaseRTTIType* apRtti) 20 | { 21 | size_t s = 0; 22 | 23 | auto initSize = [&](auto& x) 24 | { 25 | if (x.Is(apRtti)) 26 | { 27 | s = x.Size(); 28 | return true; 29 | } 30 | return false; 31 | }; 32 | 33 | auto f = [initSize](auto&&... xs) 34 | { 35 | (... && !initSize(xs)); 36 | }; 37 | 38 | s_metaVisitor(f); 39 | 40 | return s; 41 | } 42 | 43 | sol::object Converter::ToLua(RED4ext::CStackType& aResult, TiltedPhoques::Locked& aLua) 44 | { 45 | sol::object o = sol::nil; 46 | auto initLuaObject = [&](auto& x) 47 | { 48 | if (x.Is(aResult.type)) 49 | { 50 | o = x.ToLua(aResult, aLua); 51 | return true; 52 | } 53 | return false; 54 | }; 55 | 56 | auto f = [initLuaObject](auto&&... xs) 57 | { 58 | (... && !initLuaObject(xs)); 59 | }; 60 | 61 | s_metaVisitor(f); 62 | 63 | return o; 64 | } 65 | 66 | RED4ext::CStackType Converter::ToRED(sol::object aObject, RED4ext::CBaseRTTIType* apRtti, TiltedPhoques::Allocator* apAllocator) 67 | { 68 | RED4ext::CStackType r; 69 | auto initStackType = [&](auto& x) 70 | { 71 | if (x.Is(apRtti)) 72 | { 73 | r = x.ToRED(aObject, apRtti, apAllocator); 74 | return true; 75 | } 76 | return false; 77 | }; 78 | 79 | auto f = [initStackType](auto&&... xs) 80 | { 81 | (... && !initStackType(xs)); 82 | }; 83 | 84 | s_metaVisitor(f); 85 | 86 | return r; 87 | } 88 | 89 | void Converter::ToRED(sol::object aObject, RED4ext::CStackType* apType) 90 | { 91 | auto initStackType = [&](auto& x) 92 | { 93 | if (x.Is(apType->type)) 94 | { 95 | x.ToRED(aObject, apType); 96 | return true; 97 | } 98 | return false; 99 | }; 100 | 101 | auto f = [initStackType](auto&&... xs) 102 | { 103 | (... && !initStackType(xs)); 104 | }; 105 | 106 | s_metaVisitor(f); 107 | } 108 | -------------------------------------------------------------------------------- /src/reverse/Enum.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Enum.h" 4 | 5 | Enum::Enum(const RED4ext::CStackType& aStackType) 6 | { 7 | Get(aStackType); 8 | } 9 | 10 | Enum::Enum(const std::string& acTypeName, const std::string& acValue) 11 | { 12 | const auto* cpType = RED4ext::CRTTISystem::Get()->GetEnum(RED4ext::FNV1a64(acTypeName.c_str())); 13 | if (cpType) 14 | { 15 | m_cpType = cpType; 16 | SetValueByName(acValue); 17 | } 18 | } 19 | 20 | Enum::Enum(const std::string& acTypeName, uint32_t aValue) 21 | { 22 | const auto* cpType = RED4ext::CRTTISystem::Get()->GetEnum(RED4ext::FNV1a64(acTypeName.c_str())); 23 | if (cpType) 24 | { 25 | m_cpType = cpType; 26 | SetValueSafe(aValue); 27 | } 28 | } 29 | 30 | Enum::Enum(const RED4ext::CEnum* acpType, const std::string& acValue) 31 | : m_cpType(acpType) 32 | { 33 | SetValueByName(acValue); 34 | } 35 | 36 | Enum::Enum(const RED4ext::CEnum* acpType, uint32_t aValue) 37 | : m_cpType(acpType) 38 | { 39 | SetValueSafe(aValue); 40 | } 41 | 42 | void Enum::SetValueSafe(uint64_t aValue) 43 | { 44 | for (uint32_t i = 0; i < m_cpType->valueList.size; ++i) 45 | { 46 | if (m_cpType->valueList[i] == static_cast(aValue)) 47 | { 48 | m_value = aValue; 49 | break; 50 | } 51 | } 52 | } 53 | 54 | void Enum::Get(const RED4ext::CStackType& acStackType) noexcept 55 | { 56 | m_cpType = static_cast(acStackType.type); 57 | switch (acStackType.type->GetSize()) 58 | { 59 | case sizeof(uint8_t): m_value = *static_cast(acStackType.value); break; 60 | case sizeof(uint16_t): m_value = *static_cast(acStackType.value); break; 61 | case sizeof(uint32_t): m_value = *static_cast(acStackType.value); break; 62 | case sizeof(uint64_t): m_value = *static_cast(acStackType.value); break; 63 | } 64 | } 65 | 66 | void Enum::Set(RED4ext::CStackType& aStackType, TiltedPhoques::Allocator* apAllocator) const noexcept 67 | { 68 | aStackType.type = const_cast(m_cpType); // Sad cast 69 | switch (m_cpType->GetSize()) 70 | { 71 | case sizeof(uint8_t): aStackType.value = apAllocator->New(static_cast(m_value)); break; 72 | case sizeof(uint16_t): aStackType.value = apAllocator->New(static_cast(m_value)); break; 73 | case sizeof(uint32_t): aStackType.value = apAllocator->New(static_cast(m_value)); break; 74 | case sizeof(uint64_t): aStackType.value = apAllocator->New(static_cast(m_value)); break; 75 | } 76 | } 77 | 78 | void Enum::Set(RED4ext::CStackType& acStackType) const noexcept 79 | { 80 | switch (m_cpType->GetSize()) 81 | { 82 | case sizeof(uint8_t): *static_cast(acStackType.value) = static_cast(m_value); break; 83 | case sizeof(uint16_t): *static_cast(acStackType.value) = static_cast(m_value); break; 84 | case sizeof(uint32_t): *static_cast(acStackType.value) = static_cast(m_value); break; 85 | case sizeof(uint64_t): *static_cast(acStackType.value) = static_cast(m_value); break; 86 | } 87 | } 88 | 89 | std::string Enum::GetValueName() const 90 | { 91 | if (!m_cpType) 92 | return ""; 93 | 94 | for (uint32_t i = 0; i < m_cpType->valueList.size; ++i) 95 | { 96 | if (m_cpType->valueList[i] == static_cast(m_value)) 97 | { 98 | return m_cpType->hashList[i].ToString(); 99 | } 100 | } 101 | 102 | return ""; 103 | } 104 | 105 | void Enum::SetValueByName(const std::string& acValue) 106 | { 107 | if (!m_cpType) 108 | return; 109 | 110 | const RED4ext::CName cValueName(acValue.c_str()); 111 | 112 | for (uint32_t i = 0; i < m_cpType->hashList.size; ++i) 113 | { 114 | if (m_cpType->hashList[i] == cValueName) 115 | { 116 | m_value = m_cpType->valueList[i]; 117 | break; 118 | } 119 | } 120 | } 121 | 122 | std::string Enum::ToString() const 123 | { 124 | if (m_cpType) 125 | { 126 | const RED4ext::CName name = m_cpType->GetName(); 127 | return name.ToString() + std::string(" : ") + GetValueName() + std::string(" (") + std::to_string(m_value) + std::string(")"); 128 | } 129 | 130 | return "Invalid enum"; 131 | } 132 | 133 | bool Enum::operator==(const Enum& acRhs) const noexcept 134 | { 135 | if (!m_cpType || !acRhs.m_cpType) 136 | return false; 137 | 138 | const RED4ext::CName name = m_cpType->GetName(); 139 | const RED4ext::CName nameRhs = acRhs.m_cpType->GetName(); 140 | 141 | return name == nameRhs && m_value == acRhs.m_value; 142 | } 143 | 144 | const RED4ext::CEnum* Enum::GetType() const 145 | { 146 | return m_cpType; 147 | } 148 | 149 | const void* Enum::GetValuePtr() const 150 | { 151 | return &m_value; 152 | } 153 | -------------------------------------------------------------------------------- /src/reverse/Enum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Enum 4 | { 5 | Enum(const RED4ext::CEnum*, const std::string& acValue); 6 | Enum(const RED4ext::CEnum*, uint32_t aValue); 7 | Enum(const RED4ext::CStackType& acStackType); 8 | Enum(const std::string& acTypeName, const std::string& acValue); 9 | Enum(const std::string& acTypeName, uint32_t aValue); 10 | 11 | void Get(const RED4ext::CStackType& acStackType) noexcept; 12 | void Set(RED4ext::CStackType& acStackType, TiltedPhoques::Allocator* apAllocator) const noexcept; 13 | void Set(RED4ext::CStackType& acStackType) const noexcept; 14 | 15 | // Returns the enum value by name 16 | std::string GetValueName() const; 17 | 18 | // Sets value by name in the enum list 19 | void SetValueByName(const std::string& acValue); 20 | 21 | // Sets by value verified against enum list 22 | void SetValueSafe(uint64_t aValue); 23 | 24 | std::string ToString() const; 25 | 26 | bool operator==(const Enum& acRhs) const noexcept; 27 | 28 | const RED4ext::CEnum* GetType() const; 29 | const void* GetValuePtr() const; 30 | 31 | protected: 32 | friend struct Scripting; 33 | 34 | const RED4ext::CEnum* m_cpType{nullptr}; 35 | uint64_t m_value{0}; 36 | }; 37 | -------------------------------------------------------------------------------- /src/reverse/EnumStatic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "EnumStatic.h" 4 | #include "Enum.h" 5 | 6 | #include "scripting/Scripting.h" 7 | 8 | EnumStatic::EnumStatic(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass) 9 | : ClassType(aView, apClass) 10 | { 11 | } 12 | 13 | EnumStatic::~EnumStatic() = default; 14 | 15 | sol::object EnumStatic::Index_Impl(const std::string& acName, sol::this_environment aThisEnv) 16 | { 17 | // TODO - this should not use Type:: probably but ClassType 18 | auto result = Type::Index_Impl(acName, aThisEnv); 19 | 20 | if (result != sol::nil) 21 | return result; 22 | 23 | const auto* cpEnum = static_cast(m_pType); 24 | 25 | if (!cpEnum) 26 | return sol::nil; 27 | 28 | auto lockedState = m_lua.Lock(); 29 | const auto& cLuaState = lockedState.Get(); 30 | 31 | // TODO - this should not use Type:: probably but ClassType 32 | return Type::NewIndex_Impl(acName, make_object(cLuaState, Enum(cpEnum, acName))); 33 | } 34 | -------------------------------------------------------------------------------- /src/reverse/EnumStatic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Type.h" 4 | 5 | struct EnumStatic : ClassType 6 | { 7 | EnumStatic(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass); 8 | ~EnumStatic() override; 9 | 10 | sol::object Index_Impl(const std::string& acName, sol::this_environment aThisEnv) override; 11 | }; 12 | -------------------------------------------------------------------------------- /src/reverse/LuaRED.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/Meta.h" 4 | #include "Utils.h" 5 | 6 | template struct LuaRED 7 | { 8 | static constexpr char const* Name = REDName; 9 | 10 | sol::object ToLua(RED4ext::CStackType& aResult, TiltedPhoques::Locked& aLua) 11 | { 12 | if constexpr (std::is_integral_v && sizeof(T) == sizeof(uint64_t)) 13 | { 14 | constexpr auto format{std::is_signed_v ? "return {}ll" : "return {}ull"}; 15 | auto res{aLua.Get().script(fmt::format(format, *static_cast(aResult.value)))}; 16 | assert(res.valid()); 17 | return res.get(); 18 | } 19 | else 20 | { 21 | return make_object(aLua.Get(), *static_cast(aResult.value)); 22 | } 23 | } 24 | 25 | RED4ext::CStackType ToRED(sol::object aObject, RED4ext::CBaseRTTIType*, TiltedPhoques::Allocator* apAllocator) 26 | { 27 | RED4ext::CStackType result; 28 | result.type = m_pRtti; 29 | if (aObject == sol::nil) 30 | { 31 | result.value = apAllocator->New(); 32 | } 33 | else if constexpr (std::is_integral_v && sizeof(T) == sizeof(uint64_t)) 34 | { 35 | if (aObject.get_type() == sol::type::number) 36 | { 37 | sol::state_view v(aObject.lua_state()); 38 | double value = v["tonumber"](aObject); 39 | result.value = apAllocator->New(static_cast(value)); 40 | } 41 | else if (IsLuaCData(aObject)) 42 | { 43 | sol::state_view v(aObject.lua_state()); 44 | std::string str = v["tostring"](aObject); 45 | if constexpr (std::is_signed_v) 46 | result.value = apAllocator->New(std::stoll(str)); 47 | else 48 | result.value = apAllocator->New(std::stoull(str)); 49 | } 50 | } 51 | else if constexpr (std::is_same_v) 52 | { 53 | if (aObject.get_type() == sol::type::boolean) 54 | result.value = apAllocator->New(aObject.as()); 55 | } 56 | else if constexpr (std::is_arithmetic_v) 57 | { 58 | if (aObject.get_type() == sol::type::number) 59 | { 60 | if (aObject.is()) 61 | { 62 | result.value = apAllocator->New(aObject.as()); 63 | } 64 | } 65 | else if (IsLuaCData(aObject)) 66 | { 67 | sol::state_view v(aObject.lua_state()); 68 | double value = v["tonumber"](aObject); 69 | result.value = apAllocator->New(static_cast(value)); 70 | } 71 | } 72 | else if (aObject.is()) 73 | { 74 | result.value = apAllocator->New(aObject.as()); 75 | } 76 | 77 | return result; 78 | } 79 | 80 | void ToRED(sol::object aObject, RED4ext::CStackType* apType) 81 | { 82 | if (aObject == sol::nil) 83 | { 84 | *reinterpret_cast(apType->value) = T{}; 85 | } 86 | else if constexpr (std::is_integral_v && sizeof(T) == sizeof(uint64_t)) 87 | { 88 | if (aObject.get_type() == sol::type::number) 89 | { 90 | sol::state_view v(aObject.lua_state()); 91 | double value = v["tonumber"](aObject); 92 | *reinterpret_cast(apType->value) = static_cast(value); 93 | } 94 | else if (IsLuaCData(aObject)) 95 | { 96 | sol::state_view v(aObject.lua_state()); 97 | std::string str = v["tostring"](aObject); 98 | if constexpr (std::is_signed_v) 99 | *reinterpret_cast(apType->value) = std::stoll(str); 100 | else 101 | *reinterpret_cast(apType->value) = std::stoull(str); 102 | } 103 | } 104 | else if constexpr (std::is_same_v) 105 | { 106 | if (aObject.get_type() == sol::type::boolean) 107 | *reinterpret_cast(apType->value) = aObject.as(); 108 | } 109 | else if constexpr (std::is_arithmetic_v) 110 | { 111 | if (aObject.get_type() == sol::type::number) 112 | { 113 | *reinterpret_cast(apType->value) = aObject.as(); 114 | } 115 | else if (IsLuaCData(aObject)) 116 | { 117 | sol::state_view v(aObject.lua_state()); 118 | double value = v["tonumber"](aObject); 119 | *reinterpret_cast(apType->value) = static_cast(value); 120 | } 121 | } 122 | else if (aObject.is()) 123 | { 124 | *reinterpret_cast(apType->value) = aObject.as(); 125 | } 126 | } 127 | 128 | size_t Size() const noexcept { return sizeof(T); } 129 | 130 | bool Is(RED4ext::CBaseRTTIType* apRtti) const 131 | { 132 | if (!Resolve()) 133 | return false; 134 | 135 | return apRtti == m_pRtti; 136 | } 137 | 138 | protected: 139 | bool Resolve() const 140 | { 141 | if (m_pRtti) 142 | return true; 143 | 144 | auto* pRtti = RED4ext::CRTTISystem::Get(); 145 | m_pRtti = pRtti->GetType(RED4ext::FNV1a64(Name)); 146 | 147 | return m_pRtti != nullptr; 148 | } 149 | 150 | mutable RED4ext::CBaseRTTIType* m_pRtti{nullptr}; 151 | }; 152 | -------------------------------------------------------------------------------- /src/reverse/NativeProxy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BasicTypes.h" 4 | #include "WeakReference.h" 5 | 6 | #include 7 | #include 8 | 9 | struct NativeProxyType : RED4ext::CClass 10 | { 11 | NativeProxyType(RED4ext::CClass* apBase); 12 | 13 | RED4ext::Handle CreateHandle(); 14 | 15 | const bool IsEqual(const RED4ext::ScriptInstance aLhs, const RED4ext::ScriptInstance aRhs, uint32_t a3) final; // 48 16 | void Assign(RED4ext::ScriptInstance aLhs, const RED4ext::ScriptInstance aRhs) const final; // 50 17 | void ConstructCls(RED4ext::ScriptInstance aMemory) const final; // D8 18 | void DestructCls(RED4ext::ScriptInstance aMemory) const final; // E0 19 | void* AllocMemory() const final; // E8 20 | 21 | static NativeProxyType* ResolveProxy(const std::string& aName); 22 | static RED4ext::CName AddSignature(NativeProxyType* apProxy, const std::string& aName, const sol::table& aArgs); 23 | static std::string FormatTypeName(RED4ext::CRTTISystem* apRtti, RED4ext::CBaseRTTIType* apType); 24 | }; 25 | 26 | struct NativeProxy 27 | { 28 | using LockableState = TiltedPhoques::Lockable::Ref; 29 | 30 | NativeProxy(NativeProxy::LockableState aLua, sol::environment aEnvironment, const sol::object& aSpec); 31 | NativeProxy(NativeProxy::LockableState aLua, sol::environment aEnvironment, const std::string& aInterface, const sol::object& aSpec); 32 | NativeProxy(NativeProxy&& aOther) noexcept; 33 | 34 | [[nodiscard]] WeakReference GetTarget(/*sol::this_state aState*/) const; 35 | [[nodiscard]] CName GetFunction(const std::string& aName) const; 36 | [[nodiscard]] CName GetDefaultFunction() const; 37 | 38 | bool AddFunction(NativeProxyType* apProxy, const std::string& aName, const sol::object& aSpec); 39 | 40 | static void Callback(RED4ext::IScriptable* apSelf, RED4ext::CStackFrame* apFrame, void* apOut, int64_t); 41 | 42 | RED4ext::Handle m_target; 43 | TiltedPhoques::Map m_functions; 44 | TiltedPhoques::Map m_signatures; 45 | sol::environment m_environment; 46 | LockableState m_lua; 47 | }; 48 | -------------------------------------------------------------------------------- /src/reverse/RTTIExtender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class RTTIExtender 4 | { 5 | static void AddFunctionalTests(); 6 | static void AddEntitySpawner(); 7 | 8 | public: 9 | static void InitializeTypes(); 10 | static void InitializeSingletons(); 11 | }; 12 | -------------------------------------------------------------------------------- /src/reverse/RTTIHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BasicTypes.h" 4 | 5 | struct LuaSandbox; 6 | struct RTTIHelper 7 | { 8 | using LockableState = TiltedPhoques::Lockable::Ref; 9 | using RedFunctionMap = TiltedPhoques::Map>; 10 | using LuaFunctionMap = TiltedPhoques::Map>; 11 | 12 | ~RTTIHelper() = default; 13 | 14 | void AddFunctionAlias(const std::string& acAliasFuncName, const std::string& acOrigClassName, const std::string& acOrigFuncName); 15 | void AddFunctionAlias(const std::string& acAliasClassName, const std::string& acAliasFuncName, const std::string& acOrigClassName, const std::string& acOrigFuncName); 16 | 17 | sol::function ResolveFunction(const std::string& acFuncName); 18 | sol::function ResolveFunction(RED4ext::CClass* apClass, const std::string& acFuncName, bool aIsMember); 19 | 20 | RED4ext::IScriptable* ResolveHandle(RED4ext::CBaseFunction* apFunc, sol::variadic_args& aArgs, uint64_t& aArgOffset) const; 21 | sol::variadic_results ExecuteFunction( 22 | RED4ext::CBaseFunction* apFunc, RED4ext::IScriptable* apContext, sol::variadic_args aLuaArgs, uint64_t aLuaArgOffset, std::string& aErrorMessage, 23 | bool aAllowNull = false) const; 24 | 25 | RED4ext::ScriptInstance NewInstance(RED4ext::CBaseRTTIType* apType, sol::optional aProps, TiltedPhoques::Allocator* apAllocator) const; 26 | sol::object NewInstance(RED4ext::CBaseRTTIType* apType, sol::optional aProps) const; 27 | sol::object NewHandle(RED4ext::CBaseRTTIType* apType, sol::optional aProps) const; 28 | 29 | sol::object GetProperty(RED4ext::CClass* apClass, RED4ext::ScriptInstance apHandle, const std::string& acPropName, bool& aSuccess) const; 30 | void SetProperty(RED4ext::CClass* apClass, RED4ext::ScriptInstance apHandle, const std::string& acPropName, sol::object aPropValue, bool& aSuccess) const; 31 | void SetProperties(RED4ext::CClass* apClass, RED4ext::ScriptInstance apHandle, sol::optional aProps) const; 32 | 33 | RED4ext::CBaseFunction* FindFunction(const uint64_t acFullNameHash) const; 34 | RED4ext::CBaseFunction* FindFunction(RED4ext::CClass* apClass, const uint64_t acFullNameHash) const; 35 | std::map FindFunctions(const uint64_t acShortNameHash) const; 36 | std::map FindFunctions(RED4ext::CClass* apClass, const uint64_t acShortNameHash, bool aIsMember) const; 37 | 38 | static void Initialize(const LockableState& acpLua, LuaSandbox& apSandbox); 39 | static void Shutdown(); 40 | static RTTIHelper& Get(); 41 | 42 | private: 43 | RTTIHelper(const LockableState& acLua, LuaSandbox& apSandbox); 44 | 45 | void InitializeRTTI(); 46 | void ParseGlobalStatics(); 47 | 48 | bool IsFunctionAlias(RED4ext::CBaseFunction* apFunc); 49 | 50 | sol::function GetResolvedFunction(const uint64_t acFuncHash) const; 51 | sol::function GetResolvedFunction(const uint64_t acClassHash, const uint64_t acFuncHash, bool aIsMember) const; 52 | void AddResolvedFunction(const uint64_t acFuncHash, sol::function& acFunc); 53 | void AddResolvedFunction(const uint64_t acClassHash, const uint64_t acFuncHash, sol::function& acFunc, bool aIsMember); 54 | 55 | sol::function MakeInvokableFunction(RED4ext::CBaseFunction* apFunc); 56 | sol::function MakeInvokableOverload(std::map aOverloadedFuncs) const; 57 | 58 | RED4ext::ScriptInstance NewPlaceholder(RED4ext::CBaseRTTIType* apType, TiltedPhoques::Allocator* apAllocator) const; 59 | bool IsClassReferenceType(RED4ext::CClass* apClass) const; 60 | void FreeInstance(RED4ext::CStackType& aStackType, bool aOwnValue, bool aNewValue, TiltedPhoques::Allocator* apAllocator) const; 61 | void FreeInstance(RED4ext::CBaseRTTIType* apType, void* apValue, bool aOwnValue, bool aNewValue, TiltedPhoques::Allocator* apAllocator) const; 62 | 63 | bool ExecuteFunction(RED4ext::CBaseFunction* apFunc, RED4ext::IScriptable* apContext, TiltedPhoques::Vector& aArgs, RED4ext::CStackType& aResult) const; 64 | 65 | enum 66 | { 67 | kGlobalHash = 0, 68 | kStaticScope = 0, 69 | kMemberScope = 1, 70 | kTrackedOverloadCalls = 256, 71 | }; 72 | 73 | struct Overload 74 | { 75 | RED4ext::CBaseFunction* func; 76 | std::string lastError; 77 | uint32_t totalCalls; 78 | }; 79 | 80 | LockableState m_lua; 81 | RED4ext::CRTTISystem* m_pRtti{nullptr}; 82 | RED4ext::CClass* m_pGameInstanceType{nullptr}; 83 | RedFunctionMap m_extendedFunctions; 84 | LuaFunctionMap m_resolvedFunctions[2]; 85 | LuaSandbox& m_sandbox; 86 | }; 87 | -------------------------------------------------------------------------------- /src/reverse/RTTILocator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "RTTILocator.h" 4 | 5 | RTTILocator::RTTILocator(RED4ext::CName aName) 6 | : m_name(aName) 7 | { 8 | } 9 | 10 | RTTILocator::operator RED4ext::CBaseRTTIType*() 11 | { 12 | if (m_pRtti) 13 | return m_pRtti; 14 | 15 | auto* pRtti = RED4ext::CRTTISystem::Get(); 16 | if (pRtti == nullptr) 17 | { 18 | return nullptr; 19 | } 20 | 21 | m_pRtti = pRtti->GetType(m_name); 22 | return m_pRtti; 23 | } 24 | -------------------------------------------------------------------------------- /src/reverse/RTTILocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct RTTILocator 4 | { 5 | RTTILocator(RED4ext::CName aName); 6 | 7 | operator RED4ext::CBaseRTTIType*(); 8 | 9 | private: 10 | const RED4ext::CName m_name; 11 | RED4ext::CBaseRTTIType* m_pRtti = nullptr; 12 | }; 13 | -------------------------------------------------------------------------------- /src/reverse/RTTIMapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct LuaSandbox; 4 | struct RTTIMapper 5 | { 6 | using LockableState = TiltedPhoques::Lockable::Ref; 7 | 8 | RTTIMapper(const LockableState& acpLua, LuaSandbox& apSandbox); 9 | ~RTTIMapper(); 10 | 11 | void Register(); 12 | void Refresh(); 13 | 14 | static void SanitizeName(std::string& aName); 15 | static RED4ext::CName TryResolveTypeName(const sol::object& aValue); 16 | 17 | private: 18 | struct FuncFlags 19 | { 20 | uint32_t isNative : 1; // 00 21 | uint32_t isStatic : 1; // 01 22 | uint32_t isFinal : 1; // 02 23 | uint32_t isEvent : 1; // 03 24 | uint32_t isExec : 1; // 04 25 | uint32_t isUndefinedBody : 1; // 05 Unconfirmed (unset for scripted funcs without body) 26 | uint32_t isTimer : 1; // 06 Unconfirmed 27 | uint32_t isPrivate : 1; // 07 28 | uint32_t isProtected : 1; // 08 29 | uint32_t isPublic : 1; // 09 30 | uint32_t b11 : 1; // 0A 31 | uint32_t b12 : 1; // 0B 32 | uint32_t b13 : 1; // 0C 33 | uint32_t isConst : 1; // 0D 34 | uint32_t isQuest : 1; // 0E 35 | uint32_t isThreadsafe : 1; // 0F 36 | uint32_t b16 : 16; 37 | }; 38 | RED4EXT_ASSERT_SIZE(FuncFlags, 0x4); 39 | 40 | void RegisterSimpleTypes(sol::state& aLuaState, sol::table& aLuaGlobal) const; 41 | void RegisterDirectTypes(sol::state& aLuaState, sol::table& aLuaGlobal, RED4ext::CRTTISystem* apRtti); 42 | void RegisterDirectGlobals(sol::table& aLuaGlobal, RED4ext::CRTTISystem* apRtti); 43 | void RegisterScriptAliases(sol::table& aLuaGlobal, RED4ext::CRTTISystem* apRtti); 44 | void RegisterSpecialAccessors(sol::state& aLuaState, sol::table& aLuaGlobal) const; 45 | 46 | template void ExtendUsertype(const std::string acTypeName, sol::state& aLuaState, sol::table& aLuaGlobal) const; 47 | 48 | LockableState m_lua; 49 | LuaSandbox& m_sandbox; 50 | }; 51 | -------------------------------------------------------------------------------- /src/reverse/Relocation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | template<> 13 | struct RED4ext::Detail::AddressResolverOverride : std::true_type 14 | { 15 | inline static uintptr_t Resolve(uint32_t aHash) 16 | { 17 | using functionType = void* (*)(uint32_t); 18 | static functionType resolveFunc = nullptr; 19 | 20 | static std::once_flag flag; 21 | std::call_once(flag, 22 | []() 23 | { 24 | char exePath[4096]; 25 | GetModuleFileNameA(GetModuleHandle(nullptr), exePath, 4096); 26 | 27 | std::filesystem::path exe = exePath; 28 | 29 | auto dllName = exe.parent_path() / "version.dll"; 30 | constexpr auto functionName = "ResolveAddress"; 31 | 32 | auto handle = LoadLibraryA(dllName.string().c_str()); 33 | if (!handle) 34 | { 35 | std::stringstream stream; 36 | stream << "Failed to get '" << dllName 37 | << "' handle.\nProcess will now close.\n\nLast error: " << GetLastError(); 38 | 39 | MessageBoxA(nullptr, stream.str().c_str(), "Cyber Engine Tweaks", MB_ICONERROR | MB_OK); 40 | TerminateProcess(GetCurrentProcess(), 1); 41 | return; // Disable stupid warning 42 | } 43 | 44 | resolveFunc = reinterpret_cast(GetProcAddress(handle, functionName)); 45 | if (resolveFunc == nullptr) 46 | { 47 | std::stringstream stream; 48 | stream << "Failed to get '" << functionName 49 | << "' address.\nProcess will now close.\n\nLast error: " << GetLastError(); 50 | 51 | MessageBoxA(nullptr, stream.str().c_str(), "Cyber Engine Tweaks", MB_ICONERROR | MB_OK); 52 | TerminateProcess(GetCurrentProcess(), 1); 53 | } 54 | }); 55 | 56 | auto address = resolveFunc(aHash); 57 | if (address == nullptr) 58 | { 59 | std::stringstream stream; 60 | stream << "Failed to resolve address for hash " << std::hex << std::showbase << aHash << ".\nProcess will now close."; 61 | 62 | MessageBoxA(nullptr, stream.str().c_str(), "Cyber Engine Tweaks", MB_ICONERROR | MB_OK); 63 | TerminateProcess(GetCurrentProcess(), 1); 64 | } 65 | 66 | return reinterpret_cast(address); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /src/reverse/RenderContext.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "RenderContext.h" 4 | #include "Addresses.h" 5 | 6 | RenderContext* RenderContext::GetInstance() noexcept 7 | { 8 | static RED4ext::UniversalRelocPtr s_instance(CyberEngineTweaks::AddressHashes::CRenderGlobal_InstanceOffset); 9 | return *s_instance.GetAddr(); 10 | } 11 | -------------------------------------------------------------------------------- /src/reverse/RenderContext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct RenderContext 4 | { 5 | struct Device 6 | { 7 | IDXGISwapChain4* pSwapChain; 8 | uint8_t pad8[0xB0 - 0x8]; 9 | }; 10 | 11 | RenderContext() = delete; 12 | ~RenderContext() = delete; 13 | 14 | static RenderContext* GetInstance() noexcept; 15 | 16 | uint8_t pad0[0xC97F38]; 17 | Device devices[0x30]; // Count unknown, it is at least 0x20 18 | uint8_t pad[0x13BC4D0 - (0xC97F38 + sizeof(devices))]; 19 | ID3D12CommandQueue* pDirectQueue; // 0x13BC4D0 20 | }; 21 | 22 | static_assert(sizeof(RenderContext::Device) == 0xB0); 23 | static_assert(offsetof(RenderContext, devices) == 0xC97F38); 24 | static_assert(offsetof(RenderContext, pDirectQueue) == 0x13BC4D0); -------------------------------------------------------------------------------- /src/reverse/ResourceAsyncReference.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ResourceAsyncReference.h" 4 | #include "RTTILocator.h" 5 | #include "Converter.h" 6 | 7 | ResourceAsyncReference::ResourceAsyncReference( 8 | const TiltedPhoques::Locked& aView, RED4ext::CBaseRTTIType* apType, RED4ext::ResourceAsyncReference* apReference) 9 | : ClassType(aView, reinterpret_cast(apType)->innerType) 10 | , m_reference(*apReference) 11 | { 12 | } 13 | 14 | uint64_t ResourceAsyncReference::Hash(const std::string& aPath) 15 | { 16 | return RED4ext::ResourcePath(aPath.c_str()); 17 | } 18 | 19 | RED4ext::ScriptInstance ResourceAsyncReference::GetHandle() const 20 | { 21 | return nullptr; 22 | } 23 | 24 | RED4ext::ScriptInstance ResourceAsyncReference::GetValuePtr() const 25 | { 26 | return GetHandle(); 27 | } 28 | 29 | uint64_t ResourceAsyncReference::GetHash() const 30 | { 31 | return m_reference.path.hash; 32 | } 33 | 34 | // Manual uint64 to Lua conversion because sol + LuaJIT can't do it 35 | sol::object ResourceAsyncReference::GetLuaHash() const 36 | { 37 | static RTTILocator s_uint64Type{RED4ext::FNV1a64("Uint64")}; 38 | 39 | auto lockedState = m_lua.Lock(); 40 | 41 | RED4ext::CStackType stackType; 42 | stackType.type = s_uint64Type; 43 | stackType.value = const_cast*>(&m_reference); 44 | 45 | return Converter::ToLua(stackType, lockedState); 46 | } 47 | -------------------------------------------------------------------------------- /src/reverse/ResourceAsyncReference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Type.h" 4 | 5 | struct ResourceAsyncReference : ClassType 6 | { 7 | ResourceAsyncReference( 8 | const TiltedPhoques::Locked& aView, RED4ext::CBaseRTTIType* apType, RED4ext::ResourceAsyncReference* apReference); 9 | 10 | static uint64_t Hash(const std::string& aPath); 11 | 12 | RED4ext::ScriptInstance GetHandle() const override; 13 | RED4ext::ScriptInstance GetValuePtr() const override; 14 | 15 | uint64_t GetHash() const; 16 | sol::object GetLuaHash() const; 17 | 18 | private: 19 | RED4ext::ResourceAsyncReference m_reference; 20 | }; 21 | -------------------------------------------------------------------------------- /src/reverse/SingletonReference.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "SingletonReference.h" 4 | 5 | SingletonReference::SingletonReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass) 6 | : ClassType(aView, apClass) 7 | { 8 | } 9 | 10 | SingletonReference::~SingletonReference() = default; 11 | 12 | RED4ext::ScriptInstance SingletonReference::GetHandle() const 13 | { 14 | const auto* engine = RED4ext::CGameEngine::Get(); 15 | auto* pGameInstance = engine->framework->gameInstance; 16 | 17 | return pGameInstance->GetSystem(m_pType); 18 | } 19 | -------------------------------------------------------------------------------- /src/reverse/SingletonReference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Type.h" 4 | 5 | struct SingletonReference : ClassType 6 | { 7 | SingletonReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass); 8 | ~SingletonReference() override; 9 | 10 | protected: 11 | RED4ext::ScriptInstance GetHandle() const override; 12 | }; 13 | -------------------------------------------------------------------------------- /src/reverse/StrongReference.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "StrongReference.h" 4 | #include "RTTILocator.h" 5 | 6 | #include "CET.h" 7 | 8 | static RTTILocator s_sIScriptableType{RED4ext::FNV1a64("IScriptable")}; 9 | 10 | StrongReference::StrongReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::Handle aStrongHandle) 11 | : ClassType(aView, nullptr) 12 | , m_strongHandle(std::move(aStrongHandle)) 13 | , m_pHandleType(nullptr) 14 | { 15 | if (m_strongHandle) 16 | { 17 | m_pType = m_strongHandle->GetType(); 18 | } 19 | } 20 | 21 | StrongReference::StrongReference( 22 | const TiltedPhoques::Lockable::Ref& aView, RED4ext::Handle aStrongHandle, RED4ext::CRTTIHandleType* apStrongHandleType) 23 | : ClassType(aView, nullptr) 24 | , m_strongHandle(std::move(aStrongHandle)) 25 | , m_pHandleType(apStrongHandleType) 26 | { 27 | if (m_strongHandle) 28 | { 29 | auto const cpClass = reinterpret_cast(apStrongHandleType->GetInnerType()); 30 | 31 | m_pType = cpClass->IsA(s_sIScriptableType) ? m_strongHandle->GetType() : m_strongHandle->GetNativeType(); 32 | } 33 | } 34 | 35 | StrongReference::~StrongReference() 36 | { 37 | // Nasty hack so that the Lua VM doesn't try to release game memory on shutdown 38 | if (!CET::IsRunning()) 39 | { 40 | m_strongHandle.instance = nullptr; 41 | m_strongHandle.refCount = nullptr; 42 | } 43 | } 44 | 45 | RED4ext::ScriptInstance StrongReference::GetHandle() const 46 | { 47 | return m_strongHandle.instance; 48 | } 49 | 50 | RED4ext::ScriptInstance StrongReference::GetValuePtr() const 51 | { 52 | return const_cast*>(&m_strongHandle); 53 | } 54 | 55 | RED4ext::CBaseRTTIType* StrongReference::GetValueType() const 56 | { 57 | return m_pHandleType; 58 | } 59 | -------------------------------------------------------------------------------- /src/reverse/StrongReference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Type.h" 4 | 5 | struct StrongReference : ClassType 6 | { 7 | StrongReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::Handle aStrongHandle); 8 | StrongReference( 9 | const TiltedPhoques::Lockable::Ref& aView, RED4ext::Handle aStrongHandle, 10 | RED4ext::CRTTIHandleType* apStrongHandleType); 11 | ~StrongReference() override; 12 | 13 | protected: 14 | RED4ext::ScriptInstance GetHandle() const override; 15 | RED4ext::ScriptInstance GetValuePtr() const override; 16 | RED4ext::CBaseRTTIType* GetValueType() const override; 17 | 18 | private: 19 | friend struct Scripting; 20 | friend struct TweakDB; 21 | 22 | RED4ext::Handle m_strongHandle; 23 | RED4ext::CRTTIHandleType* m_pHandleType; 24 | }; 25 | -------------------------------------------------------------------------------- /src/reverse/TweakDB/FlatPool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class FlatPool 6 | { 7 | public: 8 | static constexpr int32_t InvalidOffset = -1; 9 | 10 | FlatPool(); 11 | explicit FlatPool(RED4ext::TweakDB* aTweakDb); 12 | 13 | int32_t AllocateData(const RED4ext::CStackType& aData); 14 | int32_t AllocateValue(const RED4ext::CBaseRTTIType* aType, RED4ext::ScriptInstance aValue); 15 | int32_t AllocateDefault(const RED4ext::CBaseRTTIType* aType); 16 | 17 | RED4ext::CStackType GetData(int32_t aOffset); 18 | RED4ext::ScriptInstance GetValuePtr(int32_t aOffset); 19 | 20 | [[nodiscard]] bool IsFlatType(RED4ext::CBaseRTTIType* aType) const; 21 | [[nodiscard]] bool IsFlatType(RED4ext::CName aTypeName) const; 22 | 23 | private: 24 | struct FlatTypeInfo 25 | { 26 | RED4ext::CBaseRTTIType* type; 27 | uintptr_t offset; 28 | }; 29 | 30 | using FlatValueMap = TiltedPhoques::Map; // ValueHash -> Offset 31 | using FlatPoolMap = TiltedPhoques::Map; // TypeName -> Pool 32 | using FlatDefaultMap = TiltedPhoques::Map; // TypeName -> Offset 33 | using FlatTypeMap = TiltedPhoques::Map; // VFT -> TypeInfo 34 | 35 | void Initialize(); 36 | void SyncBuffer(); 37 | 38 | inline RED4ext::CStackType GetFlatData(int32_t aOffset); 39 | inline static uint64_t Hash(const RED4ext::CBaseRTTIType* aType, RED4ext::ScriptInstance aValue); 40 | 41 | RED4ext::TweakDB* m_tweakDb; 42 | uintptr_t m_bufferEnd; 43 | uintptr_t m_offsetEnd; 44 | FlatPoolMap m_pools; 45 | FlatDefaultMap m_defaults; 46 | FlatTypeMap m_vfts; 47 | }; 48 | -------------------------------------------------------------------------------- /src/reverse/TweakDB/ResourcesList.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ResourcesList.h" 4 | 5 | #include 6 | #include 7 | 8 | using TOodleLZ_Decompress = 9 | size_t (*)(char* in, int insz, char* out, int outsz, int wantsFuzzSafety, int b, int c, void* d, void* e, void* f, void* g, void* workBuffer, size_t workBufferSize, int j); 10 | 11 | ResourcesList::Resource::Resource(std::string aName) noexcept 12 | : m_isFiltered(false) 13 | , m_name(std::move(aName)) 14 | , m_hash(RED4ext::FNV1a64(m_name.c_str())) 15 | { 16 | } 17 | 18 | ResourcesList* ResourcesList::Get() 19 | { 20 | static ResourcesList instance; 21 | return &instance; 22 | } 23 | 24 | bool ResourcesList::Initialize() 25 | { 26 | Reset(); 27 | 28 | // TODO - share decompression routine with TweakDBMetadata 29 | 30 | auto hOodleHandle = GetModuleHandle(TEXT("oo2ext_7_win64.dll")); 31 | if (hOodleHandle == nullptr) 32 | { 33 | spdlog::error("Could not get Oodle access"); 34 | return false; 35 | } 36 | 37 | auto OodleLZ_Decompress = reinterpret_cast(GetProcAddress(hOodleHandle, "OodleLZ_Decompress")); 38 | if (OodleLZ_Decompress == nullptr) 39 | { 40 | spdlog::error("Could not get OodleLZ_Decompress"); 41 | return false; 42 | } 43 | 44 | auto filepath = GetAbsolutePath(c_defaultFilename, CET::Get().GetPaths().TweakDB(), false, true); 45 | if (!exists(filepath)) 46 | return false; 47 | 48 | m_resources.reserve(1485150); 49 | 50 | try 51 | { 52 | std::ifstream file(filepath, std::ios::binary); 53 | file.exceptions(std::ios::badbit); 54 | 55 | size_t headerSize = 8; 56 | 57 | std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); 58 | std::string buffer; 59 | buffer.resize(*reinterpret_cast(content.data() + 4)); 60 | 61 | char workingMemory[0x80000]; 62 | auto size = OodleLZ_Decompress( 63 | content.data() + headerSize, static_cast(content.size() - headerSize), buffer.data(), static_cast(buffer.size()), 1, 1, 0, nullptr, nullptr, nullptr, nullptr, 64 | workingMemory, std::size(workingMemory), 3); 65 | 66 | assert(size == buffer.size()); 67 | if (size != buffer.size()) 68 | { 69 | spdlog::error("Decompress failed!"); 70 | return false; 71 | } 72 | 73 | std::istringstream iss(buffer); 74 | 75 | std::string filename; 76 | while (std::getline(iss, filename)) 77 | { 78 | filename.resize(filename.size() - 1); 79 | m_resources.emplace_back(filename); 80 | } 81 | 82 | for (auto& resource : m_resources) 83 | { 84 | m_resourcesByHash.emplace(resource.m_hash, &resource); 85 | } 86 | 87 | return m_isInitialized = true; 88 | } 89 | // this is easier for now 90 | catch (std::exception&) 91 | { 92 | return m_isInitialized = false; 93 | } 94 | } 95 | 96 | bool ResourcesList::IsInitialized() const 97 | { 98 | return m_isInitialized; 99 | } 100 | 101 | const std::string& ResourcesList::Resolve(uint64_t aHash) 102 | { 103 | static std::string defaultName = "UNRESOLVED_RESOURCE_PATH"; 104 | 105 | const auto it = m_resourcesByHash.find(aHash); 106 | 107 | return it == m_resourcesByHash.end() ? defaultName : it->second->m_name; 108 | } 109 | 110 | TiltedPhoques::Vector& ResourcesList::GetResources() 111 | { 112 | return m_resources; 113 | } 114 | 115 | void ResourcesList::Reset() 116 | { 117 | m_isInitialized = false; 118 | m_resources.clear(); 119 | m_resourcesByHash.clear(); 120 | } 121 | -------------------------------------------------------------------------------- /src/reverse/TweakDB/ResourcesList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct ResourcesList 4 | { 5 | inline static const std::string c_defaultFilename = "usedhashes.kark"; 6 | 7 | struct Resource 8 | { 9 | bool m_isFiltered; 10 | std::string m_name; 11 | uint64_t m_hash; 12 | 13 | Resource(std::string aName) noexcept; 14 | 15 | Resource(Resource&&) noexcept = default; 16 | Resource& operator=(Resource&&) noexcept = default; 17 | }; 18 | 19 | static ResourcesList* Get(); 20 | 21 | bool Initialize(); 22 | 23 | bool IsInitialized() const; 24 | 25 | const std::string& Resolve(uint64_t aHash); 26 | 27 | TiltedPhoques::Vector& GetResources(); 28 | 29 | protected: 30 | void Reset(); 31 | 32 | private: 33 | std::atomic_bool m_isInitialized = false; 34 | TiltedPhoques::Vector m_resources; 35 | TiltedPhoques::Map m_resourcesByHash; 36 | }; 37 | -------------------------------------------------------------------------------- /src/reverse/TweakDB/TweakDB.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | struct TweakDB 9 | { 10 | TweakDB(const TiltedPhoques::Lockable::Ref& aLua); 11 | 12 | void DebugStats(); 13 | sol::object GetRecords(const std::string& acRecordTypeName) const; 14 | sol::object GetRecordByName(const std::string& acRecordName) const; 15 | sol::object GetRecord(TweakDBID aDBID) const; 16 | sol::object QueryByName(const std::string& acQueryName) const; 17 | sol::object Query(TweakDBID aDBID) const; 18 | sol::object GetFlatByName(const std::string& acFlatName) const; 19 | sol::object GetFlat(TweakDBID aDBID) const; 20 | bool SetFlatsByName(const std::string& acRecordName, sol::table aTable, sol::this_environment aThisEnv); 21 | bool SetFlats(TweakDBID aDBID, sol::table aTable, sol::this_environment aThisEnv); 22 | bool SetFlatByName(const std::string& acFlatName, sol::optional aObject, sol::this_environment aThisEnv) const; 23 | bool SetFlat(TweakDBID aDBID, sol::optional aObject, sol::this_environment aThisEnv) const; 24 | bool SetFlatByNameAutoUpdate(const std::string& acFlatName, sol::optional aObject, sol::this_environment aThisEnv); 25 | bool SetFlatAutoUpdate(TweakDBID aDBID, sol::optional aObject, sol::this_environment aThisEnv); 26 | bool SetTypedFlatByName(const std::string& acFlatName, sol::optional aObject, const std::string& acTypeName, sol::this_environment aThisEnv) const; 27 | bool SetTypedFlat(TweakDBID aDBID, sol::optional aObject, const std::string& acTypeName, sol::this_environment aThisEnv) const; 28 | bool UpdateRecordByName(const std::string& acRecordName); 29 | bool UpdateRecordByID(TweakDBID aDBID); 30 | bool UpdateRecord(sol::object aValue, sol::this_environment aThisEnv); 31 | bool CreateRecord(const std::string& acRecordName, const std::string& acRecordTypeName, sol::this_environment aThisEnv) const; 32 | bool CreateRecordToID(TweakDBID aDBID, const std::string& acRecordTypeName, sol::this_environment aThisEnv) const; 33 | bool CloneRecordByName(const std::string& acRecordName, const std::string& acClonedRecordName, sol::this_environment aThisEnv) const; 34 | bool CloneRecord(const std::string& acRecordName, TweakDBID aClonedRecordDBID, sol::this_environment aThisEnv) const; 35 | bool CloneRecordToID(TweakDBID aDBID, TweakDBID aClonedRecordDBID, sol::this_environment aThisEnv) const; 36 | bool DeleteRecord(const std::string& acRecordName, sol::this_environment aThisEnv); 37 | bool DeleteRecordByID(TweakDBID aDBID, sol::this_environment aThisEnv); 38 | 39 | protected: 40 | friend struct TweakDBEditor; 41 | bool SetOrCreateFlat( 42 | TweakDBID aDBID, sol::object aObject, const std::string& acFlatName, const std::string& acTypeName, const std::shared_ptr& acpLogger = nullptr) const; 43 | static RED4ext::CStackType InternalGetFlat(RED4ext::TweakDBID aDBID); 44 | static int32_t InternalSetFlat(RED4ext::TweakDBID aDBID, const RED4ext::CStackType& acStackType); 45 | static bool InternalCreateRecord(const std::string& acRecordName, const std::string& acRecordTypeName, const std::shared_ptr& acpLogger); 46 | static bool InternalCreateRecord(TweakDBID aDBID, const std::string& acRecordTypeName, const std::shared_ptr& acpLogger); 47 | static bool InternalCloneRecord(const std::string& acRecordName, RED4ext::TweakDBID aClonedRecordDBID, const std::shared_ptr& acpLogger); 48 | static bool InternalCloneRecord(TweakDBID aDBID, RED4ext::TweakDBID aClonedRecordDBID, const std::shared_ptr& acpLogger); 49 | // Can't figure out a good name for this function. 50 | // Creates a record of the same type as 'acClonedRecord' 51 | // Creates all of its flats 52 | // Setting 'cloneValues' to false will set default values 53 | static bool 54 | InternalCloneRecord(const std::string& acRecordName, const RED4ext::gamedataTweakDBRecord* acClonedRecord, bool cloneValues, const std::shared_ptr& acpLogger); 55 | static bool InternalCloneRecord(TweakDBID aDBID, const RED4ext::gamedataTweakDBRecord* acClonedRecord, bool cloneValues, const std::shared_ptr& acpLogger); 56 | static bool 57 | InternalCloneFlats(RED4ext::TweakDBID aDBID, const RED4ext::gamedataTweakDBRecord* acClonedRecord, bool cloneValues, const std::shared_ptr& acpLogger); 58 | static bool InternalDeleteRecord(RED4ext::TweakDBID aDBID, const std::shared_ptr& acpLogger = nullptr); 59 | static bool RemoveFlat(RED4ext::TweakDBID aDBID); 60 | static bool IsACreatedRecord(RED4ext::TweakDBID aDBID); 61 | inline static std::string GetTDBIDString(uint64_t aDBID); 62 | 63 | private: 64 | TiltedPhoques::Lockable::Ref m_lua; 65 | static TiltedPhoques::UniquePtr s_flatPool; 66 | static std::mutex s_mutex; 67 | static std::set s_createdRecords; 68 | }; -------------------------------------------------------------------------------- /src/reverse/Type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Type 4 | { 5 | struct Descriptor 6 | { 7 | std::string name{"Unknown"}; 8 | TiltedPhoques::Vector functions; 9 | TiltedPhoques::Vector staticFunctions; 10 | TiltedPhoques::Vector properties; 11 | 12 | std::string ToString() const; 13 | }; 14 | 15 | Type(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass); 16 | virtual ~Type() = default; 17 | 18 | RED4ext::CBaseRTTIType* GetType() const { return m_pType; } 19 | virtual RED4ext::ScriptInstance GetHandle() const { return nullptr; } 20 | virtual RED4ext::CBaseRTTIType* GetValueType() const { return m_pType; } 21 | virtual RED4ext::ScriptInstance GetValuePtr() const { return nullptr; } 22 | 23 | sol::object Index(const std::string& acName, sol::this_environment aThisEnv); 24 | sol::object NewIndex(const std::string& acName, sol::optional aParam); 25 | 26 | virtual sol::object Index_Impl(const std::string& acName, sol::this_environment aThisEnv); 27 | virtual sol::object NewIndex_Impl(const std::string& acName, sol::object aParam); 28 | 29 | std::string GetName() const; 30 | virtual Descriptor Dump(bool aWithHashes) const; 31 | std::string GameDump() const; 32 | 33 | std::string FunctionDescriptor(RED4ext::CBaseFunction* apFunc, bool aWithHashes) const; 34 | 35 | protected: 36 | RED4ext::CBaseRTTIType* m_pType{nullptr}; 37 | 38 | friend struct Scripting; 39 | 40 | TiltedPhoques::Lockable::Ref m_lua; 41 | // NOTE: this single unordered_map cannot be changed to TP::Map due to map corruption in function NewIndex_Impl 42 | std::unordered_map m_properties; 43 | }; 44 | 45 | struct ClassType : Type 46 | { 47 | ClassType(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass); 48 | ~ClassType() override = default; 49 | 50 | Descriptor Dump(bool aWithHashes) const override; 51 | sol::object Index_Impl(const std::string& acName, sol::this_environment aThisEnv) override; 52 | sol::object NewIndex_Impl(const std::string& acName, sol::object aParam) override; 53 | 54 | RED4ext::CClass* GetClass() const { return reinterpret_cast(m_pType); } 55 | }; 56 | 57 | struct UnknownType : Type 58 | { 59 | UnknownType(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass, RED4ext::ScriptInstance apInstance); 60 | 61 | Descriptor Dump(bool aWithHashes) const override; 62 | RED4ext::ScriptInstance GetHandle() const override { return m_pInstance.get(); } 63 | 64 | private: 65 | std::unique_ptr m_pInstance; 66 | }; -------------------------------------------------------------------------------- /src/reverse/WeakReference.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "WeakReference.h" 4 | #include "RTTILocator.h" 5 | 6 | #include "CET.h" 7 | 8 | static RTTILocator s_sIScriptableType{RED4ext::FNV1a64("IScriptable")}; 9 | 10 | WeakReference::WeakReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::WeakHandle aWeakHandle) 11 | : ClassType(aView, nullptr) 12 | , m_weakHandle(std::move(aWeakHandle)) 13 | , m_pHandleType(nullptr) 14 | { 15 | const auto ref = m_weakHandle.Lock(); 16 | if (ref) 17 | { 18 | m_pType = ref->GetType(); 19 | } 20 | } 21 | 22 | WeakReference::WeakReference( 23 | const TiltedPhoques::Lockable::Ref& aView, RED4ext::WeakHandle aWeakHandle, 24 | RED4ext::CRTTIWeakHandleType* apWeakHandleType) 25 | : ClassType(aView, nullptr) 26 | , m_weakHandle(std::move(aWeakHandle)) 27 | , m_pHandleType(apWeakHandleType) 28 | { 29 | const auto ref = m_weakHandle.Lock(); 30 | if (ref) 31 | { 32 | auto const cpClass = reinterpret_cast(apWeakHandleType->GetInnerType()); 33 | 34 | m_pType = cpClass->IsA(s_sIScriptableType) ? ref->GetType() : ref->GetNativeType(); 35 | } 36 | } 37 | 38 | WeakReference::~WeakReference() 39 | { 40 | // Nasty hack so that the Lua VM doesn't try to release game memory on shutdown 41 | if (!CET::IsRunning()) 42 | { 43 | m_weakHandle.instance = nullptr; 44 | m_weakHandle.refCount = nullptr; 45 | } 46 | } 47 | 48 | RED4ext::ScriptInstance WeakReference::GetHandle() const 49 | { 50 | const auto ref = m_weakHandle.Lock(); 51 | if (ref) 52 | { 53 | return m_weakHandle.instance; 54 | } 55 | 56 | return nullptr; 57 | } 58 | 59 | RED4ext::ScriptInstance WeakReference::GetValuePtr() const 60 | { 61 | return const_cast*>(&m_weakHandle); 62 | } 63 | 64 | RED4ext::CBaseRTTIType* WeakReference::GetValueType() const 65 | { 66 | return m_pHandleType; 67 | } 68 | -------------------------------------------------------------------------------- /src/reverse/WeakReference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Type.h" 4 | 5 | struct WeakReference : ClassType 6 | { 7 | WeakReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::WeakHandle aWeakHandle); 8 | WeakReference( 9 | const TiltedPhoques::Lockable::Ref& aView, RED4ext::WeakHandle aWeakHandle, 10 | RED4ext::CRTTIWeakHandleType* apWeakHandleType); 11 | ~WeakReference() override; 12 | 13 | protected: 14 | RED4ext::ScriptInstance GetHandle() const override; 15 | RED4ext::ScriptInstance GetValuePtr() const override; 16 | RED4ext::CBaseRTTIType* GetValueType() const override; 17 | 18 | private: 19 | friend struct Scripting; 20 | friend struct TweakDB; 21 | 22 | RED4ext::WeakHandle m_weakHandle; 23 | RED4ext::CRTTIWeakHandleType* m_pHandleType; 24 | }; 25 | -------------------------------------------------------------------------------- /src/scripting/FunctionOverride.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Scripting; 4 | 5 | struct FunctionOverride 6 | { 7 | struct Context 8 | { 9 | sol::protected_function ScriptFunction; 10 | sol::environment Environment; 11 | bool Forward; 12 | }; 13 | 14 | struct CallChain 15 | { 16 | RED4ext::CBaseFunction* Trampoline; 17 | Scripting* pScripting; 18 | TiltedPhoques::Vector> Before; 19 | TiltedPhoques::Vector> After; 20 | TiltedPhoques::Vector> Overrides; 21 | bool IsEmpty; 22 | bool CollectGarbage; 23 | }; 24 | 25 | FunctionOverride(Scripting* apScripting); 26 | ~FunctionOverride(); 27 | 28 | void* MakeExecutable(const uint8_t* apData, size_t aSize); 29 | void Refresh(); 30 | void Clear(); 31 | 32 | void Override( 33 | const std::string& acTypeName, const std::string& acFullName, sol::protected_function aFunction, sol::environment aEnvironment, bool aAbsolute, bool aAfter = false, 34 | bool aCollectGarbage = false); 35 | 36 | protected: 37 | static void CopyFunctionDescription(RED4ext::CBaseFunction* aFunc, RED4ext::CBaseFunction* aRealFunc, bool aForceNative); 38 | static void HandleOverridenFunction(RED4ext::IScriptable* apContext, RED4ext::CStackFrame* apFrame, void* apOut, void* a4, RED4ext::CClassFunction* apFunction); 39 | static bool HookRunPureScriptFunction(RED4ext::CClassFunction* apFunction, RED4ext::CScriptStack* apStack, RED4ext::CStackFrame* a3); 40 | static void* HookCreateFunction(void* apMemoryPool, size_t aSize); 41 | static bool ExecuteChain( 42 | const CallChain& aChain, std::shared_lock& aLock, RED4ext::IScriptable* apContext, TiltedPhoques::Vector* apOrigArgs, 43 | RED4ext::CStackType* apResult, TiltedPhoques::Vector* apOutArgs, RED4ext::CScriptStack* apStack, RED4ext::CStackFrame* apFrame, char* apCode, 44 | uint8_t aParam); 45 | static sol::function WrapNextOverride( 46 | const CallChain& aChain, int aStep, sol::state& aLuaState, sol::object& aLuaContext, TiltedPhoques::Vector& aLuaArgs, RED4ext::CBaseFunction* apRealFunction, 47 | RED4ext::IScriptable* apRealContext, std::shared_lock& aLock); 48 | 49 | private: 50 | enum 51 | { 52 | kExecutableSize = 1 << 20 53 | }; 54 | 55 | void Hook() const; 56 | 57 | void* m_pBufferStart; 58 | void* m_pBuffer; 59 | size_t m_size{kExecutableSize}; 60 | TiltedPhoques::Map m_functions; 61 | TiltedPhoques::Map m_trampolines; 62 | Scripting* m_pScripting; 63 | std::shared_mutex m_lock; 64 | }; -------------------------------------------------------------------------------- /src/scripting/GameDump.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "GameDump.h" 4 | 5 | namespace GameDump 6 | { 7 | bool DumpVTablesTask::Run() 8 | { 9 | TiltedPhoques::Map vtableMap; 10 | 11 | auto ModuleBase = GetModuleHandle(nullptr); 12 | auto begin = reinterpret_cast(ModuleBase); 13 | const auto* dosHeader = reinterpret_cast(ModuleBase); 14 | const auto* ntHeader = reinterpret_cast(reinterpret_cast(dosHeader) + dosHeader->e_lfanew); 15 | uintptr_t end = begin + ntHeader->OptionalHeader.SizeOfCode + ntHeader->OptionalHeader.SizeOfInitializedData; 16 | 17 | const auto* pRttiSystem = RED4ext::CRTTISystem::Get(); 18 | 19 | auto dumpClass = [begin, end](auto& aVtableMap, RED4ext::CBaseRTTIType* apType) 20 | { 21 | uintptr_t vtable = *reinterpret_cast(apType); 22 | const RED4ext::CName typeName = apType->GetName(); 23 | const std::string name = typeName.ToString(); 24 | if (vtable >= begin && vtable <= end) 25 | { 26 | aVtableMap.emplace(vtable, "VT_RTTI_" + name); 27 | } 28 | 29 | // Construct an empty instance of this class and dump that 30 | if (apType->GetType() == RED4ext::ERTTIType::Class) 31 | { 32 | const uint32_t size = apType->GetSize(); 33 | 34 | // We aren't borrowing the game's allocator on purpose because some classes have Abstract 35 | // allocators and they assert 36 | const auto pMemory = std::make_unique(size); 37 | 38 | memset(pMemory.get(), 0, size); 39 | 40 | apType->Construct(pMemory.get()); 41 | 42 | if (size >= sizeof(uintptr_t)) 43 | { 44 | vtable = *reinterpret_cast(pMemory.get()); 45 | 46 | if (vtable >= begin && vtable <= end) 47 | { 48 | aVtableMap.emplace(vtable, "VT_" + name); 49 | } 50 | } 51 | 52 | // Lets just leak memory from nested objects for now, this is broken on certain classes, 53 | // havent determined why 54 | // type->Destroy(buffer); 55 | } 56 | }; 57 | 58 | pRttiSystem->types.for_each( 59 | [&dumpClass, &vtableMap](RED4ext::CName aName, RED4ext::CBaseRTTIType*& apType) 60 | { 61 | TP_UNUSED(aName); 62 | 63 | dumpClass(vtableMap, apType); 64 | 65 | if (apType->GetType() == RED4ext::ERTTIType::Class) 66 | { 67 | auto* pParent = static_cast(apType)->parent; 68 | while (pParent) 69 | { 70 | dumpClass(vtableMap, pParent); 71 | 72 | pParent = pParent->parent; 73 | if (!pParent || pParent->GetType() != RED4ext::ERTTIType::Class) 74 | { 75 | break; 76 | } 77 | } 78 | } 79 | }); 80 | 81 | for (auto& [key, value] : vtableMap) 82 | { 83 | Log::Info("{:016X},{}", key, value); 84 | } 85 | 86 | return true; 87 | } 88 | } // namespace GameDump 89 | -------------------------------------------------------------------------------- /src/scripting/GameDump.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace RED4ext 4 | { 5 | struct CClass; 6 | struct IRTTIType; 7 | struct CProperty; 8 | } // namespace RED4ext 9 | 10 | namespace GameDump 11 | { 12 | struct DumpVTablesTask 13 | { 14 | static bool Run(); 15 | }; 16 | } // namespace GameDump 17 | -------------------------------------------------------------------------------- /src/scripting/GameHooks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "GameHooks.h" 4 | 5 | #include "RED4ext/GameStates.hpp" 6 | 7 | static std::unique_ptr s_pGameMainThread; 8 | 9 | void GameMainThread::RepeatedTaskQueue::AddTask(const std::function& aFunction) 10 | { 11 | std::lock_guard lock(m_mutex); 12 | m_tasks.emplace_back(aFunction); 13 | } 14 | 15 | void GameMainThread::RepeatedTaskQueue::Drain() 16 | { 17 | std::lock_guard lock(m_mutex); 18 | for (auto taskIt = m_tasks.begin(); taskIt != m_tasks.end();) 19 | { 20 | if ((*taskIt)()) 21 | taskIt = m_tasks.erase(taskIt); 22 | else 23 | ++taskIt; 24 | } 25 | } 26 | GameMainThread::StateTickOverride::StateTickOverride(uint32_t aHash, const char* acpRealFunctionName) 27 | { 28 | const RED4ext::UniversalRelocPtr func(aHash); 29 | Location = func.GetAddr(); 30 | 31 | if (Location) 32 | { 33 | if (MH_CreateHook(Location, reinterpret_cast(&GameMainThread::HookStateTick), reinterpret_cast(&RealFunction)) != MH_OK || MH_EnableHook(Location) != MH_OK) 34 | Log::Error("Could not hook main thread function {}! Main thread is not completely hooked!", acpRealFunctionName); 35 | else 36 | Log::Info("Main thread function {} hook complete!", acpRealFunctionName); 37 | } 38 | else 39 | Log::Error("Could not locate {}! Main thread is not completely hooked!", acpRealFunctionName); 40 | } 41 | 42 | GameMainThread::StateTickOverride::~StateTickOverride() 43 | { 44 | MH_DisableHook(Location); 45 | 46 | Location = nullptr; 47 | RealFunction = nullptr; 48 | } 49 | 50 | bool GameMainThread::StateTickOverride::OnTick(RED4ext::IGameState* apThisState, RED4ext::CGameApplication* apGameApplication) 51 | { 52 | Tasks.Drain(); 53 | 54 | return RealFunction(apThisState, apGameApplication); 55 | } 56 | 57 | GameMainThread& GameMainThread::Get() 58 | { 59 | static GameMainThread s_gameMainThread; 60 | 61 | return s_gameMainThread; 62 | } 63 | 64 | void GameMainThread::AddBaseInitializationTask(const std::function& aFunction) 65 | { 66 | constexpr auto cStateIndex = static_cast(RED4ext::EGameStateType::BaseInitialization); 67 | return m_stateTickOverrides[cStateIndex].Tasks.AddTask(aFunction); 68 | } 69 | 70 | void GameMainThread::AddInitializationTask(const std::function& aFunction) 71 | { 72 | constexpr auto cStateIndex = static_cast(RED4ext::EGameStateType::Initialization); 73 | return m_stateTickOverrides[cStateIndex].Tasks.AddTask(aFunction); 74 | } 75 | 76 | void GameMainThread::AddRunningTask(const std::function& aFunction) 77 | { 78 | constexpr auto cStateIndex = static_cast(RED4ext::EGameStateType::Running); 79 | return m_stateTickOverrides[cStateIndex].Tasks.AddTask(aFunction); 80 | } 81 | 82 | void GameMainThread::AddShutdownTask(const std::function& aFunction) 83 | { 84 | constexpr auto cStateIndex = static_cast(RED4ext::EGameStateType::Shutdown); 85 | return m_stateTickOverrides[cStateIndex].Tasks.AddTask(aFunction); 86 | } 87 | 88 | void GameMainThread::AddGenericTask(const std::function& aFunction) 89 | { 90 | m_genericQueue.AddTask(aFunction); 91 | } 92 | 93 | bool GameMainThread::HookStateTick(RED4ext::IGameState* apThisState, RED4ext::CGameApplication* apGameApplication) 94 | { 95 | auto& gmt = Get(); 96 | 97 | // drain generic tasks 98 | gmt.m_genericQueue.Drain(); 99 | 100 | // execute specific state tasks, including original function 101 | const auto cStateIndex = static_cast(apThisState->GetType()); 102 | return gmt.m_stateTickOverrides[cStateIndex].OnTick(apThisState, apGameApplication); 103 | } 104 | -------------------------------------------------------------------------------- /src/scripting/GameHooks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct GameMainThread 4 | { 5 | static GameMainThread& Get(); 6 | 7 | void AddBaseInitializationTask(const std::function& aFunction); 8 | void AddInitializationTask(const std::function& aFunction); 9 | void AddRunningTask(const std::function& aFunction); 10 | void AddShutdownTask(const std::function& aFunction); 11 | void AddGenericTask(const std::function& aFunction); 12 | 13 | private: 14 | GameMainThread() = default; 15 | 16 | using TStateTick = bool(RED4ext::IGameState*, RED4ext::CGameApplication*); 17 | 18 | static bool HookStateTick(RED4ext::IGameState* apThisState, RED4ext::CGameApplication* apGameApplication); 19 | 20 | // helper task queue which executes added tasks each drain until they are finished 21 | struct RepeatedTaskQueue 22 | { 23 | void AddTask(const std::function& aFunction); 24 | void Drain(); 25 | 26 | private: 27 | std::recursive_mutex m_mutex; 28 | TiltedPhoques::Vector> m_tasks; 29 | }; 30 | 31 | struct StateTickOverride 32 | { 33 | StateTickOverride(uint32_t aHash, const char* acpRealFunctionName); 34 | ~StateTickOverride(); 35 | 36 | bool OnTick(RED4ext::IGameState*, RED4ext::CGameApplication*); 37 | 38 | uint8_t* Location = nullptr; 39 | TStateTick* RealFunction = nullptr; 40 | RepeatedTaskQueue Tasks; 41 | }; 42 | 43 | std::array m_stateTickOverrides{ 44 | StateTickOverride(CyberEngineTweaks::AddressHashes::CBaseInitializationState_OnTick, "CBaseInitializationState::OnTick"), 45 | StateTickOverride(CyberEngineTweaks::AddressHashes::CInitializationState_OnTick, "CInitializationState::OnTick"), 46 | StateTickOverride(CyberEngineTweaks::AddressHashes::CRunningState_OnTick, "CRunningState::OnTick"), 47 | StateTickOverride(CyberEngineTweaks::AddressHashes::CShutdownState_OnTick, "CShutdownState::OnTick")}; 48 | 49 | RepeatedTaskQueue m_genericQueue; 50 | }; 51 | -------------------------------------------------------------------------------- /src/scripting/GameOptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct GameOption 4 | { 5 | enum Type : uint8_t 6 | { 7 | kBoolean, 8 | kInteger, 9 | kFloat, 10 | kString, 11 | kColor 12 | }; 13 | 14 | virtual ~GameOption(); 15 | virtual bool ValueToString(RED4ext::CString& aReturn); 16 | virtual bool Get(void* apValue, Type aType); 17 | virtual bool MaybeSetFromString(const RED4ext::CString&); 18 | virtual bool Set(void* apValue, Type aType); 19 | virtual bool DefaultToString(RED4ext::CString&); 20 | virtual bool SetDefault(void* apValue, Type aType); 21 | virtual bool SetMin(void* apValue, Type aType); 22 | virtual bool SetMax(void* apValue, Type aType); 23 | virtual bool IsBounded(); 24 | virtual bool IsDefault(); // Converts both value and default to string and then does a string compare... 25 | virtual Type GetType(); 26 | virtual bool Reset(); 27 | 28 | const char* pName; 29 | const char* pCategory; 30 | uint64_t unk18; 31 | uint64_t unk20; 32 | uint8_t unk28; 33 | uint8_t flag; 34 | uint8_t pad2A[0x30 - 0x2A]; 35 | union 36 | { 37 | bool Boolean; 38 | struct 39 | { 40 | int32_t Value; 41 | int32_t Min; 42 | int32_t Max; 43 | int32_t Default; 44 | uint8_t isBounded; 45 | } Integer; 46 | struct 47 | { 48 | float Value; 49 | float Min; 50 | float Max; 51 | float Default; 52 | uint8_t isBounded; 53 | } Float; 54 | 55 | RED4ext::CString String; 56 | }; 57 | 58 | std::string GetInfo(); 59 | 60 | std::string GetString(); 61 | 62 | bool GetBool(bool& retval); 63 | bool GetInt(int& retval); 64 | bool GetFloat(float& retval); 65 | bool GetColor(int& retval); 66 | 67 | bool Set(const std::string& value); 68 | bool SetBool(bool value); 69 | bool SetInt(int value); 70 | bool SetFloat(float value); 71 | bool SetString(const std::string& value); 72 | bool SetColor(int value); 73 | 74 | bool IsA(void* apVtable) const; 75 | 76 | bool Toggle(); 77 | }; 78 | 79 | static_assert(offsetof(GameOption, unk28) == 0x28); 80 | static_assert(offsetof(GameOption, Boolean) == 0x30); 81 | static_assert(offsetof(GameOption, Integer.isBounded) == 0x40); 82 | 83 | // Struct to expose to Lua to allow these functions to be called at runtime 84 | struct GameOptions 85 | { 86 | private: 87 | static GameOption* Find(const std::string& category, const std::string& name); 88 | 89 | public: 90 | static void Print(const std::string& category, const std::string& name); 91 | 92 | static std::string Get(const std::string& category, const std::string& name); 93 | static bool GetBool(const std::string& category, const std::string& name); 94 | static int GetInt(const std::string& category, const std::string& name); 95 | static float GetFloat(const std::string& category, const std::string& name); 96 | 97 | static void Set(const std::string& category, const std::string& name, const std::string& value); 98 | static void SetBool(const std::string& category, const std::string& name, bool value); 99 | static void SetInt(const std::string& category, const std::string& name, int value); 100 | static void SetFloat(const std::string& category, const std::string& name, float value); 101 | 102 | static void Toggle(const std::string& category, const std::string& name); 103 | 104 | static void Dump(); 105 | static void List(const std::string& category); 106 | 107 | static TiltedPhoques::Vector& GetList(); 108 | }; 109 | -------------------------------------------------------------------------------- /src/scripting/LuaSandbox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Sandbox.h" 4 | 5 | struct LuaSandbox 6 | { 7 | LuaSandbox(Scripting* apScripting, const VKBindings& acVKBindings); 8 | ~LuaSandbox() = default; 9 | 10 | void Initialize(); 11 | void PostInitializeScripting(); 12 | void PostInitializeTweakDB(); 13 | void PostInitializeMods(); 14 | 15 | void ResetState(); 16 | 17 | uint64_t CreateSandbox( 18 | const std::filesystem::path& acPath = "", const std::string& acName = "", bool aEnableExtraLibs = true, bool aEnableDB = true, bool aEnableIO = true, 19 | bool aEnableLogger = true); 20 | 21 | Sandbox& operator[](uint64_t aID); 22 | const Sandbox& operator[](uint64_t aID) const; 23 | 24 | [[nodiscard]] TiltedPhoques::Locked GetLockedState() const; 25 | 26 | void SetImGuiAvailable(bool aAvailable); 27 | bool GetImGuiAvailable() const; 28 | 29 | sol::table& GetGlobals(); 30 | 31 | const bool GetIsLaunchedThroughMO2() const { return m_isLaunchedThroughMO2; } 32 | [[nodiscard]] std::filesystem::path 33 | Relative(const std::filesystem::path& acFilePath, const std::filesystem::path& acRootPath) const; 34 | [[nodiscard]] std::filesystem::path 35 | GetLuaPath(const std::string& acFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting) const; 36 | [[nodiscard]] std::filesystem::path 37 | GetLuaPath(std::filesystem::path aFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting) const; 38 | 39 | private: 40 | void InitializeExtraLibsForSandbox(Sandbox& aSandbox, const sol::state& acpState) const; 41 | void InitializeDBForSandbox(Sandbox& aSandbox, const sol::state& acpState); 42 | void InitializeIOForSandbox(Sandbox& aSandbox, const sol::state& acpState, const std::string& acName); 43 | void InitializeLoggerForSandbox(Sandbox& aSandbox, const sol::state& acpState, const std::string& acName) const; 44 | 45 | void CloseDBForSandbox(const Sandbox& aSandbox) const; 46 | 47 | Scripting* m_pScripting; 48 | const VKBindings& m_vkBindings; 49 | sol::table m_globals{}; 50 | TiltedPhoques::Vector m_sandboxes{}; 51 | TiltedPhoques::Map m_modules{}; 52 | 53 | bool m_imguiAvailable{false}; 54 | bool m_isLaunchedThroughMO2{false}; 55 | }; 56 | -------------------------------------------------------------------------------- /src/scripting/LuaVM.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "LuaVM.h" 4 | 5 | #include 6 | 7 | const VKBind* LuaVM::GetBind(const VKModBind& acModBind) const 8 | { 9 | return m_scripting.GetBind(acModBind); 10 | } 11 | 12 | const TiltedPhoques::Vector* LuaVM::GetBinds(const std::string& acModName) const 13 | { 14 | return m_scripting.GetBinds(acModName); 15 | } 16 | 17 | const TiltedPhoques::Map>>& LuaVM::GetAllBinds() const 18 | { 19 | return m_scripting.GetAllBinds(); 20 | } 21 | 22 | bool LuaVM::ExecuteLua(const std::string& acCommand) const 23 | { 24 | if (!m_initialized) 25 | { 26 | spdlog::get("scripting")->info("Command not executed! LuaVM is not yet initialized!"); 27 | return false; 28 | } 29 | 30 | return m_scripting.ExecuteLua(acCommand); 31 | } 32 | 33 | void LuaVM::Update(float aDeltaTime) 34 | { 35 | if (!m_initialized) 36 | return; 37 | 38 | CET::Get().GetBindings().Update(); 39 | 40 | if (m_reload) 41 | { 42 | m_scripting.ReloadAllMods(); 43 | 44 | m_reload = false; 45 | } 46 | 47 | m_scripting.TriggerOnUpdate(aDeltaTime); 48 | } 49 | 50 | void LuaVM::Draw() const 51 | { 52 | if (!m_initialized || m_drawBlocked) 53 | return; 54 | 55 | m_scripting.TriggerOnDraw(); 56 | } 57 | 58 | void LuaVM::ReloadAllMods() 59 | { 60 | if (m_initialized) 61 | m_reload = true; 62 | } 63 | 64 | void LuaVM::OnOverlayOpen() const 65 | { 66 | if (m_initialized) 67 | m_scripting.TriggerOnOverlayOpen(); 68 | } 69 | 70 | void LuaVM::OnOverlayClose() const 71 | { 72 | if (m_initialized) 73 | m_scripting.TriggerOnOverlayClose(); 74 | } 75 | 76 | void LuaVM::Initialize() 77 | { 78 | if (!IsInitialized()) 79 | m_scripting.Initialize(); 80 | } 81 | 82 | bool LuaVM::IsInitialized() const 83 | { 84 | return m_initialized; 85 | } 86 | 87 | void LuaVM::BlockDraw(bool aBlockDraw) 88 | { 89 | m_drawBlocked = aBlockDraw; 90 | } 91 | 92 | void LuaVM::RemoveTDBIDDerivedFrom(uint64_t aDBID) 93 | { 94 | std::lock_guard _{m_tdbidLock}; 95 | 96 | const auto it = m_tdbidDerivedLookup.find(aDBID); 97 | if (it != m_tdbidDerivedLookup.end()) 98 | { 99 | for (uint64_t flatID : it->second) 100 | { 101 | m_tdbidLookup.erase(flatID); 102 | } 103 | 104 | m_tdbidDerivedLookup.erase(it); 105 | } 106 | } 107 | 108 | bool LuaVM::GetTDBIDDerivedFrom(uint64_t aDBID, TiltedPhoques::Vector& aDerivedList) 109 | { 110 | std::lock_guard _{m_tdbidLock}; 111 | 112 | const auto it = m_tdbidDerivedLookup.find(aDBID & 0xFFFFFFFFFF); 113 | if (it == m_tdbidDerivedLookup.end()) 114 | return false; 115 | 116 | aDerivedList.reserve(it->second.size()); 117 | std::ranges::copy(it->second, std::back_inserter(aDerivedList)); 118 | return true; 119 | } 120 | 121 | uint64_t LuaVM::GetTDBIDBase(uint64_t aDBID) 122 | { 123 | std::lock_guard _{m_tdbidLock}; 124 | 125 | const auto it = m_tdbidLookup.find(aDBID & 0xFFFFFFFFFF); 126 | if (it == m_tdbidLookup.end()) 127 | return 0; 128 | return it->second.base; 129 | } 130 | 131 | TDBIDLookupEntry LuaVM::GetTDBIDLookupEntry(uint64_t aDBID) 132 | { 133 | std::lock_guard _{m_tdbidLock}; 134 | 135 | const auto it = m_tdbidLookup.find(aDBID & 0xFFFFFFFFFF); 136 | if (it == m_tdbidLookup.end()) 137 | return {0, ""}; 138 | 139 | return it->second; 140 | } 141 | 142 | std::string LuaVM::GetTDBDIDDebugString(TDBID aDBID) const 143 | { 144 | RED4ext::TweakDBID internal(aDBID.value); 145 | return internal.HasTDBOffset() ? fmt::format("", internal.name.hash, internal.name.length, internal.ToTDBOffset()) 146 | : fmt::format("", internal.name.hash, internal.name.length); 147 | } 148 | 149 | std::string LuaVM::GetTDBIDString(uint64_t aDBID, bool aOnlyRegistered) 150 | { 151 | std::lock_guard _{m_tdbidLock}; 152 | 153 | auto it = m_tdbidLookup.find(aDBID & 0xFFFFFFFFFF); 154 | if (it == m_tdbidLookup.end()) 155 | return aOnlyRegistered ? "" : GetTDBDIDDebugString(TDBID{aDBID}); 156 | 157 | std::string string = it->second.name; 158 | uint64_t base = it->second.base; 159 | while (base != 0) 160 | { 161 | it = m_tdbidLookup.find(it->second.base); 162 | if (it == m_tdbidLookup.end()) 163 | { 164 | string.insert(0, GetTDBDIDDebugString(TDBID{base})); 165 | break; 166 | } 167 | string.insert(0, it->second.name); 168 | base = it->second.base; 169 | } 170 | 171 | return string; 172 | } 173 | 174 | void LuaVM::RegisterTDBIDString(uint64_t aValue, uint64_t aBase, const std::string& acString) 175 | { 176 | if (aValue == 0) 177 | return; 178 | std::lock_guard _{m_tdbidLock}; 179 | 180 | m_tdbidLookup[aValue] = {aBase, acString}; 181 | if (aBase != 0) 182 | m_tdbidDerivedLookup[aBase].insert(aValue); 183 | } 184 | 185 | void LuaVM::PostInitializeScripting() 186 | { 187 | m_scripting.PostInitializeScripting(); 188 | } 189 | 190 | void LuaVM::PostInitializeTweakDB() 191 | { 192 | m_scripting.PostInitializeTweakDB(); 193 | } 194 | 195 | void LuaVM::PostInitializeMods() 196 | { 197 | assert(!m_initialized); 198 | 199 | m_scripting.PostInitializeMods(); 200 | 201 | spdlog::get("scripting")->info("LuaVM: initialization finished!"); 202 | 203 | m_initialized = true; 204 | } 205 | -------------------------------------------------------------------------------- /src/scripting/LuaVM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Scripting.h" 4 | #include "reverse/BasicTypes.h" 5 | 6 | typedef TweakDBID TDBID; 7 | 8 | struct UnknownString; 9 | 10 | using TSetMousePosition = BOOL(void*, HWND, long, long); 11 | using TTDBIDCtorDerive = TDBID*(const TDBID*, TDBID*, const char*); 12 | using TRunningStateRun = bool(uintptr_t, uintptr_t); 13 | using TShutdownStateRun = bool(uintptr_t, uintptr_t); 14 | using TSetLoadingState = uintptr_t(uintptr_t, int); 15 | using TTweakDBLoad = uint64_t(uintptr_t, uintptr_t); 16 | using TTranslateBytecode = void(uintptr_t, uintptr_t); 17 | using TPlayerSpawned = uint64_t(uint64_t, uint64_t, uint64_t, uint64_t); 18 | 19 | struct TDBIDLookupEntry 20 | { 21 | uint64_t base; 22 | std::string name; 23 | }; 24 | 25 | struct Image; 26 | struct LuaVM 27 | { 28 | LuaVM(const Paths& aPaths, const Options& aOptions, VKBindings& aBindings, D3D12& aD3D12); 29 | ~LuaVM() = default; 30 | 31 | [[nodiscard]] const VKBind* GetBind(const VKModBind& acModBind) const; 32 | [[nodiscard]] const TiltedPhoques::Vector* GetBinds(const std::string& acModName) const; 33 | [[nodiscard]] const TiltedPhoques::Map>>& GetAllBinds() const; 34 | 35 | void Prepare(); 36 | 37 | bool ExecuteLua(const std::string& acCommand) const; 38 | 39 | void Update(float aDeltaTime); 40 | void Draw() const; 41 | void ReloadAllMods(); 42 | 43 | void OnOverlayOpen() const; 44 | void OnOverlayClose() const; 45 | 46 | void Initialize(); 47 | 48 | bool IsInitialized() const; 49 | 50 | void BlockDraw(bool aBlockDraw); 51 | 52 | // Used by TweakDB when you delete a custom record 53 | void RemoveTDBIDDerivedFrom(uint64_t aDBID); 54 | bool GetTDBIDDerivedFrom(uint64_t aDBID, TiltedPhoques::Vector& aDerivedList); 55 | uint64_t GetTDBIDBase(uint64_t aDBID); 56 | TDBIDLookupEntry GetTDBIDLookupEntry(uint64_t aDBID); 57 | std::string GetTDBDIDDebugString(TDBID aDBID) const; 58 | std::string GetTDBIDString(uint64_t aDBID, bool aOnlyRegistered = false); 59 | 60 | void RegisterTDBIDString(uint64_t aValue, uint64_t aBase, const std::string& acString); 61 | 62 | void PostInitializeScripting(); 63 | void PostInitializeTweakDB(); 64 | void PostInitializeMods(); 65 | 66 | protected: 67 | void Hook(); 68 | 69 | static void HookLog(RED4ext::IScriptable*, RED4ext::CStackFrame* apStack, void*, void*); 70 | static void HookLogChannel(RED4ext::IScriptable*, RED4ext::CStackFrame* apStack, void*, void*); 71 | static void HookTDBIDToStringDEBUG(RED4ext::IScriptable*, RED4ext::CStackFrame* apStack, void* apResult, void*); 72 | static TDBID* HookTDBIDCtorDerive(TDBID* apBase, TDBID* apThis, const char* acpName); 73 | static uintptr_t HookSetLoadingState(uintptr_t aThis, int aState); 74 | static uint64_t HookTweakDBLoad(uintptr_t aThis, uintptr_t aParam); 75 | static void HookTranslateBytecode(uintptr_t aBinder, uintptr_t aData); 76 | static uint64_t HookPlayerSpawned(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4); 77 | 78 | private: 79 | std::mutex m_tdbidLock{}; 80 | TiltedPhoques::Map m_tdbidLookup{}; 81 | // Used by TweakDB to get the flats associated with a record 82 | TiltedPhoques::Map> m_tdbidDerivedLookup{}; 83 | 84 | RED4ext::OpcodeHandlers::Handler_t m_realLog{nullptr}; 85 | RED4ext::OpcodeHandlers::Handler_t m_realLogError{nullptr}; 86 | RED4ext::OpcodeHandlers::Handler_t m_realLogWarning{nullptr}; 87 | RED4ext::OpcodeHandlers::Handler_t m_realLogChannel{nullptr}; 88 | RED4ext::OpcodeHandlers::Handler_t m_realLogChannelWarning{nullptr}; 89 | RED4ext::OpcodeHandlers::Handler_t m_realTDBIDToStringDEBUG{nullptr}; 90 | TTDBIDCtorDerive* m_realTDBIDCtorDerive{nullptr}; 91 | TSetLoadingState* m_realSetLoadingState{nullptr}; 92 | TTweakDBLoad* m_realTweakDBLoad{nullptr}; 93 | TTranslateBytecode* m_realTranslateBytecode{nullptr}; 94 | TPlayerSpawned* m_realPlayerSpawned{nullptr}; 95 | 96 | Scripting m_scripting; 97 | 98 | bool m_initialized{false}; 99 | bool m_drawBlocked{false}; 100 | bool m_reload{false}; 101 | 102 | D3D12& m_d3d12; 103 | std::chrono::time_point m_lastframe{std::chrono::high_resolution_clock::now()}; 104 | }; 105 | -------------------------------------------------------------------------------- /src/scripting/Sandbox.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Sandbox.h" 4 | #include "Scripting.h" 5 | 6 | Sandbox::Sandbox(uint64_t aId, Scripting* apScripting, sol::table aBaseEnvironment, const std::filesystem::path& acRootPath) 7 | : m_id(aId) 8 | , m_pScripting(apScripting) 9 | , m_env(apScripting->GetLockedState().Get(), sol::create, aBaseEnvironment) 10 | , m_path(acRootPath) 11 | { 12 | } 13 | 14 | sol::protected_function_result Sandbox::ExecuteFile(const std::string& acPath) const 15 | { 16 | return m_pScripting->GetLockedState().Get().script_file(acPath, m_env, sol::load_mode::text); 17 | } 18 | 19 | sol::protected_function_result Sandbox::ExecuteString(const std::string& acString) const 20 | { 21 | return m_pScripting->GetLockedState().Get().script(acString, m_env, sol::detail::default_chunk_name(), sol::load_mode::text); 22 | } 23 | 24 | uint64_t Sandbox::GetId() const 25 | { 26 | return m_id; 27 | } 28 | 29 | sol::environment& Sandbox::GetEnvironment() 30 | { 31 | return m_env; 32 | } 33 | 34 | const std::filesystem::path& Sandbox::GetRootPath() const 35 | { 36 | return m_path; 37 | } 38 | -------------------------------------------------------------------------------- /src/scripting/Sandbox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Scripting; 4 | 5 | struct Sandbox 6 | { 7 | Sandbox(uint64_t aId, Scripting* apScripting, sol::table aBaseEnvironment, const std::filesystem::path& acRootPath); 8 | ~Sandbox() = default; 9 | 10 | sol::protected_function_result ExecuteFile(const std::string& acPath) const; 11 | sol::protected_function_result ExecuteString(const std::string& acString) const; 12 | 13 | uint64_t GetId() const; 14 | sol::environment& GetEnvironment(); 15 | const std::filesystem::path& GetRootPath() const; 16 | 17 | private: 18 | uint64_t m_id{0}; 19 | Scripting* m_pScripting; 20 | sol::environment m_env; 21 | std::filesystem::path m_path{}; 22 | }; -------------------------------------------------------------------------------- /src/scripting/ScriptContext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "LuaSandbox.h" 4 | 5 | struct ScriptContext 6 | { 7 | ScriptContext(LuaSandbox& aLuaSandbox, const std::filesystem::path& acPath, const std::string& acName); 8 | ScriptContext(ScriptContext&& other) noexcept; 9 | ~ScriptContext(); 10 | 11 | [[nodiscard]] bool IsValid() const; 12 | 13 | [[nodiscard]] const VKBind* GetBind(const std::string& acId) const; 14 | [[nodiscard]] const TiltedPhoques::Vector& GetBinds() const; 15 | 16 | void TriggerOnHook() const; 17 | void TriggerOnTweak() const; 18 | void TriggerOnInit() const; 19 | void TriggerOnUpdate(float aDeltaTime) const; 20 | void TriggerOnDraw() const; 21 | 22 | void TriggerOnOverlayOpen() const; 23 | void TriggerOnOverlayClose() const; 24 | 25 | [[nodiscard]] sol::object GetRootObject() const; 26 | 27 | private: 28 | void TriggerOnShutdown() const; 29 | 30 | ScriptContext(const ScriptContext&) = default; 31 | 32 | LuaSandbox& m_sandbox; 33 | uint64_t m_sandboxID; 34 | sol::object m_object{}; 35 | sol::function m_onHook{}; 36 | sol::function m_onTweak{}; 37 | sol::function m_onInit{}; 38 | sol::function m_onShutdown{}; 39 | sol::function m_onUpdate{}; 40 | sol::function m_onDraw{}; 41 | sol::function m_onOverlayOpen{}; 42 | sol::function m_onOverlayClose{}; 43 | TiltedPhoques::Vector m_vkBinds{}; 44 | std::string m_name{}; 45 | std::shared_ptr m_logger{nullptr}; 46 | bool m_initialized{false}; 47 | }; -------------------------------------------------------------------------------- /src/scripting/ScriptStore.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ScriptStore.h" 4 | 5 | #include 6 | 7 | #include "Utils.h" 8 | 9 | ScriptStore::ScriptStore(LuaSandbox& aLuaSandbox, const Paths& aPaths, VKBindings& aBindings) 10 | : m_sandbox(aLuaSandbox) 11 | , m_paths(aPaths) 12 | , m_bindings(aBindings) 13 | { 14 | } 15 | 16 | void ScriptStore::DiscardAll() 17 | { 18 | m_vkBinds.clear(); 19 | m_contexts.clear(); 20 | m_sandbox.ResetState(); 21 | m_bindings.Load(); 22 | } 23 | 24 | void ScriptStore::LoadAll() 25 | { 26 | DiscardAll(); 27 | 28 | const auto consoleLogger = spdlog::get("scripting"); 29 | 30 | const auto& cModsRoot = m_paths.ModsRoot(); 31 | for (const auto& file : std::filesystem::directory_iterator(cModsRoot)) 32 | { 33 | if (!file.is_directory() && !file.is_symlink()) 34 | continue; 35 | 36 | const auto name = UTF16ToUTF8(file.path().filename().native()); 37 | const auto pathStr = UTF16ToUTF8((cModsRoot / file.path()).native()); 38 | 39 | if (file.path().native().starts_with(L"cet\\")) 40 | { 41 | consoleLogger->warn("Ignoring mod using reserved name! ('{}')", pathStr); 42 | continue; 43 | } 44 | 45 | const auto path = GetAbsolutePath(file.path(), cModsRoot, false); 46 | if (path.empty()) 47 | { 48 | consoleLogger->error("Tried to access invalid directory! ('{}')", pathStr); 49 | continue; 50 | } 51 | 52 | if (!is_directory(path)) 53 | { 54 | // no error message, path was probably symlink to file 55 | continue; 56 | } 57 | 58 | if (!exists(path / L"init.lua")) 59 | { 60 | consoleLogger->warn("Ignoring mod which does not contain init.lua! ('{}')", pathStr); 61 | continue; 62 | } 63 | 64 | auto ctx = ScriptContext{m_sandbox, path, name}; 65 | if (ctx.IsValid()) 66 | { 67 | m_contexts.emplace(name, std::move(ctx)); 68 | consoleLogger->info("Mod {} loaded! ('{}')", name, pathStr); 69 | } 70 | else 71 | consoleLogger->error("Mod {} failed to load! ('{}')", name, pathStr); 72 | } 73 | 74 | for (auto& contextIt : m_contexts) 75 | m_vkBinds.insert({contextIt.first, contextIt.second.GetBinds()}); 76 | 77 | m_bindings.InitializeMods(m_vkBinds); 78 | } 79 | 80 | const VKBind* ScriptStore::GetBind(const VKModBind& acModBind) const 81 | { 82 | if (acModBind == Bindings::GetOverlayToggleModBind()) 83 | return &Bindings::GetOverlayToggleBind(); 84 | 85 | const auto it = m_contexts.find(acModBind.ModName); 86 | if (it != m_contexts.cend()) 87 | return it->second.GetBind(acModBind.ID); 88 | 89 | return nullptr; 90 | } 91 | 92 | const TiltedPhoques::Vector* ScriptStore::GetBinds(const std::string& acModName) const 93 | { 94 | const auto it = m_contexts.find(acModName); 95 | if (it != m_contexts.cend()) 96 | { 97 | return &it->second.GetBinds(); 98 | } 99 | 100 | return nullptr; 101 | } 102 | 103 | const TiltedPhoques::Map>>& ScriptStore::GetAllBinds() const 104 | { 105 | return m_vkBinds; 106 | } 107 | 108 | void ScriptStore::TriggerOnHook() const 109 | { 110 | for (const auto& mod : m_contexts | std::views::values) 111 | mod.TriggerOnHook(); 112 | } 113 | 114 | void ScriptStore::TriggerOnTweak() const 115 | { 116 | for (const auto& mod : m_contexts | std::views::values) 117 | mod.TriggerOnTweak(); 118 | } 119 | 120 | void ScriptStore::TriggerOnInit() const 121 | { 122 | for (const auto& mod : m_contexts | std::views::values) 123 | mod.TriggerOnInit(); 124 | } 125 | 126 | void ScriptStore::TriggerOnUpdate(float aDeltaTime) const 127 | { 128 | for (const auto& mod : m_contexts | std::views::values) 129 | mod.TriggerOnUpdate(aDeltaTime); 130 | } 131 | 132 | void ScriptStore::TriggerOnDraw() const 133 | { 134 | for (const auto& mod : m_contexts | std::views::values) 135 | mod.TriggerOnDraw(); 136 | } 137 | 138 | void ScriptStore::TriggerOnOverlayOpen() const 139 | { 140 | for (const auto& mod : m_contexts | std::views::values) 141 | mod.TriggerOnOverlayOpen(); 142 | } 143 | 144 | void ScriptStore::TriggerOnOverlayClose() const 145 | { 146 | for (const auto& mod : m_contexts | std::views::values) 147 | mod.TriggerOnOverlayClose(); 148 | } 149 | 150 | sol::object ScriptStore::GetMod(const std::string& acName) const 151 | { 152 | const auto it = m_contexts.find(acName); 153 | if (it != m_contexts.cend()) 154 | return it->second.GetRootObject(); 155 | 156 | return sol::nil; 157 | } 158 | -------------------------------------------------------------------------------- /src/scripting/ScriptStore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ScriptContext.h" 4 | 5 | struct ScriptStore 6 | { 7 | ScriptStore(LuaSandbox& aLuaSandbox, const Paths& aPaths, VKBindings& aBindings); 8 | ~ScriptStore() = default; 9 | 10 | void DiscardAll(); 11 | void LoadAll(); 12 | 13 | [[nodiscard]] const VKBind* GetBind(const VKModBind& acModBind) const; 14 | [[nodiscard]] const TiltedPhoques::Vector* GetBinds(const std::string& acModName) const; 15 | [[nodiscard]] const TiltedPhoques::Map>>& GetAllBinds() const; 16 | 17 | void TriggerOnHook() const; 18 | void TriggerOnTweak() const; 19 | void TriggerOnInit() const; 20 | void TriggerOnUpdate(float aDeltaTime) const; 21 | void TriggerOnDraw() const; 22 | 23 | void TriggerOnOverlayOpen() const; 24 | void TriggerOnOverlayClose() const; 25 | 26 | [[nodiscard]] sol::object GetMod(const std::string& acName) const; 27 | 28 | private: 29 | TiltedPhoques::Map m_contexts{}; 30 | TiltedPhoques::Map>> m_vkBinds{}; 31 | LuaSandbox& m_sandbox; 32 | const Paths& m_paths; 33 | VKBindings& m_bindings; 34 | }; -------------------------------------------------------------------------------- /src/scripting/Scripting.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "FunctionOverride.h" 4 | #include "ScriptStore.h" 5 | #include "reverse/RTTIMapper.h" 6 | #include "reverse/SingletonReference.h" 7 | 8 | struct D3D12; 9 | 10 | struct Scripting 11 | { 12 | using LockedState = TiltedPhoques::Locked; 13 | 14 | Scripting(const Paths& aPaths, const Options& aOptions, VKBindings& aBindings, D3D12& aD3D12); 15 | ~Scripting() = default; 16 | 17 | void Initialize(); 18 | void PostInitializeScripting(); 19 | void PostInitializeTweakDB(); 20 | void PostInitializeMods(); 21 | 22 | [[nodiscard]] const VKBind* GetBind(const VKModBind& acModBind) const; 23 | [[nodiscard]] const TiltedPhoques::Vector* GetBinds(const std::string& acModName) const; 24 | [[nodiscard]] const TiltedPhoques::Map>>& GetAllBinds() const; 25 | 26 | void TriggerOnHook() const; 27 | void TriggerOnTweak() const; 28 | void TriggerOnInit() const; 29 | void TriggerOnUpdate(float aDeltaTime) const; 30 | void TriggerOnDraw() const; 31 | void TriggerOnOverlayOpen() const; 32 | void TriggerOnOverlayClose() const; 33 | 34 | sol::object GetMod(const std::string& acName) const; 35 | void UnloadAllMods(); 36 | void ReloadAllMods(); 37 | bool ExecuteLua(const std::string& acCommand) const; 38 | void CollectGarbage() const; 39 | 40 | LuaSandbox& GetSandbox(); 41 | LockedState GetLockedState() const noexcept; 42 | const std::filesystem::path& GameRoot() const { return m_paths.GameRoot(); } 43 | 44 | static size_t Size(RED4ext::CBaseRTTIType* apRttiType); 45 | static sol::object ToLua(LockedState& aState, RED4ext::CStackType& aResult); 46 | static RED4ext::CStackType ToRED(sol::object aObject, RED4ext::CBaseRTTIType* apRttiType, TiltedPhoques::Allocator* apAllocator); 47 | static void ToRED(sol::object aObject, RED4ext::CStackType& apType); 48 | 49 | protected: 50 | void RegisterOverrides(); 51 | 52 | sol::object Index(const std::string& acName, sol::this_state aState, sol::this_environment aEnv); 53 | sol::object GetSingletonHandle(const std::string& acName, sol::this_environment aThisEnv); 54 | 55 | private: 56 | TiltedPhoques::Lockable m_lua; 57 | TiltedPhoques::Map m_properties{}; 58 | TiltedPhoques::Map m_singletons{}; 59 | LuaSandbox m_sandbox; 60 | RTTIMapper m_mapper; 61 | ScriptStore m_store; 62 | FunctionOverride m_override; 63 | const Paths& m_paths; 64 | const Options& m_options; 65 | D3D12& m_d3d12; 66 | }; 67 | -------------------------------------------------------------------------------- /src/scripting/Texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Texture 4 | { 5 | static void BindTexture(sol::table& aTable); 6 | static std::shared_ptr Load(const std::string& acPath); 7 | static void ImGuiImage( 8 | const Texture& acTexture, ImVec2 aSize = ImVec2(0, 0), const ImVec2& aUv0 = ImVec2(0, 0), const ImVec2& aUv1 = ImVec2(1, 1), const ImVec4& aTintCol = ImVec4(1, 1, 1, 1), 9 | const ImVec4& aBorderCol = ImVec4(0, 0, 0, 0)); 10 | 11 | ImVec2 GetSize() const; 12 | 13 | void Release(); 14 | 15 | ~Texture() = default; 16 | 17 | private: 18 | Microsoft::WRL::ComPtr m_texture; 19 | D3D12_GPU_DESCRIPTOR_HANDLE m_handle; 20 | ImVec2 m_size{}; 21 | }; 22 | -------------------------------------------------------------------------------- /src/sol_imgui/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 MSeys 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "reverse/Relocation.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "RED4ext/GameApplication.hpp" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | 70 | #include "common/Logging.h" 71 | #include "common/FontMaterialDesignIcons.h" 72 | #include "common/ImGuiNotify.h" 73 | #include "Options.h" 74 | #include "Paths.h" 75 | #include "PersistentState.h" 76 | #include "reverse/Addresses.h" 77 | #include "scripting/GameHooks.h" 78 | #include "VKBindings.h" 79 | 80 | template <> struct std::hash 81 | { 82 | std::size_t operator()(RED4ext::CName aKey) const noexcept { return static_cast(aKey.hash); } 83 | }; 84 | -------------------------------------------------------------------------------- /src/window/window.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "window.h" 4 | 5 | #include 6 | 7 | using namespace std::chrono_literals; 8 | 9 | static Window* s_pWindow = nullptr; 10 | 11 | static BOOL CALLBACK EnumWindowsProcCP77(HWND ahWnd, LPARAM alParam) 12 | { 13 | DWORD lpdwProcessId; 14 | GetWindowThreadProcessId(ahWnd, &lpdwProcessId); 15 | if (lpdwProcessId == GetCurrentProcessId()) 16 | { 17 | wchar_t name[512] = {0}; 18 | RealGetWindowClassW(ahWnd,name,511); 19 | if (wcscmp (L"W2ViewportClass", name) == 0) 20 | { 21 | *reinterpret_cast(alParam) = ahWnd; 22 | return FALSE; 23 | } 24 | } 25 | return TRUE; 26 | } 27 | 28 | LRESULT APIENTRY Window::WndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam) 29 | { 30 | if (s_pWindow) 31 | { 32 | if (auMsg == WM_WINDOWPOSCHANGED) 33 | { 34 | const auto* wp = reinterpret_cast(alParam); 35 | 36 | if ((wp->flags & SWP_NOMOVE) == 0) 37 | s_pWindow->m_wndPos = {wp->x, wp->y}; 38 | if ((wp->flags & SWP_NOSIZE) == 0) 39 | s_pWindow->m_wndSize = {wp->cx, wp->cy}; 40 | 41 | RECT cr; 42 | GetClientRect(ahWnd, &cr); 43 | s_pWindow->m_clientPos = {cr.left, cr.top}; 44 | s_pWindow->m_clientSize = {cr.right - cr.left, cr.bottom - cr.top}; 45 | } 46 | 47 | { 48 | if (s_pWindow->m_pBindings->OnWndProc(ahWnd, auMsg, awParam, alParam)) 49 | return 0; // VKBindings wants this input ignored! 50 | } 51 | 52 | { 53 | if (s_pWindow->m_pD3D12->OnWndProc(ahWnd, auMsg, awParam, alParam)) 54 | return 0; // D3D12 wants this input ignored! 55 | } 56 | 57 | return CallWindowProc(s_pWindow->m_wndProc, ahWnd, auMsg, awParam, alParam); 58 | } 59 | 60 | return 0; 61 | } 62 | 63 | Window::Window(VKBindings* apBindings, D3D12* apD3D12) 64 | : m_pBindings(apBindings) 65 | , m_pD3D12(apD3D12) 66 | { 67 | s_pWindow = this; 68 | 69 | std::thread t( 70 | [this] 71 | { 72 | while (m_hWnd == nullptr) 73 | { 74 | if (EnumWindows(EnumWindowsProcCP77, reinterpret_cast(&m_hWnd))) 75 | std::this_thread::sleep_for(50ms); 76 | else 77 | { 78 | m_wndProc = reinterpret_cast(SetWindowLongPtr(m_hWnd, GWLP_WNDPROC, reinterpret_cast(WndProc))); 79 | Log::Info("Window::Initialize() - window hook complete."); 80 | } 81 | } 82 | 83 | m_initialized = true; 84 | }); 85 | 86 | t.detach(); 87 | } 88 | 89 | Window::~Window() 90 | { 91 | s_pWindow = nullptr; 92 | 93 | if (m_hWnd != nullptr) 94 | SetWindowLongPtr(m_hWnd, GWLP_WNDPROC, reinterpret_cast(m_wndProc)); 95 | } 96 | -------------------------------------------------------------------------------- /src/window/window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct D3D12; 4 | struct VKBindings; 5 | 6 | struct Window 7 | { 8 | Window(VKBindings* apBindings, D3D12* apD3D12); 9 | ~Window(); 10 | 11 | HWND GetWindow() const { return m_hWnd; } 12 | 13 | POINT GetWndPos() const { return m_wndPos; } 14 | SIZE GetWndSize() const { return m_wndSize; } 15 | 16 | POINT GetClientPos() const { return m_clientPos; } 17 | SIZE GetClientSize() const { return m_clientSize; } 18 | 19 | bool IsInitialized() const { return m_initialized; } 20 | 21 | protected: 22 | static LRESULT APIENTRY WndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam); 23 | 24 | private: 25 | bool m_initialized{false}; 26 | 27 | HWND m_hWnd{nullptr}; 28 | WNDPROC m_wndProc{nullptr}; 29 | 30 | POINT m_wndPos{}; 31 | SIZE m_wndSize{}; 32 | POINT m_clientPos{}; 33 | SIZE m_clientSize{}; 34 | 35 | VKBindings* m_pBindings; 36 | D3D12* m_pD3D12; 37 | }; 38 | -------------------------------------------------------------------------------- /src/xmake/CETVersion.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | ${define CET_DEBUG_BUILD} 4 | ${define CET_GIT_TAG} 5 | ${define CET_GIT_TAG_LONG} 6 | ${define CET_GIT_BRANCH} 7 | ${define CET_GIT_COMMIT} 8 | ${define CET_GIT_COMMIT_LONG} 9 | ${define CET_GIT_COMMIT_DATE} 10 | 11 | ${define CET_VERSION_MAJOR} 12 | ${define CET_VERSION_MINOR} 13 | ${define CET_VERSION_PATCH} 14 | ${define CET_VERSION_SIMPLE} 15 | ${define CET_VERSION_FULL} 16 | ${define CET_VERSION} 17 | 18 | ${define CET_CURRENT_YEAR} 19 | ${define CET_PRODUCT_NAME} 20 | ${define CET_GROUP_NAME} 21 | ${define CET_FILE_NAME} 22 | -------------------------------------------------------------------------------- /src/xmake/Resource.rc.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximegmd/CyberEngineTweaks/12873cefce4be2aada4722bcd8d6dd9f2026c64e/src/xmake/Resource.rc.in -------------------------------------------------------------------------------- /vendor/asiloader/global.ini: -------------------------------------------------------------------------------- 1 | [GlobalSets] 2 | LoadPlugins=1 3 | LoadFromScriptsOnly=1 4 | DontLoadFromDllMain=0 5 | FindModule=0 6 | UseD3D8to9=0 7 | DisableCrashDumps=0 8 | Direct3D8DisableMaximizedWindowedModeShim=0 -------------------------------------------------------------------------------- /vendor/asiloader/version.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximegmd/CyberEngineTweaks/12873cefce4be2aada4722bcd8d6dd9f2026c64e/vendor/asiloader/version.dll --------------------------------------------------------------------------------