├── .clang-format ├── .editorconfig ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── config ├── Project.hpp.in ├── Version.rc.in └── version.lua.in ├── data └── Resources.txt ├── lib ├── Core │ ├── Container │ │ ├── Container.hpp │ │ └── Registry.hpp │ ├── Facades │ │ ├── Container.hpp │ │ ├── Hook.hpp │ │ ├── Log.hpp │ │ ├── Runtime.cpp │ │ └── Runtime.hpp │ ├── Foundation │ │ ├── Application.cpp │ │ ├── Application.hpp │ │ ├── Feature.hpp │ │ ├── LocaleProvider.hpp │ │ ├── RuntimeProvider.cpp │ │ └── RuntimeProvider.hpp │ ├── Hooking │ │ ├── Detail.hpp │ │ ├── HookingAgent.cpp │ │ ├── HookingAgent.hpp │ │ ├── HookingDriver.cpp │ │ └── HookingDriver.hpp │ ├── Logging │ │ ├── LoggingAgent.cpp │ │ ├── LoggingAgent.hpp │ │ ├── LoggingDriver.cpp │ │ └── LoggingDriver.hpp │ ├── Memory │ │ ├── AddressResolver.cpp │ │ └── AddressResolver.hpp │ ├── Raw.hpp │ ├── Runtime │ │ ├── HostImage.cpp │ │ ├── HostImage.hpp │ │ ├── ModuleImage.cpp │ │ ├── ModuleImage.hpp │ │ ├── OwnerMutex.cpp │ │ └── OwnerMutex.hpp │ ├── Stl.hpp │ └── Win.hpp ├── Red │ ├── Alias.hpp │ ├── Engine.hpp │ ├── Engine │ │ ├── Framework.hpp │ │ ├── LogChannel.hpp │ │ ├── Macros │ │ │ └── Framework.hpp │ │ └── Mappings.hpp │ ├── Specializations.hpp │ ├── TypeInfo.hpp │ ├── TypeInfo │ │ ├── Common.hpp │ │ ├── Construction.hpp │ │ ├── Definition.hpp │ │ ├── Invocation.hpp │ │ ├── Macros │ │ │ ├── Definition.hpp │ │ │ └── Resolving.hpp │ │ ├── Mappings.hpp │ │ ├── Parameters.hpp │ │ ├── Properties.hpp │ │ ├── Registrar.hpp │ │ └── Resolving.hpp │ ├── Utils.hpp │ └── Utils │ │ ├── Handles.hpp │ │ ├── JobQueues.hpp │ │ └── Resources.hpp └── Support │ ├── MinHook │ ├── MinHookProvider.cpp │ └── MinHookProvider.hpp │ ├── RED4ext │ ├── RED4extProvider.cpp │ └── RED4extProvider.hpp │ ├── RedLib │ ├── RedLibProvider.cpp │ └── RedLibProvider.hpp │ └── Spdlog │ ├── SpdlogProvider.cpp │ └── SpdlogProvider.hpp ├── src ├── App │ ├── Application.cpp │ ├── Application.hpp │ ├── Archives │ │ ├── ArchiveLoader.cpp │ │ ├── ArchiveLoader.hpp │ │ ├── ArchiveLogger.Data.cpp │ │ ├── ArchiveLogger.cpp │ │ ├── ArchiveLogger.hpp │ │ ├── ArchiveWatcher.cpp │ │ └── ArchiveWatcher.hpp │ ├── Environment.hpp │ ├── Facade.cpp │ ├── Facade.hpp │ ├── Foundation │ │ ├── AbstractWatcher.cpp │ │ └── AbstractWatcher.hpp │ ├── Scripts │ │ ├── ObjectRegistry.cpp │ │ ├── ObjectRegistry.hpp │ │ ├── ScriptLoader.cpp │ │ ├── ScriptLoader.hpp │ │ ├── ScriptLogger.cpp │ │ ├── ScriptLogger.hpp │ │ ├── ScriptReporter.cpp │ │ ├── ScriptReporter.hpp │ │ ├── ScriptWatcher.cpp │ │ └── ScriptWatcher.hpp │ ├── Shared │ │ ├── ResourcePathRegistry.cpp │ │ └── ResourcePathRegistry.hpp │ ├── Tweaks │ │ ├── TweakLoader.cpp │ │ ├── TweakLoader.hpp │ │ ├── TweakWatcher.cpp │ │ └── TweakWatcher.hpp │ ├── UI │ │ ├── InkInspector.cpp │ │ ├── InkInspector.hpp │ │ ├── InkWidgetCollector.cpp │ │ └── InkWidgetCollector.hpp │ └── World │ │ ├── PhysicsTraceResult.hpp │ │ ├── WorldInspector.cpp │ │ ├── WorldInspector.hpp │ │ ├── WorldNodeRegistry.cpp │ │ └── WorldNodeRegistry.hpp ├── Red │ ├── Addresses │ │ └── Library.hpp │ ├── AsyncFileAPI.cpp │ ├── AsyncFileAPI.hpp │ ├── CameraSystem.hpp │ ├── Common.hpp │ ├── CommunitySystem.hpp │ ├── Debug.hpp │ ├── EngineConfig.cpp │ ├── EngineConfig.hpp │ ├── FileSystem.cpp │ ├── FileSystem.hpp │ ├── GameEngine.hpp │ ├── ISerializable.hpp │ ├── InkContext.cpp │ ├── InkContext.hpp │ ├── InkInput.cpp │ ├── InkInput.hpp │ ├── InkLayer.hpp │ ├── InkLibrary.hpp │ ├── InkSpawner.hpp │ ├── InkSystem.cpp │ ├── InkSystem.hpp │ ├── InkWidget.hpp │ ├── InkWindow.hpp │ ├── Input.hpp │ ├── JobHandle.hpp │ ├── Logger.hpp │ ├── Math.hpp │ ├── NodeRef.hpp │ ├── Physics.hpp │ ├── Rendering.hpp │ ├── ResourceBank.hpp │ ├── ResourceDepot.hpp │ ├── ResourcePath.hpp │ ├── ScriptBinder.cpp │ ├── ScriptBinder.hpp │ ├── ScriptBundle.cpp │ ├── ScriptBundle.hpp │ ├── ScriptCompiler.hpp │ ├── ScriptReport.hpp │ ├── ScriptValidator.hpp │ ├── Scripting.hpp │ ├── SharedStorage.hpp │ ├── StreamingSector.hpp │ ├── Strings.hpp │ ├── Transform.hpp │ └── WorldNode.hpp ├── main.cpp ├── pch.cpp └── pch.hpp ├── support ├── cet │ ├── init.lua │ ├── libs │ │ ├── Cron.lua │ │ ├── Enumeration.lua │ │ ├── ImGuiEx.lua │ │ ├── MathEx.lua │ │ ├── PersistentState.lua │ │ └── Ref.lua │ ├── modules │ │ ├── hot │ │ │ └── main.lua │ │ ├── ink │ │ │ └── main.lua │ │ └── world │ │ │ └── main.lua │ └── version.lua └── vscode │ ├── .vscode │ ├── launch.json │ └── settings.json │ ├── .vscodeignore │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ └── extension.ts │ └── tsconfig.json ├── tools └── dist │ ├── package-addons.ps1 │ ├── package-mod.ps1 │ ├── readme.md │ └── steps │ ├── compile-vscode-ext.ps1 │ ├── compose-cet-mod.ps1 │ ├── compose-plugin-asi.ps1 │ ├── compose-plugin-data.ps1 │ ├── compose-plugin-red4ext.ps1 │ ├── create-zip-from-stage.ps1 │ └── install-from-stage.ps1 ├── vendor ├── filewatch │ └── FileWatch.hpp ├── semver │ ├── .github │ │ ├── FUNDING.yml │ │ └── workflows │ │ │ ├── macos.yml │ │ │ ├── ubuntu.yml │ │ │ └── windows.yml │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── example │ │ ├── basic_example.cpp │ │ └── range_example.cpp │ ├── include │ │ └── semver.hpp │ └── test │ │ ├── 3rdparty │ │ └── Catch2 │ │ │ ├── LICENSE │ │ │ └── catch.hpp │ │ └── test.cpp └── wil │ ├── .gitattributes │ ├── .gitignore │ ├── CODE_OF_CONDUCT.md │ ├── LICENSE │ ├── README.md │ ├── ThirdPartyNotices.txt │ ├── cmake │ └── common_build_flags.cmake │ ├── include │ └── wil │ │ ├── Tracelogging.h │ │ ├── com.h │ │ ├── com_apartment_variable.h │ │ ├── common.h │ │ ├── cppwinrt.h │ │ ├── cppwinrt_helpers.h │ │ ├── cppwinrt_wrl.h │ │ ├── filesystem.h │ │ ├── nt_result_macros.h │ │ ├── registry.h │ │ ├── resource.h │ │ ├── result.h │ │ ├── result_macros.h │ │ ├── result_originate.h │ │ ├── rpc_helpers.h │ │ ├── safecast.h │ │ ├── stl.h │ │ ├── token_helpers.h │ │ ├── traceloggingconfig.h │ │ ├── win32_helpers.h │ │ ├── winrt.h │ │ ├── wistd_config.h │ │ ├── wistd_functional.h │ │ ├── wistd_memory.h │ │ ├── wistd_type_traits.h │ │ └── wrl.h │ ├── natvis │ └── wil.natvis │ ├── packaging │ └── nuget │ │ ├── Microsoft.Windows.ImplementationLibrary.nuspec │ │ └── Microsoft.Windows.ImplementationLibrary.targets │ ├── scripts │ ├── azure-pipelines.yml │ ├── build_all.cmd │ ├── init.cmd │ ├── init_all.cmd │ └── runtests.cmd │ └── tests │ ├── ComApartmentVariableTests.cpp │ ├── ComTests.cpp │ ├── CommonTests.cpp │ ├── CppWinRT20Tests.cpp │ ├── CppWinRTTests.cpp │ ├── FakeWinRTTypes.h │ ├── FileSystemTests.cpp │ ├── MallocSpy.h │ ├── NtResultTests.cpp │ ├── ResourceTests.cpp │ ├── ResultTests.cpp │ ├── Rpc.cpp │ ├── SafeCastTests.cpp │ ├── StlTests.cpp │ ├── TokenHelpersTests.cpp │ ├── UniqueWinRTEventTokenTests.cpp │ ├── WatcherTests.cpp │ ├── WinRTTests.cpp │ ├── WinVerifyTrustTest.cpp │ ├── WistdTests.cpp │ ├── catch.hpp │ ├── common.h │ ├── main.cpp │ ├── test_objects.h │ ├── wiTest.cpp │ └── workarounds │ ├── readme.md │ └── wrl │ └── wrl │ ├── async.h │ └── implements.h └── xmake.lua /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Microsoft 3 | --- 4 | Language: Cpp 5 | 6 | AccessModifierOffset: -4 7 | AlwaysBreakTemplateDeclarations: Yes 8 | BreakConstructorInitializers: BeforeComma 9 | BreakInheritanceList: BeforeComma 10 | KeepEmptyLinesAtTheStartOfBlocks: false 11 | NamespaceIndentation: Inner 12 | PointerAlignment: Left 13 | SpaceAfterTemplateKeyword: false 14 | IndentCaseLabels: false 15 | BreakBeforeBraces: Custom 16 | BraceWrapping: 17 | AfterCaseLabel: true 18 | AfterClass: true 19 | AfterControlStatement: Always 20 | AfterEnum: true 21 | AfterFunction: true 22 | AfterNamespace: true 23 | AfterStruct: true 24 | AfterUnion: true 25 | AfterExternBlock: true 26 | BeforeCatch: true 27 | BeforeElse: true 28 | IndentBraces: false 29 | SplitEmptyFunction: true 30 | SplitEmptyRecord: true 31 | SplitEmptyNamespace: true 32 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | 8 | [*.{h,c,cpp,hpp}] 9 | indent_style = space 10 | indent_size = 4 11 | 12 | [*.{yml,yaml}] 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.json] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.vs 3 | /.vscode 4 | /.xmake 5 | /build 6 | /dev 7 | /src/App/Staging 8 | /src/App/Project.hpp 9 | /src/App/Version.rc 10 | /support/cet/**/.* 11 | /support/cet/*.log 12 | /support/cet/*.sqlite3 13 | /support/vscode/out 14 | /support/vscode/node_modules 15 | /support/vscode/*.log 16 | /support/vscode/*.vsix 17 | CMakeLists.txt 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/RED4ext.SDK"] 2 | path = vendor/RED4ext.SDK 3 | url = git@github.com:psiberx/RED4ext.SDK.git 4 | [submodule "vendor/wil"] 5 | path = vendor/wil 6 | url = git@github.com:microsoft/wil.git 7 | [submodule "vendor/semver"] 8 | path = vendor/semver 9 | url = git@github.com:Neargye/semver.git 10 | [submodule "vendor/nameof"] 11 | path = vendor/nameof 12 | url = git@github.com:Neargye/nameof.git 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Pavel Siberx 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 | # Red Hot Tools 2 | 3 | This plugin aims to help in the Cyberpunk 2077 mods development, offering the following features: 4 | 5 | - Inspect game entities and world nodes 6 | - Inspect ink layers and widgets (user interface) 7 | - Hot reload archives, scripts and tweaks 8 | - Prevent game from starting if scripts compilation fails 9 | 10 | For greater convenience it integrates with other tools: 11 | 12 | - **WolvenKit**: Pack & reload archives from the editor with a press of a button 13 | - **Visual Studio Code**: Reload scripts and tweaks from the editor using context menu or hotkeys 14 | - **Cyber Engine Tweaks**: Reload assets using in-game menu and inspect world nodes and entities 15 | 16 | ## Getting Started 17 | 18 | ### Compatibility 19 | 20 | - Cyberpunk 2077 2.21 21 | - [ArchiveXL](https://github.com/psiberx/cp2077-archive-xl) 1.21.0+ 22 | - [Cyber Engine Tweaks](https://github.com/yamashi/CyberEngineTweaks) 1.35.0+ 23 | - [redscript](https://github.com/jac3km4/redscript) 0.5.25+ 24 | 25 | ### Installation 26 | 27 | 1. Install requirements: 28 | - [RED4ext](https://docs.red4ext.com/getting-started/installing-red4ext) 1.27.0+ 29 | 2. Extract the release archive `RedHotTools-x.x.x.zip` into the Cyberpunk 2077 directory. 30 | 3. _(Optional)_ Install CET UI: Extract the archive `RedHotTools-x.x.x-Overlay.zip` into the Cyberpunk 2077 directory. 31 | 4. _(Optional)_ Install VS Code extension: Drag the `red-hot-vscode-x.x.x.vsix` onto your VS Code extension bar. 32 | 33 | ## Visual Studio Code 34 | 35 | With extension installed, when editing scripts or tweaks files in VS Code, 36 | you'll get new editor commands in the menu to hot reload the scripts or tweaks. 37 | As any other command they can also be bound to hotkeys. 38 | 39 | ## Limitations 40 | 41 | - Struct fields are not reinitialized (if you change the fields of a struct and an instance of that struct exists during hot reload, this can lead to issues) 42 | - Handlers for scriptable systems requests aren't registered on scripts load, if you add a new handler you have to reload the game session 43 | -------------------------------------------------------------------------------- /config/Project.hpp.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Generated by xmake from config/Project.hpp.in 4 | 5 | #define BUILD_SUFFIX ".${MODE}" 6 | #define DEBUG_SUFFIX ".${DEBUG}" 7 | 8 | #include 9 | 10 | namespace App::Project 11 | { 12 | constexpr auto Name = "${NAME}"; 13 | constexpr auto Author = "${AUTHOR}"; 14 | 15 | constexpr auto NameW = L"${NAME}"; 16 | constexpr auto AuthorW = L"${AUTHOR}"; 17 | 18 | constexpr auto Version = semver::from_string_noexcept("${VERSION}").value(); 19 | } 20 | -------------------------------------------------------------------------------- /config/Version.rc.in: -------------------------------------------------------------------------------- 1 | #define VER_PRODUCTVERSION ${VERSION_MAJOR},${VERSION_MINOR},${VERSION_ALTER},0 2 | #define VER_FILEVERSION ${VERSION_MAJOR},${VERSION_MINOR},${VERSION_ALTER},${VERSION_BUILD} 3 | 4 | #define VER_PRODUCTNAME_STR "${NAME}\0" 5 | #define VER_PRODUCTVERSION_STR "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_ALTER}\0" 6 | #define VER_FILEVERSION_STR "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_ALTER}.${VERSION_BUILD}\0" 7 | 8 | 1 VERSIONINFO 9 | FILEVERSION VER_FILEVERSION 10 | PRODUCTVERSION VER_PRODUCTVERSION 11 | FILEFLAGSMASK 0x17L 12 | FILEFLAGS 0x0L 13 | FILEOS 0x4L 14 | FILETYPE 0x2L 15 | FILESUBTYPE 0x0L 16 | BEGIN 17 | BLOCK "StringFileInfo" 18 | BEGIN 19 | BLOCK "040904b0" 20 | BEGIN 21 | VALUE "CompanyName", "\0" 22 | VALUE "FileDescription", "\0" 23 | VALUE "FileVersion", VER_FILEVERSION_STR 24 | VALUE "InternalName", "\0" 25 | VALUE "LegalCopyright", "\0" 26 | VALUE "LegalTrademarks1", "\0" 27 | VALUE "LegalTrademarks2", "\0" 28 | VALUE "OriginalFilename", "\0" 29 | VALUE "ProductName", VER_PRODUCTNAME_STR 30 | VALUE "ProductVersion", VER_PRODUCTVERSION_STR 31 | END 32 | END 33 | BLOCK "VarFileInfo" 34 | BEGIN 35 | VALUE "Translation", 0x409, 1200 36 | END 37 | END 38 | -------------------------------------------------------------------------------- /config/version.lua.in: -------------------------------------------------------------------------------- 1 | return '${VERSION}' 2 | -------------------------------------------------------------------------------- /lib/Core/Container/Container.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Stl.hpp" 4 | 5 | namespace Core 6 | { 7 | class Container 8 | { 9 | public: 10 | template 11 | inline static void Set(const Core::SharedPtr& aInstance) 12 | { 13 | Resolver::Assign(aInstance); 14 | } 15 | 16 | template 17 | inline static Core::SharedPtr Get() 18 | { 19 | return Resolver::Retrieve().lock(); 20 | } 21 | 22 | template 23 | inline static bool Has() 24 | { 25 | return !Resolver::Retrieve().expired(); 26 | } 27 | 28 | private: 29 | template 30 | struct Resolver 31 | { 32 | inline static void Assign(const Core::SharedPtr& aInstance) 33 | { 34 | s_instance = aInstance; 35 | } 36 | 37 | inline static Core::WeakPtr& Retrieve() 38 | { 39 | return s_instance; 40 | } 41 | 42 | inline static Core::WeakPtr s_instance; 43 | }; 44 | }; 45 | } -------------------------------------------------------------------------------- /lib/Core/Container/Registry.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Container/Container.hpp" 4 | #include "Core/Stl.hpp" 5 | 6 | namespace Core 7 | { 8 | template 9 | class RegistryProxy; 10 | 11 | template 12 | class Registry 13 | { 14 | public: 15 | template 16 | requires std::is_base_of_v 17 | T& Register(Args&&... aArgs) 18 | { 19 | auto registrable = Core::MakeShared(std::forward(aArgs)...); 20 | m_registered.push_back(registrable); 21 | Container::Set(registrable); 22 | 23 | if constexpr (std::is_base_of_v, T>) 24 | { 25 | registrable->SetParent(*this); 26 | } 27 | 28 | OnRegistered(registrable); 29 | 30 | return *registrable.get(); 31 | } 32 | 33 | protected: 34 | using TraverseFunc = void (*)(const Core::SharedPtr&); 35 | 36 | void ForEach(TraverseFunc aTraverse) const 37 | { 38 | std::for_each(m_registered.begin(), m_registered.end(), aTraverse); 39 | } 40 | 41 | const Core::Vector>& GetRegistered() const 42 | { 43 | return m_registered; 44 | } 45 | 46 | template 47 | inline static Core::SharedPtr Resolve() 48 | { 49 | return Container::Get(); 50 | } 51 | 52 | template 53 | inline static bool Resolvable() 54 | { 55 | return Container::Get(); 56 | } 57 | 58 | virtual void OnRegistered(const Core::SharedPtr& aRegistred) {} 59 | 60 | private: 61 | Core::Vector> m_registered; 62 | }; 63 | 64 | template 65 | class RegistryProxy 66 | { 67 | protected: 68 | template 69 | requires std::is_base_of_v 70 | T& Register(Args&&... aArgs) 71 | { 72 | assert(m_parent); 73 | return m_parent->template Register(std::forward(aArgs)...); 74 | } 75 | 76 | template 77 | requires std::is_base_of_v 78 | T& Register(Core::SharedPtr aFeature) 79 | { 80 | assert(m_parent); 81 | return m_parent->Register(std::forward(aFeature)); 82 | } 83 | 84 | private: 85 | friend class Registry; 86 | 87 | void SetParent(Registry& aParent) 88 | { 89 | m_parent = &aParent; 90 | } 91 | 92 | Registry* m_parent{ nullptr }; 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /lib/Core/Facades/Container.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Container/Container.hpp" 4 | 5 | namespace Core 6 | { 7 | template 8 | inline Core::SharedPtr Resolve() 9 | { 10 | return Container::Get(); 11 | } 12 | 13 | template 14 | inline bool Resolvable() 15 | { 16 | return Container::Get(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/Core/Facades/Log.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Logging/LoggingDriver.hpp" 4 | 5 | namespace Core::Log 6 | { 7 | inline void Info(const std::string_view& aMessage) 8 | { 9 | LoggingDriver::GetDefault().LogInfo(aMessage); 10 | } 11 | 12 | inline void Warning(const std::string_view& aMessage) 13 | { 14 | LoggingDriver::GetDefault().LogWarning(aMessage); 15 | } 16 | 17 | inline void Error(const std::string_view& aMessage) 18 | { 19 | LoggingDriver::GetDefault().LogError(aMessage); 20 | } 21 | 22 | inline void Debug(const std::string_view& aMessage) 23 | { 24 | LoggingDriver::GetDefault().LogDebug(aMessage); 25 | } 26 | 27 | template 28 | constexpr void Info(std::format_string aFormat, Args&&... aArgs) 29 | { 30 | LoggingDriver::GetDefault().LogInfo(std::format(aFormat, std::forward(aArgs)...)); 31 | } 32 | 33 | template 34 | constexpr void Warning(std::format_string aFormat, Args&&... aArgs) 35 | { 36 | LoggingDriver::GetDefault().LogWarning(std::format(aFormat, std::forward(aArgs)...)); 37 | } 38 | 39 | template 40 | constexpr void Error(std::format_string aFormat, Args&&... aArgs) 41 | { 42 | LoggingDriver::GetDefault().LogError(std::format(aFormat, std::forward(aArgs)...)); 43 | } 44 | 45 | template 46 | constexpr void Debug(std::format_string aFormat, Args&&... aArgs) 47 | { 48 | LoggingDriver::GetDefault().LogDebug(std::format(aFormat, std::forward(aArgs)...)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/Core/Facades/Runtime.cpp: -------------------------------------------------------------------------------- 1 | #include "Runtime.hpp" 2 | #include "Core/Stl.hpp" 3 | 4 | namespace 5 | { 6 | Core::UniquePtr s_host; 7 | Core::UniquePtr s_module; 8 | } 9 | 10 | void Core::Runtime::Initialize(const Core::HostImage& aHost, const Core::ModuleImage& aModule) 11 | { 12 | s_host = Core::MakeUnique(aHost); 13 | s_module = Core::MakeUnique(aModule); 14 | } 15 | 16 | Core::HostImage* Core::Runtime::GetHost() 17 | { 18 | return s_host.get(); 19 | } 20 | 21 | Core::ModuleImage* Core::Runtime::GetModule() 22 | { 23 | return s_module.get(); 24 | } 25 | 26 | uintptr_t Core::Runtime::GetImageBase() 27 | { 28 | assert(s_host); 29 | return s_host->GetBase(); 30 | } 31 | 32 | std::filesystem::path Core::Runtime::GetImagePath() 33 | { 34 | assert(s_host); 35 | return s_host->GetPath(); 36 | } 37 | 38 | std::filesystem::path Core::Runtime::GetRootDir() 39 | { 40 | assert(s_host); 41 | return s_host->GetRootDir(); 42 | } 43 | 44 | std::filesystem::path Core::Runtime::GetModulePath() 45 | { 46 | assert(s_module); 47 | return s_module->GetPath(); 48 | } 49 | 50 | std::filesystem::path Core::Runtime::GetModuleDir() 51 | { 52 | assert(s_module); 53 | return s_module->GetDir(); 54 | } 55 | 56 | std::string Core::Runtime::GetModuleName() 57 | { 58 | assert(s_module); 59 | return s_module->GetName(); 60 | } 61 | 62 | bool Core::Runtime::IsASI() 63 | { 64 | assert(s_module); 65 | return s_module->IsASI(); 66 | } 67 | 68 | bool Core::Runtime::IsASI(HMODULE aHandle) 69 | { 70 | return Core::ModuleImage(aHandle).IsASI(); 71 | } 72 | 73 | bool Core::Runtime::IsEXE(std::wstring_view aName) 74 | { 75 | if (s_host) 76 | return s_host->GetPath().filename() == aName; 77 | 78 | return Core::HostImage().GetPath().filename() == aName; 79 | } 80 | -------------------------------------------------------------------------------- /lib/Core/Facades/Runtime.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Runtime/HostImage.hpp" 4 | #include "Core/Runtime/ModuleImage.hpp" 5 | 6 | namespace Core::Runtime 7 | { 8 | void Initialize(const Core::HostImage& aHost, const Core::ModuleImage& aModule); 9 | 10 | HostImage* GetHost(); 11 | ModuleImage* GetModule(); 12 | 13 | [[nodiscard]] uintptr_t GetImageBase(); 14 | [[nodiscard]] std::filesystem::path GetImagePath(); 15 | [[nodiscard]] std::filesystem::path GetRootDir(); 16 | [[nodiscard]] std::filesystem::path GetModulePath(); 17 | [[nodiscard]] std::filesystem::path GetModuleDir(); 18 | [[nodiscard]] std::string GetModuleName(); 19 | [[nodiscard]] bool IsASI(); 20 | [[nodiscard]] bool IsASI(HMODULE aHandle); 21 | [[nodiscard]] bool IsEXE(std::wstring_view aName); 22 | } 23 | -------------------------------------------------------------------------------- /lib/Core/Foundation/Application.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.hpp" 2 | 3 | void Core::Application::Bootstrap() 4 | { 5 | if (m_booted) 6 | return; 7 | 8 | if (!s_discoveryCallbacks.empty()) 9 | { 10 | for (const auto& callback : s_discoveryCallbacks) 11 | { 12 | callback(*this); 13 | } 14 | s_discoveryCallbacks.clear(); 15 | } 16 | 17 | m_booted = true; 18 | 19 | OnStarting(); 20 | 21 | for (const auto& feature : GetRegistered()) 22 | { 23 | feature->OnBootstrap(); 24 | } 25 | 26 | OnStarted(); 27 | } 28 | 29 | void Core::Application::Shutdown() 30 | { 31 | if (!m_booted) 32 | return; 33 | 34 | OnStopping(); 35 | 36 | for (const auto& feature : GetRegistered()) 37 | { 38 | feature->OnShutdown(); 39 | } 40 | 41 | OnStopped(); 42 | 43 | m_booted = false; 44 | } 45 | 46 | void Core::Application::OnRegistered(const SharedPtr& aFeature) 47 | { 48 | aFeature->OnRegister(); 49 | 50 | if (m_booted) 51 | { 52 | aFeature->OnBootstrap(); 53 | } 54 | } 55 | 56 | bool Core::Application::Discover(AutoDiscoveryCallback aCallback) 57 | { 58 | s_discoveryCallbacks.push_back(aCallback); 59 | return true; 60 | } 61 | -------------------------------------------------------------------------------- /lib/Core/Foundation/Application.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Container/Registry.hpp" 4 | #include "Core/Foundation/Feature.hpp" 5 | #include "Core/Stl.hpp" 6 | 7 | namespace Core 8 | { 9 | using AutoDiscoveryCallback = void(*)(Application&); 10 | 11 | class Application : public Registry 12 | { 13 | public: 14 | template 15 | Feature::Defer Register(Args&&... aArgs) 16 | { 17 | return Registry::Register(std::forward(aArgs)...); 18 | } 19 | 20 | void Bootstrap(); 21 | void Shutdown(); 22 | 23 | static bool Discover(AutoDiscoveryCallback aCallback); 24 | 25 | protected: 26 | void OnRegistered(const SharedPtr& aFeature) override; 27 | 28 | virtual void OnStarting() {}; 29 | virtual void OnStarted() {}; 30 | virtual void OnStopping() {}; 31 | virtual void OnStopped() {}; 32 | 33 | private: 34 | bool m_booted = false; 35 | 36 | inline static Vector s_discoveryCallbacks; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /lib/Core/Foundation/Feature.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Container/Registry.hpp" 4 | 5 | namespace Core 6 | { 7 | class Application; 8 | 9 | class Feature : public RegistryProxy 10 | { 11 | public: 12 | Feature() = default; 13 | virtual ~Feature() = default; 14 | 15 | Feature(Feature&& aOther) = delete; 16 | Feature(const Feature& aOther) = delete; 17 | 18 | protected: 19 | friend class Application; 20 | friend class Registry; 21 | 22 | virtual void OnRegister() {}; 23 | virtual void OnInitialize() {}; 24 | virtual void OnBootstrap() {}; 25 | virtual void OnShutdown() {}; 26 | 27 | template 28 | requires std::is_base_of_v 29 | class Defer 30 | { 31 | public: 32 | inline Defer(T& aTarget) 33 | : m_instance(aTarget) 34 | { 35 | ++m_instance.m_deferChain; 36 | } 37 | 38 | inline Defer(T* aTarget) 39 | : m_instance(*aTarget) 40 | { 41 | ++m_instance.m_deferChain; 42 | } 43 | 44 | inline ~Defer() 45 | { 46 | if (--m_instance.m_deferChain == 0) 47 | { 48 | static_cast(m_instance).OnInitialize(); 49 | } 50 | } 51 | 52 | [[nodiscard]] inline T* operator->() const 53 | { 54 | return &m_instance; 55 | } 56 | 57 | private: 58 | T& m_instance; 59 | }; 60 | 61 | private: 62 | uint8_t m_deferChain = 0; 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /lib/Core/Foundation/LocaleProvider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | 5 | namespace Core 6 | { 7 | class LocaleProvider : public Feature 8 | { 9 | public: 10 | LocaleProvider(const char* aLocale = "en_US.UTF-8") 11 | { 12 | std::setlocale(LC_ALL, aLocale); 13 | } 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /lib/Core/Foundation/RuntimeProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "RuntimeProvider.hpp" 2 | #include "Core/Facades/Runtime.hpp" 3 | 4 | Core::RuntimeProvider::RuntimeProvider(HMODULE aHandle) noexcept 5 | : m_handle(aHandle) 6 | , m_basePathDepth(0) 7 | { 8 | } 9 | 10 | void Core::RuntimeProvider::OnInitialize() 11 | { 12 | Runtime::Initialize(HostImage(m_basePathDepth), ModuleImage(m_handle)); 13 | } 14 | -------------------------------------------------------------------------------- /lib/Core/Foundation/RuntimeProvider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Win.hpp" 5 | 6 | namespace Core 7 | { 8 | class RuntimeProvider : public Feature 9 | { 10 | public: 11 | explicit RuntimeProvider(HMODULE aHandle) noexcept; 12 | 13 | auto SetBaseImagePathDepth(int aDepth) noexcept 14 | { 15 | m_basePathDepth = aDepth; 16 | return Defer(this); 17 | } 18 | 19 | protected: 20 | void OnInitialize() override; 21 | 22 | HMODULE m_handle; 23 | int m_basePathDepth; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /lib/Core/Hooking/HookingAgent.cpp: -------------------------------------------------------------------------------- 1 | #include "HookingAgent.hpp" 2 | 3 | #include 4 | 5 | namespace 6 | { 7 | Core::HookingDriver* s_driver; 8 | } 9 | 10 | void Core::HookingAgent::SetHookingDriver(Core::HookingDriver& aDriver) 11 | { 12 | s_driver = &aDriver; 13 | } 14 | 15 | Core::HookingDriver& Core::HookingAgent::GetHookingDriver() 16 | { 17 | assert(s_driver); 18 | return *s_driver; 19 | } 20 | -------------------------------------------------------------------------------- /lib/Core/Hooking/HookingDriver.cpp: -------------------------------------------------------------------------------- 1 | #include "HookingDriver.hpp" 2 | #include "HookingAgent.hpp" 3 | 4 | #include 5 | 6 | namespace 7 | { 8 | Core::HookingDriver* s_default; 9 | } 10 | 11 | void Core::HookingDriver::SetDefault(Core::HookingDriver& aDriver) 12 | { 13 | s_default = &aDriver; 14 | 15 | HookingAgent::SetHookingDriver(aDriver); 16 | } 17 | 18 | Core::HookingDriver& Core::HookingDriver::GetDefault() 19 | { 20 | assert(s_default); 21 | return *s_default; 22 | } 23 | -------------------------------------------------------------------------------- /lib/Core/Hooking/HookingDriver.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Core 4 | { 5 | class HookingDriver 6 | { 7 | public: 8 | virtual bool HookAttach(uintptr_t aAddress, void* aCallback) = 0; 9 | virtual bool HookAttach(uintptr_t aAddress, void* aCallback, void** aOriginal) = 0; 10 | virtual bool HookDetach(uintptr_t aAddress) = 0; 11 | 12 | static void SetDefault(HookingDriver& aDriver); 13 | static HookingDriver& GetDefault(); 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /lib/Core/Logging/LoggingAgent.cpp: -------------------------------------------------------------------------------- 1 | #include "LoggingAgent.hpp" 2 | 3 | #include 4 | 5 | namespace 6 | { 7 | Core::LoggingDriver* s_driver; 8 | } 9 | 10 | void Core::LoggingAgent::SetDriver(Core::LoggingDriver& aDriver) 11 | { 12 | s_driver = &aDriver; 13 | } 14 | 15 | Core::LoggingDriver& Core::LoggingAgent::GetLoggingDriver() 16 | { 17 | assert(s_driver); 18 | return *s_driver; 19 | } 20 | 21 | void Core::LoggingAgent::LogFlush() 22 | { 23 | s_driver->LogFlush(); 24 | } 25 | -------------------------------------------------------------------------------- /lib/Core/Logging/LoggingAgent.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "LoggingDriver.hpp" 4 | 5 | namespace Core 6 | { 7 | class LoggingAgent 8 | { 9 | protected: 10 | inline static void LogInfo(const char* aMessage) 11 | { 12 | GetLoggingDriver().LogInfo(aMessage); 13 | } 14 | 15 | inline static void LogWarning(const char* aMessage) 16 | { 17 | GetLoggingDriver().LogWarning(aMessage); 18 | } 19 | 20 | inline static void LogError(const char* aMessage) 21 | { 22 | GetLoggingDriver().LogError(aMessage); 23 | } 24 | 25 | inline static void LogDebug(const char* aMessage) 26 | { 27 | GetLoggingDriver().LogDebug(aMessage); 28 | } 29 | 30 | template 31 | inline static constexpr void LogInfo(std::format_string aFormat, Args&&... aArgs) 32 | { 33 | GetLoggingDriver().LogInfo(aFormat, std::forward(aArgs)...); 34 | } 35 | 36 | template 37 | inline static constexpr void LogWarning(std::format_string aFormat, Args&&... aArgs) 38 | { 39 | GetLoggingDriver().LogWarning(std::format(aFormat, std::forward(aArgs)...)); 40 | } 41 | 42 | template 43 | inline static constexpr void LogError(std::format_string aFormat, Args&&... aArgs) 44 | { 45 | GetLoggingDriver().LogError(std::format(aFormat, std::forward(aArgs)...)); 46 | } 47 | 48 | template 49 | inline static constexpr void LogDebug(std::format_string aFormat, Args&&... aArgs) 50 | { 51 | GetLoggingDriver().LogDebug(std::format(aFormat, std::forward(aArgs)...)); 52 | } 53 | 54 | static void LogFlush(); 55 | 56 | static LoggingDriver& GetLoggingDriver(); 57 | 58 | private: 59 | friend LoggingDriver; 60 | 61 | static void SetDriver(LoggingDriver& aDriver); 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /lib/Core/Logging/LoggingDriver.cpp: -------------------------------------------------------------------------------- 1 | #include "LoggingDriver.hpp" 2 | #include "LoggingAgent.hpp" 3 | 4 | #include 5 | 6 | namespace 7 | { 8 | Core::LoggingDriver* s_default; 9 | } 10 | 11 | void Core::LoggingDriver::SetDefault(LoggingDriver& aDriver) 12 | { 13 | s_default = &aDriver; 14 | 15 | LoggingAgent::SetDriver(aDriver); 16 | } 17 | 18 | Core::LoggingDriver& Core::LoggingDriver::GetDefault() 19 | { 20 | assert(s_default); 21 | return *s_default; 22 | } 23 | -------------------------------------------------------------------------------- /lib/Core/Logging/LoggingDriver.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Core 4 | { 5 | class LoggingDriver 6 | { 7 | public: 8 | virtual void LogInfo(const std::string_view& aMessage) = 0; 9 | virtual void LogWarning(const std::string_view& aMessage) = 0; 10 | virtual void LogError(const std::string_view& aMessage) = 0; 11 | virtual void LogDebug(const std::string_view& aMessage) = 0; 12 | virtual void LogFlush() = 0; 13 | 14 | template 15 | constexpr void LogInfo(std::format_string aFormat, Args&&... aArgs) 16 | { 17 | LogInfo(std::format(aFormat, std::forward(aArgs)...)); 18 | } 19 | 20 | template 21 | constexpr void LogWarning(std::format_string aFormat, Args&&... aArgs) 22 | { 23 | LogWarning(std::format(aFormat, std::forward(aArgs)...)); 24 | } 25 | 26 | template 27 | constexpr void LogError(std::format_string aFormat, Args&&... aArgs) 28 | { 29 | LogError(std::format(aFormat, std::forward(aArgs)...)); 30 | } 31 | 32 | template 33 | constexpr void LogDebug(std::format_string aFormat, Args&&... aArgs) 34 | { 35 | LogDebug(std::format(aFormat, std::forward(aArgs)...)); 36 | } 37 | 38 | static void SetDefault(LoggingDriver& aDriver); 39 | static LoggingDriver& GetDefault(); 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /lib/Core/Memory/AddressResolver.cpp: -------------------------------------------------------------------------------- 1 | #include "AddressResolver.hpp" 2 | 3 | #include 4 | 5 | namespace 6 | { 7 | Core::AddressResolver* s_default; 8 | } 9 | 10 | void Core::AddressResolver::SetDefault(Core::AddressResolver& aResolver) 11 | { 12 | s_default = &aResolver; 13 | } 14 | 15 | Core::AddressResolver& Core::AddressResolver::GetDefault() 16 | { 17 | assert(s_default); 18 | return *s_default; 19 | } 20 | -------------------------------------------------------------------------------- /lib/Core/Memory/AddressResolver.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Core 4 | { 5 | class AddressResolver 6 | { 7 | public: 8 | virtual uintptr_t ResolveAddress(uint32_t aAddressID) = 0; 9 | 10 | static void SetDefault(AddressResolver& aResolver); 11 | static AddressResolver& GetDefault(); 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /lib/Core/Runtime/HostImage.cpp: -------------------------------------------------------------------------------- 1 | #include "HostImage.hpp" 2 | #include "Core/Win.hpp" 3 | 4 | Core::HostImage::HostImage(int aExePathDepth) 5 | { 6 | const auto handle = GetModuleHandleW(nullptr); 7 | 8 | m_base = reinterpret_cast(handle); 9 | 10 | std::wstring filePath; 11 | wil::GetModuleFileNameW(handle, filePath); 12 | 13 | m_exe = filePath; 14 | m_root = m_exe.parent_path(); 15 | 16 | while (--aExePathDepth >= 0) 17 | m_root = m_root.parent_path(); 18 | 19 | TryResolveVersion(filePath); 20 | } 21 | 22 | uintptr_t Core::HostImage::GetBase() const 23 | { 24 | return m_base; 25 | } 26 | 27 | std::filesystem::path Core::HostImage::GetPath() const 28 | { 29 | return m_exe; 30 | } 31 | 32 | std::string Core::HostImage::GetName() const 33 | { 34 | return m_exe.stem().string(); 35 | } 36 | 37 | std::filesystem::path Core::HostImage::GetRootDir() const 38 | { 39 | return m_root; 40 | } 41 | 42 | const Core::FileVer& Core::HostImage::GetFileVer() const 43 | { 44 | return m_fileVer; 45 | } 46 | 47 | const Core::SemvVer& Core::HostImage::GetProductVer() const 48 | { 49 | return m_productVer; 50 | } 51 | 52 | bool Core::HostImage::TryResolveVersion(const std::wstring& filePath) 53 | { 54 | auto size = GetFileVersionInfoSizeW(filePath.c_str(), nullptr); 55 | if (!size) 56 | return false; 57 | 58 | std::unique_ptr data(new (std::nothrow) uint8_t[size]()); 59 | if (!data) 60 | return false; 61 | 62 | if (!GetFileVersionInfoW(filePath.c_str(), 0, size, data.get())) 63 | return false; 64 | 65 | VS_FIXEDFILEINFO* fileInfo = nullptr; 66 | UINT fileInfoBytes; 67 | 68 | if (!VerQueryValueW(data.get(), L"\\", reinterpret_cast(&fileInfo), &fileInfoBytes)) 69 | return false; 70 | 71 | constexpr auto signature = 0xFEEF04BD; 72 | if (fileInfo->dwSignature != signature) 73 | return false; 74 | 75 | m_fileVer.major = (fileInfo->dwFileVersionMS >> 16) & 0xFF; 76 | m_fileVer.minor = fileInfo->dwFileVersionMS & 0xFFFF; 77 | m_fileVer.build = (fileInfo->dwFileVersionLS >> 16) & 0xFFFF; 78 | m_fileVer.revision = fileInfo->dwFileVersionLS & 0xFFFF; 79 | 80 | m_productVer.major = (fileInfo->dwProductVersionMS >> 16) & 0xFF; 81 | m_productVer.minor = fileInfo->dwProductVersionMS & 0xFFFF; 82 | m_productVer.patch = (fileInfo->dwProductVersionLS >> 16) & 0xFFFF; 83 | 84 | return true; 85 | } 86 | -------------------------------------------------------------------------------- /lib/Core/Runtime/HostImage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Core 4 | { 5 | struct FileVer 6 | { 7 | uint16_t major; 8 | uint16_t minor; 9 | uint16_t build; 10 | uint16_t revision; 11 | }; 12 | 13 | struct SemvVer 14 | { 15 | uint16_t major; 16 | uint16_t minor; 17 | uint32_t patch; 18 | }; 19 | 20 | class HostImage 21 | { 22 | public: 23 | explicit HostImage(int32_t aExePathDepth = 0); 24 | ~HostImage() = default; 25 | 26 | [[nodiscard]] uintptr_t GetBase() const; 27 | [[nodiscard]] std::filesystem::path GetPath() const; 28 | [[nodiscard]] std::string GetName() const; 29 | [[nodiscard]] std::filesystem::path GetRootDir() const; 30 | 31 | [[nodiscard]] const FileVer& GetFileVer() const; 32 | [[nodiscard]] const SemvVer& GetProductVer() const; 33 | 34 | private: 35 | bool TryResolveVersion(const std::wstring& aFilePath); 36 | 37 | uintptr_t m_base; 38 | std::filesystem::path m_exe; 39 | std::filesystem::path m_root; 40 | FileVer m_fileVer{}; 41 | SemvVer m_productVer{}; 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /lib/Core/Runtime/ModuleImage.cpp: -------------------------------------------------------------------------------- 1 | #include "ModuleImage.hpp" 2 | 3 | Core::ModuleImage::ModuleImage(HMODULE aHandle) 4 | { 5 | std::wstring filePath; 6 | wil::GetModuleFileNameW(aHandle, filePath); 7 | 8 | m_path = filePath; 9 | } 10 | 11 | std::filesystem::path Core::ModuleImage::GetPath() const 12 | { 13 | return m_path; 14 | } 15 | 16 | std::filesystem::path Core::ModuleImage::GetDir() const 17 | { 18 | return m_path.parent_path(); 19 | } 20 | 21 | std::string Core::ModuleImage::GetName() const 22 | { 23 | return m_path.stem().string(); 24 | } 25 | 26 | bool Core::ModuleImage::IsASI() const 27 | { 28 | return m_path.extension() == L".asi"; 29 | } 30 | -------------------------------------------------------------------------------- /lib/Core/Runtime/ModuleImage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Win.hpp" 4 | 5 | namespace Core 6 | { 7 | class ModuleImage 8 | { 9 | public: 10 | explicit ModuleImage(HMODULE aHandle); 11 | ~ModuleImage() = default; 12 | 13 | [[nodiscard]] std::filesystem::path GetPath() const; 14 | [[nodiscard]] std::filesystem::path GetDir() const; 15 | [[nodiscard]] std::string GetName() const; 16 | [[nodiscard]] bool IsASI() const; 17 | 18 | private: 19 | std::filesystem::path m_path; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /lib/Core/Runtime/OwnerMutex.cpp: -------------------------------------------------------------------------------- 1 | #include "OwnerMutex.hpp" 2 | 3 | Core::OwnerMutex::OwnerMutex(std::string_view aName) 4 | : m_aname(aName) 5 | , m_mutex(nullptr) 6 | { 7 | } 8 | 9 | Core::OwnerMutex::OwnerMutex(std::wstring_view aName) 10 | : m_wname(aName) 11 | , m_mutex(nullptr) 12 | { 13 | } 14 | 15 | Core::OwnerMutex::~OwnerMutex() 16 | { 17 | Release(); 18 | } 19 | 20 | bool Core::OwnerMutex::Obtain() 21 | { 22 | const auto mutex = !m_wname.empty() 23 | ? CreateMutexW(NULL, TRUE, m_wname.data()) 24 | : CreateMutexA(NULL, TRUE, m_aname.data()); 25 | 26 | if (!mutex) 27 | return false; 28 | 29 | if (GetLastError() == ERROR_ALREADY_EXISTS) 30 | { 31 | ReleaseMutex(mutex); 32 | return false; 33 | } 34 | 35 | m_mutex = mutex; 36 | 37 | return true; 38 | } 39 | 40 | bool Core::OwnerMutex::Release() 41 | { 42 | if (!m_mutex) 43 | return false; 44 | 45 | ReleaseMutex(m_mutex); 46 | m_mutex = nullptr; 47 | 48 | return true; 49 | } 50 | 51 | bool Core::OwnerMutex::IsOwner() 52 | { 53 | return m_mutex; 54 | } 55 | -------------------------------------------------------------------------------- /lib/Core/Runtime/OwnerMutex.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Win.hpp" 4 | 5 | namespace Core 6 | { 7 | class OwnerMutex 8 | { 9 | public: 10 | explicit OwnerMutex(std::string_view aName); 11 | explicit OwnerMutex(std::wstring_view aName); 12 | ~OwnerMutex(); 13 | 14 | bool Obtain(); 15 | bool Release(); 16 | bool IsOwner(); 17 | 18 | private: 19 | std::string_view m_aname; 20 | std::wstring_view m_wname; 21 | HANDLE m_mutex; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /lib/Core/Stl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace Core 8 | { 9 | // Pretty much the same as TiltedCore with StlAllocator, 10 | // but unique ptr allows implicit casting to the base. 11 | 12 | namespace Detail 13 | { 14 | template 15 | struct UniqueDeleter 16 | { 17 | constexpr UniqueDeleter() noexcept = default; 18 | 19 | template 20 | requires std::is_base_of_v 21 | UniqueDeleter(const UniqueDeleter& d) noexcept {} 22 | 23 | void operator()(std::conditional_t, T, T*> aData) 24 | { 25 | TiltedPhoques::Delete(aData); 26 | } 27 | }; 28 | } 29 | 30 | template 31 | using Vector = std::vector>; 32 | 33 | template 34 | using Set = tsl::hopscotch_set, std::equal_to, TiltedPhoques::StlAllocator>; 35 | 36 | template 37 | using Map = tsl::hopscotch_map, std::equal_to, TiltedPhoques::StlAllocator>>; 38 | 39 | template 40 | using SortedMap = std::map, TiltedPhoques::StlAllocator>>; 41 | 42 | // TODO: OrderedMap 43 | 44 | template 45 | using SharedPtr = std::shared_ptr; 46 | 47 | template 48 | using WeakPtr = std::weak_ptr; 49 | 50 | template 51 | using UniquePtr = std::unique_ptr>; 52 | 53 | template 54 | struct ShareFromThis : public std::enable_shared_from_this 55 | { 56 | SharedPtr ToShared() 57 | { 58 | return std::enable_shared_from_this::shared_from_this(); 59 | } 60 | 61 | WeakPtr ToWeak() 62 | { 63 | return std::enable_shared_from_this::weak_from_this(); 64 | } 65 | }; 66 | 67 | template 68 | auto MakeShared(Args&&... aArgs) 69 | { 70 | return std::allocate_shared(TiltedPhoques::StlAllocator(), std::forward(aArgs)...); 71 | } 72 | 73 | template 74 | auto MakeUnique(Args&&... aArgs) 75 | { 76 | return UniquePtr(TiltedPhoques::New(std::forward(aArgs)...)); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/Core/Win.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef WIN32_LEAN_AND_MEAN 4 | #define WIN32_LEAN_AND_MEAN 5 | #endif 6 | #ifndef NOMINMAX 7 | #define NOMINMAX 8 | #endif 9 | 10 | #include 11 | #include 12 | -------------------------------------------------------------------------------- /lib/Red/Alias.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | using namespace RED4ext; 6 | } 7 | 8 | namespace Red::Detail 9 | { 10 | using namespace RED4ext::Detail; 11 | } 12 | -------------------------------------------------------------------------------- /lib/Red/Engine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TypeInfo/Resolving.hpp" 4 | #include "TypeInfo/Parameters.hpp" 5 | 6 | #include "Engine/Framework.hpp" 7 | #include "Engine/LogChannel.hpp" 8 | 9 | #include "Engine/Mappings.hpp" 10 | -------------------------------------------------------------------------------- /lib/Red/Engine/Framework.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Macros/Framework.hpp" 4 | 5 | namespace Red 6 | { 7 | template 8 | struct RuntimeSystemMapping : public std::false_type {}; 9 | 10 | namespace Detail 11 | { 12 | template 13 | concept HasRuntimeSystemMapping = RuntimeSystemMapping::value 14 | && RuntimeSystemMapping::offset >= 0 15 | && RuntimeSystemMapping::offset <= 64; 16 | } 17 | 18 | template 19 | requires std::is_base_of_v && std::is_base_of_v 20 | U* GetGameSystem() 21 | { 22 | static const auto s_type = GetType(); 23 | auto& gameInstance = CGameEngine::Get()->framework->gameInstance; 24 | return reinterpret_cast(gameInstance->GetSystem(s_type)); 25 | } 26 | 27 | template 28 | requires std::is_base_of_v && std::is_base_of_v && Detail::HasRuntimeSystemMapping 29 | U* GetRuntimeSystem() 30 | { 31 | constexpr auto systemOffset = RuntimeSystemMapping::offset * sizeof(Handle); 32 | const auto& runtimeSceneAddr = CGameEngine::Get()->framework->unk18; 33 | return reinterpret_cast*>(runtimeSceneAddr + systemOffset)->instance; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/Red/Engine/LogChannel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red::Log 4 | { 5 | inline void Channel(CName aChannel, const CString& aMessage) 6 | { 7 | static auto* s_rtti = CRTTISystem::Get(); 8 | static auto* s_logFunc = s_rtti->GetFunction("LogChannel"); 9 | static auto* s_stringType = s_rtti->GetType("String"); 10 | static auto* s_stringRefType = s_rtti->GetType("script_ref:String"); 11 | static auto* s_nameType = s_rtti->GetType("CName"); 12 | 13 | ScriptRef messageRef; 14 | messageRef.type = s_stringType; 15 | messageRef.name = s_stringType->GetName(); 16 | messageRef.ref = const_cast(&aMessage); 17 | 18 | StackArgs_t args; 19 | args.emplace_back(s_nameType, &aChannel); 20 | args.emplace_back(s_stringRefType, &messageRef); 21 | 22 | CStack stack(nullptr, args.data(), static_cast(args.size()), nullptr); 23 | 24 | s_logFunc->Execute(&stack); 25 | } 26 | 27 | inline void Channel(Red::CName aChannel, const std::string& aMessage) 28 | { 29 | Channel(aChannel, Red::CString(aMessage.c_str())); 30 | } 31 | 32 | template 33 | constexpr void Channel(CName aChannel, std::format_string aFormat, Args&&... aArgs) 34 | { 35 | Channel(aChannel, std::format(aFormat, std::forward(aArgs)...)); 36 | } 37 | 38 | template 39 | constexpr void Debug(std::format_string aFormat, Args&&... aArgs) 40 | { 41 | Channel("DEBUG", std::format(aFormat, std::forward(aArgs)...)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Red/Engine/Macros/Framework.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define RTTI_MAP_RUNTIME_SYSTEM(_type, _offset) \ 4 | template<> \ 5 | struct Red::RuntimeSystemMapping<_type> : public std::true_type \ 6 | { \ 7 | static constexpr auto offset = _offset; \ 8 | }; 9 | -------------------------------------------------------------------------------- /lib/Red/Specializations.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TypeInfo/Resolving.hpp" 4 | 5 | template<> 6 | struct std::hash 7 | { 8 | std::size_t operator()(RED4ext::CName aKey) const 9 | { 10 | return aKey.hash; 11 | } 12 | }; 13 | 14 | template<> 15 | struct std::hash 16 | { 17 | std::size_t operator()(RED4ext::TweakDBID aKey) const 18 | { 19 | return aKey.value; 20 | } 21 | }; 22 | 23 | template<> 24 | struct std::hash 25 | { 26 | std::size_t operator()(RED4ext::ResourcePath aKey) const 27 | { 28 | return aKey.hash; 29 | } 30 | }; 31 | 32 | template<> 33 | struct std::hash 34 | { 35 | std::size_t operator()(RED4ext::NodeRef aKey) const 36 | { 37 | return aKey.hash; 38 | } 39 | }; 40 | 41 | template 42 | requires std::is_class_v && std::is_convertible_v && Red::Detail::HasGeneratedTypeName 43 | struct std::hash 44 | { 45 | std::size_t operator()(T aKey) const 46 | { 47 | return static_cast(aKey); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /lib/Red/TypeInfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TypeInfo/Construction.hpp" 4 | #include "TypeInfo/Definition.hpp" 5 | #include "TypeInfo/Properties.hpp" 6 | #include "TypeInfo/Parameters.hpp" 7 | #include "TypeInfo/Invocation.hpp" 8 | #include "TypeInfo/Registrar.hpp" 9 | #include "TypeInfo/Resolving.hpp" 10 | 11 | #include "TypeInfo/Mappings.hpp" 12 | -------------------------------------------------------------------------------- /lib/Red/TypeInfo/Construction.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Resolving.hpp" 4 | 5 | namespace Red 6 | { 7 | template 8 | inline T* Construct() 9 | { 10 | auto type = GetType(); 11 | auto instance = reinterpret_cast(type->GetAllocator()->AllocAligned(type->GetSize(), 12 | type->GetAlignment()).memory); 13 | type->Construct(instance); 14 | return instance; 15 | } 16 | 17 | template 18 | inline void Destruct(T* aInstance) 19 | { 20 | auto type = GetType(); 21 | type->Destruct(aInstance); 22 | type->GetAllocator()->Free(aInstance); 23 | } 24 | } 25 | 26 | template 27 | requires std::is_abstract_v && Red::Detail::HasGeneratedTypeName 28 | struct RED4ext::Detail::AllocatorHook : std::true_type 29 | { 30 | inline static Memory::IAllocator* Get() 31 | { 32 | return Red::GetClass()->GetAllocator(); 33 | } 34 | }; 35 | 36 | template 37 | requires std::is_abstract_v && Red::Detail::HasGeneratedTypeName 38 | struct RED4ext::Detail::ConstructorHook : std::true_type 39 | { 40 | inline static void Apply(T* aInstance) 41 | { 42 | Red::GetClass()->ConstructCls(aInstance); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /lib/Red/TypeInfo/Macros/Resolving.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define RTTI_MAP_TYPE_NAME(_type, _name) \ 4 | template<> \ 5 | struct Red::TypeNameMapping<_type> : public std::true_type \ 6 | { \ 7 | static constexpr auto name = _name; \ 8 | }; 9 | 10 | #define RTTI_MAP_TYPE_PREFIX(_type, _prefix) \ 11 | template \ 12 | struct Red::TypePrefixMapping<_type> : public std::true_type \ 13 | { \ 14 | static constexpr auto prefix = _prefix; \ 15 | }; 16 | 17 | #define RTTI_MAP_TYPE_PROXY(_type) \ 18 | template \ 19 | struct Red::TypeProxyMapping<_type> : public std::true_type \ 20 | { \ 21 | using type = A; \ 22 | }; 23 | 24 | #define RTTI_TYPE_NAME_STR(_type) Red::GetTypeNameStr<_type>() 25 | 26 | #define RTTI_FUNC_NAME_STR(...) []() constexpr noexcept { \ 27 | constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \ 28 | return ::Red::Detail::MakeConstStr<_name.size()>(_name.data()); }() 29 | 30 | #define RTTI_PROP_NAME_STR(...) []() constexpr noexcept { \ 31 | constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \ 32 | constexpr auto _clean = ::Red::Detail::RemoveMemberPrefix(_name); \ 33 | return ::Red::Detail::MakeConstStr<_clean.size()>(_clean.data()); }() 34 | 35 | #define RTTI_ENUM_NAME_STR(...) ::nameof::nameof_enum<__VA_ARGS__>() 36 | 37 | #define RTTI_TYPE_NAME(_type) ::Red::GetTypeName<_type>() 38 | #define RTTI_FUNC_NAME(_func) ::Red::CName(RTTI_FUNC_NAME_STR(&_func).data()) 39 | #define RTTI_PROP_NAME(_prop) ::Red::CName(RTTI_PROP_NAME_STR(&_prop).data()) 40 | #define RTTI_ENUM_NAME(_enum) ::Red::CName(RTTI_ENUM_NAME_STR(_enum).data()) 41 | -------------------------------------------------------------------------------- /lib/Red/TypeInfo/Mappings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Resolving.hpp" 4 | 5 | namespace Red 6 | { 7 | RTTI_MAP_TYPE_NAME(int8_t, "Int8"); 8 | RTTI_MAP_TYPE_NAME(uint8_t, "Uint8"); 9 | RTTI_MAP_TYPE_NAME(int16_t, "Int16"); 10 | RTTI_MAP_TYPE_NAME(uint16_t, "Uint16"); 11 | RTTI_MAP_TYPE_NAME(int32_t, "Int32"); 12 | RTTI_MAP_TYPE_NAME(uint32_t, "Uint32"); 13 | RTTI_MAP_TYPE_NAME(int64_t, "Int64"); 14 | RTTI_MAP_TYPE_NAME(uint64_t, "Uint64"); 15 | RTTI_MAP_TYPE_NAME(float, "Float"); 16 | RTTI_MAP_TYPE_NAME(double, "Double"); 17 | RTTI_MAP_TYPE_NAME(bool, "Bool"); 18 | RTTI_MAP_TYPE_NAME(CString, "String"); 19 | RTTI_MAP_TYPE_NAME(CName, "CName"); 20 | RTTI_MAP_TYPE_NAME(TweakDBID, "TweakDBID"); 21 | RTTI_MAP_TYPE_NAME(ItemID, "gameItemID"); 22 | RTTI_MAP_TYPE_NAME(NodeRef, "NodeRef"); 23 | RTTI_MAP_TYPE_NAME(GlobalNodeRef, "worldGlobalNodeRef"); 24 | RTTI_MAP_TYPE_NAME(Variant, "Variant"); 25 | 26 | RTTI_MAP_TYPE_PREFIX(DynArray, "array:"); 27 | RTTI_MAP_TYPE_PREFIX(Handle, "handle:"); 28 | RTTI_MAP_TYPE_PREFIX(WeakHandle, "whandle:"); 29 | RTTI_MAP_TYPE_PREFIX(ResourceReference, "rRef:"); 30 | RTTI_MAP_TYPE_PREFIX(ResourceAsyncReference, "raRef:"); 31 | RTTI_MAP_TYPE_PREFIX(CurveData, "curveData:"); 32 | 33 | RTTI_MAP_TYPE_NAME(char, "Uint8"); 34 | RTTI_MAP_TYPE_NAME(ResourcePath, "redResourceReferenceScriptToken"); 35 | } 36 | -------------------------------------------------------------------------------- /lib/Red/TypeInfo/Properties.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.hpp" 4 | #include "Resolving.hpp" 5 | 6 | namespace Red 7 | { 8 | template 9 | requires (!std::is_base_of_v) 10 | inline T& GetPropertyPtr(C* aContext, CName aProp) 11 | { 12 | return GetPropertyPtr(aContext, GetClass(), aProp); 13 | } 14 | 15 | template 16 | inline T* GetPropertyPtr(ISerializable* aContext, CName aProp) 17 | { 18 | if (!aContext) 19 | return nullptr; 20 | 21 | auto prop = aContext->GetType()->GetProperty(aProp); 22 | if (!prop) 23 | return nullptr; 24 | 25 | return prop->GetValuePtr(aContext); 26 | } 27 | 28 | template 29 | inline T* GetPropertyPtr(void* aContext, CClass* aType, CName aProp) 30 | { 31 | if (!aContext || !aType) 32 | return nullptr; 33 | 34 | auto prop = aType->GetProperty(aProp); 35 | if (!prop) 36 | return nullptr; 37 | 38 | return prop->GetValuePtr(aContext); 39 | } 40 | 41 | template 42 | inline T* GetPropertyPtr(void* aContext, CName aType, CName aProp) 43 | { 44 | return GetPropertyPtr(aContext, GetClass(aType), aProp); 45 | } 46 | 47 | template 48 | requires (!std::is_base_of_v) 49 | inline T& GetProperty(C* aContext, CName aProp) 50 | { 51 | return *GetPropertyPtr(aContext, GetClass(), aProp); 52 | } 53 | 54 | template 55 | inline T& GetProperty(ISerializable* aContext, CName aProp) 56 | { 57 | return *GetPropertyPtr(aContext, aProp); 58 | } 59 | 60 | template 61 | inline T& GetProperty(void* aContext, CClass* aType, CName aProp) 62 | { 63 | return *GetPropertyPtr(aContext, aType, aProp); 64 | } 65 | 66 | template 67 | inline T& GetProperty(void* aContext, CName aType, CName aProp) 68 | { 69 | return *GetPropertyPtr(aContext, aType, aProp); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/Red/TypeInfo/Registrar.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | class TypeInfoRegistrar 6 | { 7 | public: 8 | using Callback = void(*)(); 9 | 10 | TypeInfoRegistrar(Callback aRegister, Callback aDescribe) 11 | { 12 | AddRegisterCallback(aRegister); 13 | AddDescribeCallback(aDescribe); 14 | } 15 | 16 | static inline void RegisterDiscovered() 17 | { 18 | QueuePendingRegisterCallbacks(); 19 | QueuePendingDescribeCallbacks(); 20 | } 21 | 22 | static inline void AddRegisterCallback(Callback aRegister) 23 | { 24 | if (aRegister) 25 | { 26 | s_registerCallbacks.push_back(aRegister); 27 | } 28 | } 29 | 30 | static inline void AddDescribeCallback(Callback aDescribe) 31 | { 32 | if (aDescribe) 33 | { 34 | s_describeCallbacks.push_back(aDescribe); 35 | } 36 | } 37 | 38 | private: 39 | static inline void QueuePendingRegisterCallbacks() 40 | { 41 | if (!s_registerCallbacks.empty()) 42 | { 43 | CRTTISystem::Get()->AddRegisterCallback(&OnRegister); 44 | } 45 | } 46 | 47 | static inline void QueuePendingDescribeCallbacks() 48 | { 49 | if (!s_describeCallbacks.empty()) 50 | { 51 | CRTTISystem::Get()->AddPostRegisterCallback(&OnDescribe); 52 | } 53 | } 54 | 55 | static inline void ProcessPendingRegisterCallbacks() 56 | { 57 | auto callbacks = std::move(s_registerCallbacks); 58 | for (const auto& callback :callbacks) 59 | { 60 | callback(); 61 | } 62 | } 63 | 64 | static inline void ProcessPendingDescriberCallbacks() 65 | { 66 | auto callbacks = std::move(s_describeCallbacks); 67 | for (const auto& callback :callbacks) 68 | { 69 | callback(); 70 | } 71 | } 72 | 73 | static inline void OnRegister() 74 | { 75 | ProcessPendingRegisterCallbacks(); 76 | QueuePendingRegisterCallbacks(); 77 | } 78 | 79 | static inline void OnDescribe() 80 | { 81 | ProcessPendingDescriberCallbacks(); 82 | QueuePendingDescribeCallbacks(); 83 | } 84 | 85 | static inline std::vector s_registerCallbacks; 86 | static inline std::vector s_describeCallbacks; 87 | }; 88 | } 89 | -------------------------------------------------------------------------------- /lib/Red/Utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Utils/Handles.hpp" 4 | #include "Utils/JobQueues.hpp" 5 | #include "Utils/Resources.hpp" 6 | -------------------------------------------------------------------------------- /lib/Red/Utils/Handles.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | template 6 | inline WeakHandle& AsWeakHandle(T* aInstance) 7 | { 8 | return *reinterpret_cast*>(&aInstance->ref); 9 | } 10 | 11 | template 12 | inline Handle AsHandle(T* aInstance) 13 | { 14 | return AsWeakHandle(aInstance); 15 | } 16 | 17 | template 18 | inline WeakHandle ToWeakHandle(T* aInstance) 19 | { 20 | if (!aInstance) 21 | return {}; 22 | 23 | return AsWeakHandle(aInstance); 24 | } 25 | 26 | template 27 | inline WeakHandle ToWeakHandle(const Handle& aInstance) 28 | { 29 | return aInstance; 30 | } 31 | 32 | template 33 | inline Handle ToHandle(T* aInstance) 34 | { 35 | if (!aInstance) 36 | return {}; 37 | 38 | if (aInstance->ref.instance) 39 | { 40 | return reinterpret_cast*>(&aInstance->ref)->Lock(); 41 | } 42 | else 43 | { 44 | return Handle(aInstance); 45 | } 46 | } 47 | 48 | template 49 | inline Handle ToHandle(U* aInstance) 50 | { 51 | if (!aInstance) 52 | return {}; 53 | 54 | auto instance = reinterpret_cast(aInstance); 55 | if (instance->ref.instance) 56 | { 57 | return reinterpret_cast*>(&instance->ref)->Lock(); 58 | } 59 | else 60 | { 61 | return Handle(reinterpret_cast(instance)); 62 | } 63 | } 64 | 65 | template 66 | inline Handle MakeScriptedHandle(CClass* aType) 67 | { 68 | return Handle(reinterpret_cast(aType->CreateInstance(true))); 69 | } 70 | 71 | template 72 | inline Handle MakeScriptedHandle(CName aTypeName) 73 | { 74 | auto classType = GetClass(aTypeName); 75 | 76 | if (classType == nullptr) 77 | { 78 | return {}; 79 | } 80 | return Handle(reinterpret_cast(classType->CreateInstance(true))); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/Red/Utils/JobQueues.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | namespace Detail 6 | { 7 | struct WaitingContext 8 | { 9 | std::mutex mutex; 10 | std::condition_variable cv; 11 | bool finished{false}; 12 | }; 13 | } 14 | 15 | template 16 | inline void WaitForQueue(JobQueue& aQueue, const W& aTimeout) 17 | { 18 | auto context = std::make_shared(); 19 | 20 | aQueue.Dispatch([context]() { 21 | context->finished = true; 22 | context->cv.notify_all(); 23 | }); 24 | 25 | std::unique_lock lock(context->mutex); 26 | context->cv.wait_for(lock, aTimeout, [context]() { return context->finished; }); 27 | } 28 | 29 | template 30 | inline void WaitForJob(const JobHandle& aJob, const W& aTimeout) 31 | { 32 | JobQueue queue; 33 | queue.Wait(aJob); 34 | WaitForQueue(queue, aTimeout); 35 | } 36 | 37 | template typename V, typename R, typename W, typename... A> 38 | inline void WaitForJobs(const V& aJobs, const W& aTimeout) 39 | { 40 | JobQueue queue; 41 | for (const auto& job : aJobs) 42 | { 43 | queue.Wait(job); 44 | } 45 | WaitForQueue(queue, aTimeout); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/Red/Utils/Resources.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "JobQueues.hpp" 4 | 5 | namespace Red 6 | { 7 | namespace Detail 8 | { 9 | template 10 | struct ResourceTraits; 11 | 12 | template 13 | struct ResourceTraits>> 14 | { 15 | inline static bool IsFinished(const SharedPtr>& aToken) 16 | { 17 | return aToken->IsLoaded() || aToken->IsFailed(); 18 | } 19 | 20 | template typename V, typename... A> 21 | inline static bool AllFinished(const V>, A...>& aTokens) 22 | { 23 | return std::ranges::all_of(aTokens, &IsFinished); 24 | } 25 | 26 | inline static JobHandle& GetJobHandle(const SharedPtr>& aToken) 27 | { 28 | return aToken->job; 29 | } 30 | 31 | inline static ResourcePath GetPath(const SharedPtr>& aToken) 32 | { 33 | return aToken->path; 34 | } 35 | }; 36 | 37 | template 38 | struct ResourceTraits> 39 | { 40 | inline static bool IsFinished(const ResourceReference& aReference) 41 | { 42 | return aReference.token->IsLoaded() || aReference.token->IsFailed(); 43 | } 44 | 45 | template typename V, typename... A> 46 | inline static bool AllFinished(const V, A...>& aReferences) 47 | { 48 | return std::ranges::all_of(aReferences, &IsFinished); 49 | } 50 | 51 | inline static JobHandle& GetJobHandle(const ResourceReference& aReference) 52 | { 53 | return aReference.token->job; 54 | } 55 | 56 | inline static ResourcePath GetPath(const ResourceReference& aReference) 57 | { 58 | return aReference.token->path; 59 | } 60 | }; 61 | } 62 | 63 | template 64 | inline void WaitForResource(const R& aResource, const W& aTimeout) 65 | { 66 | using Trait = Detail::ResourceTraits; 67 | 68 | if (!Trait::IsFinished(aResource)) 69 | { 70 | WaitForJob(Trait::GetJobHandle(aResource), aTimeout); 71 | } 72 | } 73 | 74 | template typename V, typename R, typename W, typename... A> 75 | inline void WaitForResources(const V& aResources, const W& aTimeout) 76 | { 77 | using Trait = Detail::ResourceTraits; 78 | 79 | if (!Trait::AllFinished(aResources)) 80 | { 81 | JobQueue queue; 82 | for (const auto& resource : aResources) 83 | { 84 | queue.Wait(Trait::GetJobHandle(resource)); 85 | } 86 | WaitForQueue(queue, aTimeout); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/Support/MinHook/MinHookProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "MinHookProvider.hpp" 2 | 3 | #include 4 | 5 | Support::MinHookProvider::MinHookProvider() 6 | { 7 | MH_Initialize(); 8 | 9 | SetDefault(*this); 10 | } 11 | 12 | Support::MinHookProvider::~MinHookProvider() 13 | { 14 | MH_DisableHook(MH_ALL_HOOKS); 15 | MH_Uninitialize(); 16 | } 17 | 18 | bool Support::MinHookProvider::HookAttach(uintptr_t aAddress, void* aCallback) 19 | { 20 | return HookAttach(aAddress, aCallback, nullptr); 21 | } 22 | 23 | bool Support::MinHookProvider::HookAttach(uintptr_t aAddress, void* aCallback, void** aOriginal) 24 | { 25 | if (MH_CreateHook(reinterpret_cast(aAddress), aCallback, aOriginal) != MH_OK) 26 | return false; 27 | 28 | if (MH_EnableHook(reinterpret_cast(aAddress)) != MH_OK) 29 | { 30 | MH_RemoveHook(reinterpret_cast(aAddress)); 31 | return false; 32 | } 33 | 34 | return true; 35 | } 36 | 37 | bool Support::MinHookProvider::HookDetach(uintptr_t aAddress) 38 | { 39 | if (MH_DisableHook(reinterpret_cast(aAddress)) != MH_OK) 40 | return false; 41 | 42 | if (MH_RemoveHook(reinterpret_cast(aAddress)) != MH_OK) 43 | return false; 44 | 45 | return true; 46 | } 47 | -------------------------------------------------------------------------------- /lib/Support/MinHook/MinHookProvider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Hooking/HookingDriver.hpp" 5 | 6 | namespace Support 7 | { 8 | class MinHookProvider 9 | : public Core::Feature 10 | , public Core::HookingDriver 11 | { 12 | public: 13 | MinHookProvider(); 14 | ~MinHookProvider() override; 15 | 16 | bool HookAttach(uintptr_t aAddress, void* aCallback) override; 17 | bool HookAttach(uintptr_t aAddress, void* aCallback, void** aOriginal) override; 18 | bool HookDetach(uintptr_t aAddress) override; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /lib/Support/RED4ext/RED4extProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "RED4extProvider.hpp" 2 | 3 | Support::RED4extProvider::RED4extProvider(RED4ext::PluginHandle aPlugin, const RED4ext::Sdk* aSdk) noexcept 4 | : m_plugin(aPlugin) 5 | , m_sdk(aSdk) 6 | , m_enableLogging(false) 7 | , m_enableHooking(false) 8 | , m_enableAddressLibrary(false) 9 | { 10 | } 11 | 12 | void Support::RED4extProvider::OnInitialize() 13 | { 14 | if (m_enableLogging) 15 | { 16 | LoggingDriver::SetDefault(*this); 17 | } 18 | 19 | if (m_enableHooking) 20 | { 21 | HookingDriver::SetDefault(*this); 22 | } 23 | 24 | if (m_enableAddressLibrary) 25 | { 26 | AddressResolver::SetDefault(*this); 27 | } 28 | } 29 | 30 | void Support::RED4extProvider::LogInfo(const std::string_view& aMessage) 31 | { 32 | m_sdk->logger->Info(m_plugin, aMessage.data()); 33 | } 34 | 35 | void Support::RED4extProvider::LogWarning(const std::string_view& aMessage) 36 | { 37 | m_sdk->logger->Warn(m_plugin, aMessage.data()); 38 | } 39 | 40 | void Support::RED4extProvider::LogError(const std::string_view& aMessage) 41 | { 42 | m_sdk->logger->Error(m_plugin, aMessage.data()); 43 | } 44 | 45 | void Support::RED4extProvider::LogDebug(const std::string_view& aMessage) 46 | { 47 | m_sdk->logger->Debug(m_plugin, aMessage.data()); 48 | } 49 | 50 | void Support::RED4extProvider::LogFlush() 51 | { 52 | } 53 | 54 | bool Support::RED4extProvider::HookAttach(uintptr_t aAddress, void* aCallback) 55 | { 56 | return m_sdk->hooking->Attach(m_plugin, reinterpret_cast(aAddress), aCallback, nullptr); 57 | } 58 | 59 | bool Support::RED4extProvider::HookAttach(uintptr_t aAddress, void* aCallback, void** aOriginal) 60 | { 61 | return m_sdk->hooking->Attach(m_plugin, reinterpret_cast(aAddress), aCallback, aOriginal); 62 | } 63 | 64 | bool Support::RED4extProvider::HookDetach(uintptr_t aAddress) 65 | { 66 | return m_sdk->hooking->Detach(m_plugin, reinterpret_cast(aAddress)); 67 | } 68 | 69 | uintptr_t Support::RED4extProvider::ResolveAddress(uint32_t aAddressID) 70 | { 71 | return RED4ext::UniversalRelocBase::Resolve(aAddressID); 72 | } 73 | -------------------------------------------------------------------------------- /lib/Support/RED4ext/RED4extProvider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Hooking/HookingDriver.hpp" 5 | #include "Core/Logging/LoggingDriver.hpp" 6 | #include "Core/Memory/AddressResolver.hpp" 7 | 8 | namespace Support 9 | { 10 | class RED4extProvider 11 | : public Core::Feature 12 | , public Core::LoggingDriver 13 | , public Core::HookingDriver 14 | , public Core::AddressResolver 15 | { 16 | public: 17 | RED4extProvider(RED4ext::PluginHandle aPlugin, const RED4ext::Sdk* aSdk) noexcept; 18 | 19 | void LogInfo(const std::string_view& aMessage) override; 20 | void LogWarning(const std::string_view& aMessage) override; 21 | void LogError(const std::string_view& aMessage) override; 22 | void LogDebug(const std::string_view& aMessage) override; 23 | void LogFlush() override; 24 | 25 | bool HookAttach(uintptr_t aAddress, void* aCallback) override; 26 | bool HookAttach(uintptr_t aAddress, void* aCallback, void** aOriginal) override; 27 | bool HookDetach(uintptr_t aAddress) override; 28 | 29 | uintptr_t ResolveAddress(uint32_t aAddressID) override; 30 | 31 | auto EnableLogging() noexcept 32 | { 33 | m_enableLogging = true; 34 | return Defer(this); 35 | } 36 | 37 | auto EnableHooking() noexcept 38 | { 39 | m_enableHooking = true; 40 | return Defer(this); 41 | } 42 | 43 | auto EnableAddressLibrary() noexcept 44 | { 45 | m_enableAddressLibrary = true; 46 | return Defer(this); 47 | } 48 | 49 | auto RegisterScripts(const std::filesystem::path& aPath) noexcept 50 | { 51 | m_sdk->scripts->Add(m_plugin, aPath.c_str()); 52 | return Defer(this); 53 | } 54 | 55 | protected: 56 | void OnInitialize() override; 57 | 58 | RED4ext::PluginHandle m_plugin; 59 | const RED4ext::Sdk* m_sdk; 60 | bool m_enableLogging; 61 | bool m_enableHooking; 62 | bool m_enableAddressLibrary; 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /lib/Support/RedLib/RedLibProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "RedLibProvider.hpp" 2 | #include "Red/TypeInfo/Registrar.hpp" 3 | 4 | void Support::RedLibProvider::OnBootstrap() 5 | { 6 | Red::TypeInfoRegistrar::RegisterDiscovered(); 7 | } 8 | -------------------------------------------------------------------------------- /lib/Support/RedLib/RedLibProvider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | 5 | namespace Support 6 | { 7 | class RedLibProvider : public Core::Feature 8 | { 9 | void OnBootstrap() override; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /lib/Support/Spdlog/SpdlogProvider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Logging/LoggingDriver.hpp" 5 | 6 | namespace Support 7 | { 8 | class SpdlogProvider 9 | : public Core::Feature 10 | , public Core::LoggingDriver 11 | { 12 | public: 13 | void LogInfo(const std::string_view& aMessage) override; 14 | void LogWarning(const std::string_view& aMessage) override; 15 | void LogError(const std::string_view& aMessage) override; 16 | void LogDebug(const std::string_view& aMessage) override; 17 | void LogFlush() override; 18 | 19 | auto SetLogPath(const std::filesystem::path& aPath) noexcept 20 | { 21 | m_baseLogPath = aPath; 22 | return Defer(this); 23 | } 24 | 25 | auto AppendTimestampToLogName() noexcept 26 | { 27 | m_appendTimestamp = true; 28 | return Defer(this); 29 | } 30 | 31 | auto CreateRecentLogSymlink() noexcept 32 | { 33 | m_recentSymlink = true; 34 | return Defer(this); 35 | } 36 | 37 | auto SetMaxLogFiles(int32_t aMaxFiles) noexcept 38 | { 39 | m_maxLogCount = aMaxFiles; 40 | return Defer(this); 41 | } 42 | 43 | protected: 44 | void OnInitialize() override; 45 | 46 | std::filesystem::path m_baseLogPath; 47 | bool m_appendTimestamp{ false }; 48 | bool m_recentSymlink{ false }; 49 | int32_t m_maxLogCount{ 10 }; 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /src/App/Application.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.hpp" 2 | #include "App/Archives/ArchiveLoader.hpp" 3 | #include "App/Archives/ArchiveLogger.hpp" 4 | #include "App/Archives/ArchiveWatcher.hpp" 5 | #include "App/Environment.hpp" 6 | #include "App/Scripts/ObjectRegistry.hpp" 7 | #include "App/Scripts/ScriptLoader.hpp" 8 | #include "App/Scripts/ScriptLogger.hpp" 9 | #include "App/Scripts/ScriptReporter.hpp" 10 | #include "App/Scripts/ScriptWatcher.hpp" 11 | #include "App/Shared/ResourcePathRegistry.hpp" 12 | #include "App/Tweaks/TweakLoader.hpp" 13 | #include "App/Tweaks/TweakWatcher.hpp" 14 | #include "App/UI/InkWidgetCollector.hpp" 15 | #include "App/World/WorldNodeRegistry.hpp" 16 | #include "Core/Foundation/RuntimeProvider.hpp" 17 | #include "Support/MinHook/MinHookProvider.hpp" 18 | #include "Support/RED4ext/RED4extProvider.hpp" 19 | #include "Support/RedLib/RedLibProvider.hpp" 20 | #include "Support/Spdlog/SpdlogProvider.hpp" 21 | 22 | App::Application::Application(HMODULE aHandle, const RED4ext::Sdk* aSdk) 23 | { 24 | Register(aHandle)->SetBaseImagePathDepth(2); 25 | 26 | Register(); 27 | Register()->AppendTimestampToLogName()->CreateRecentLogSymlink(); 28 | Register(aHandle, aSdk)->EnableAddressLibrary(); 29 | Register(); 30 | 31 | Register(); 32 | Register(Env::ArchiveHotDir()); 33 | Register(); 34 | 35 | Register(Env::ScriptSourceDir(), Env::ScriptBlobPath()); 36 | Register(Env::ScriptHotFile()); 37 | Register(); 38 | Register(Env::IsPrePatch212a()); 39 | Register(); 40 | 41 | Register(Env::TweakSourceDir()); 42 | Register(Env::TweakHotFile()); 43 | 44 | Register(Env::KnownHashesPath()); 45 | Register(); 46 | Register(Env::IsPrePatch212a()); 47 | } 48 | -------------------------------------------------------------------------------- /src/App/Application.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Application.hpp" 4 | 5 | namespace App 6 | { 7 | class Application : public Core::Application 8 | { 9 | public: 10 | explicit Application(HMODULE aHandle, const RED4ext::Sdk* aSdk = nullptr); 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /src/App/Archives/ArchiveLoader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Hooking/HookingAgent.hpp" 5 | #include "Core/Logging/LoggingAgent.hpp" 6 | #include "Red/ResourceDepot.hpp" 7 | 8 | namespace App 9 | { 10 | class ArchiveLoader 11 | : public Core::Feature 12 | , public Core::LoggingAgent 13 | , public Core::HookingAgent 14 | { 15 | public: 16 | bool SwapArchives(const std::filesystem::path& aArchiveHotDir); 17 | 18 | private: 19 | struct DepotLocker 20 | { 21 | DepotLocker(); 22 | DepotLocker(DepotLocker&&) = default; 23 | ~DepotLocker(); 24 | 25 | void Bypass(Red::ResourcePath aPath); 26 | 27 | std::unique_lock m_guard; 28 | inline static std::shared_mutex s_mutex; 29 | inline static Core::Map s_bypass; 30 | }; 31 | 32 | static bool CollectArchiveGroups(Red::DynArray& aGroups); 33 | static bool ResolveArchivePaths(const Red::DynArray& aGroups, 34 | const std::filesystem::path& aArchiveHotDir, 35 | Red::DynArray& aArchiveHotPaths, 36 | Red::DynArray& aArchiveModPaths); 37 | static void MoveArchiveFiles(Red::DynArray& aHotPaths, Red::DynArray& aModPaths); 38 | static void UnloadModArchives(const Red::DynArray& aGroups, 39 | const Red::DynArray& aArchivePaths); 40 | static void LoadModArchives(const Red::DynArray& aGroups, 41 | const Red::DynArray& aArchivePaths, 42 | Red::DynArray& aLoadedResources); 43 | static Red::Archive* FindArchivePosition(Red::DynArray& aArchives, const Red::CString& aArchivePath); 44 | static bool InvalidateResources(const Red::DynArray& aPaths, 45 | Core::UniquePtr& aDepotLocker); 46 | static bool MoveExtensionFiles(const Red::DynArray& aGroups, 47 | const std::filesystem::path& aHotDir); 48 | static void ReloadExtensions(); 49 | 50 | std::mutex m_updateLock; 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /src/App/Archives/ArchiveLogger.cpp: -------------------------------------------------------------------------------- 1 | #include "ArchiveLogger.hpp" 2 | #include "App/Shared/ResourcePathRegistry.hpp" 3 | #include "Core/Facades/Container.hpp" 4 | #include "Red/ResourceDepot.hpp" 5 | 6 | void App::ArchiveLogger::OnBootstrap() 7 | { 8 | HookAfter(&OnRequestResource); 9 | } 10 | 11 | void App::ArchiveLogger::OnRequestResource(Red::ResourceDepot* aDepot, const uintptr_t* aResourceHandle, 12 | Red::ResourcePath aResourcePath, const int32_t* aArchiveHandle) 13 | { 14 | if (!*aResourceHandle) 15 | { 16 | if (s_knownBrokenPaths.contains(aResourcePath)) 17 | return; 18 | 19 | auto archiveInfo = GetArchiveInfo(aDepot, aArchiveHandle); 20 | 21 | if (archiveInfo.scope == Red::ArchiveScope::Content || archiveInfo.scope == Red::ArchiveScope::DLC) 22 | return; 23 | 24 | auto resourcePathStr = Core::Resolve()->ResolvePath(aResourcePath); 25 | 26 | LogWarning(R"(Resource not found: hash={} path="{}" archive="{}")", 27 | aResourcePath.hash, 28 | !resourcePathStr.empty() ? resourcePathStr.data() : "?", 29 | !archiveInfo.path.empty() ? archiveInfo.path.data() : "?"); 30 | } 31 | } 32 | 33 | App::ArchiveLogger::ArchiveInfo App::ArchiveLogger::GetArchiveInfo(Red::ResourceDepot* aDepot, 34 | const int32_t* aArchiveHandle) 35 | { 36 | if (!aArchiveHandle || !*aArchiveHandle) 37 | return {}; 38 | 39 | for (const auto& group : aDepot->groups) 40 | { 41 | for (const auto& archive : group.archives) 42 | { 43 | if (archive.asyncHandle == *aArchiveHandle) 44 | { 45 | return {group.scope, {archive.path.c_str() + group.basePath.Length()}}; 46 | } 47 | } 48 | } 49 | 50 | return {}; 51 | } 52 | -------------------------------------------------------------------------------- /src/App/Archives/ArchiveLogger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Hooking/HookingAgent.hpp" 5 | #include "Core/Logging/LoggingAgent.hpp" 6 | 7 | namespace App 8 | { 9 | class ArchiveLogger 10 | : public Core::Feature 11 | , public Core::HookingAgent 12 | , public Core::LoggingAgent 13 | { 14 | protected: 15 | struct ArchiveInfo 16 | { 17 | Red::ArchiveScope scope; 18 | std::string_view path; 19 | }; 20 | 21 | void OnBootstrap() override; 22 | static void OnRequestResource(Red::ResourceDepot* aDepot, const uintptr_t* aResourceHandle, 23 | Red::ResourcePath aResourcePath, const int32_t* aArchiveHandle); 24 | 25 | static ArchiveInfo GetArchiveInfo(Red::ResourceDepot* aDepot, const int32_t* aArchiveHandle); 26 | 27 | static Core::Set s_knownBrokenPaths; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/App/Archives/ArchiveWatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "ArchiveWatcher.hpp" 2 | #include "App/Archives/ArchiveLoader.hpp" 3 | #include "Core/Facades/Container.hpp" 4 | 5 | App::ArchiveWatcher::ArchiveWatcher(std::filesystem::path aHotDir) 6 | : AbstractWatcher(std::chrono::milliseconds(500)) 7 | , m_archiveHotDir(std::move(aHotDir)) 8 | { 9 | bool canWatch = false; 10 | 11 | if (std::filesystem::exists(m_archiveHotDir)) 12 | { 13 | canWatch = true; 14 | } 15 | else 16 | { 17 | std::error_code error; 18 | canWatch = std::filesystem::create_directory(m_archiveHotDir, error); 19 | } 20 | 21 | if (canWatch) 22 | { 23 | LogInfo("[ArchiveWatcher] Watching \"{}\" for changes...", m_archiveHotDir.string()); 24 | Watch(m_archiveHotDir); 25 | } 26 | else 27 | { 28 | LogError("[ArchiveWatcher] Can't watch \"{}\" for changes, no access.", m_archiveHotDir.string()); 29 | } 30 | } 31 | 32 | bool App::ArchiveWatcher::Filter(const std::filesystem::path& aPath) 33 | { 34 | return aPath.extension() == L".archive" || aPath.extension() == L".xl"; 35 | } 36 | 37 | bool App::ArchiveWatcher::Process() 38 | { 39 | return Core::Resolve()->SwapArchives(m_archiveHotDir); 40 | } 41 | -------------------------------------------------------------------------------- /src/App/Archives/ArchiveWatcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "App/Foundation/AbstractWatcher.hpp" 4 | 5 | namespace App 6 | { 7 | class ArchiveLoader; 8 | 9 | class ArchiveWatcher : public AbstractWatcher 10 | { 11 | public: 12 | ArchiveWatcher(std::filesystem::path aHotDir); 13 | 14 | protected: 15 | bool Filter(const std::filesystem::path& aPath) override; 16 | bool Process() override; 17 | 18 | std::filesystem::path m_archiveHotDir; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/App/Environment.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Facades/Runtime.hpp" 4 | 5 | namespace App::Env 6 | { 7 | inline std::filesystem::path GameDir() 8 | { 9 | return Core::Runtime::GetRootDir(); 10 | } 11 | 12 | inline std::filesystem::path ArchiveModDir() 13 | { 14 | return Core::Runtime::GetRootDir() / L"archive" / L"pc" / L"mod"; 15 | } 16 | 17 | inline std::filesystem::path ArchiveHotDir() 18 | { 19 | return Core::Runtime::GetRootDir() / L"archive" / L"pc" / L"hot"; 20 | } 21 | 22 | inline std::filesystem::path ScriptBlobPath() 23 | { 24 | return Core::Runtime::GetRootDir() / L"r6" / L"cache" / L"final.redscripts"; 25 | } 26 | 27 | inline std::filesystem::path ScriptSourceDir() 28 | { 29 | return Core::Runtime::GetRootDir() / L"r6" / L"scripts"; 30 | } 31 | 32 | inline std::filesystem::path ScriptHotFile() 33 | { 34 | return Core::Runtime::GetModuleDir() / L".hot-scripts"; 35 | } 36 | 37 | inline std::filesystem::path TweakSourceDir() 38 | { 39 | return Core::Runtime::GetRootDir() / L"r6" / L"tweaks"; 40 | } 41 | 42 | inline std::filesystem::path TweakHotFile() 43 | { 44 | return Core::Runtime::GetModuleDir() / L".hot-tweaks"; 45 | } 46 | 47 | inline std::filesystem::path KnownHashesPath() 48 | { 49 | return Core::Runtime::GetModuleDir() / L"Resources.txt"; 50 | } 51 | 52 | inline bool IsPrePatch212a() 53 | { 54 | auto& fileVer = Core::Runtime::GetHost()->GetFileVer(); 55 | auto patchVer = RED4EXT_RUNTIME_2_12_HOTFIX_1; 56 | 57 | if (fileVer.major != patchVer.major) 58 | { 59 | return fileVer.major < patchVer.major; 60 | } 61 | 62 | if (fileVer.minor != patchVer.minor) 63 | { 64 | return fileVer.minor < patchVer.minor; 65 | } 66 | 67 | if (fileVer.build != patchVer.build) 68 | { 69 | return fileVer.build < patchVer.build; 70 | } 71 | 72 | if (fileVer.revision != patchVer.revision) 73 | { 74 | return fileVer.revision < patchVer.revision; 75 | } 76 | 77 | return false; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/App/Facade.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "App/Project.hpp" 4 | 5 | namespace App 6 | { 7 | class Facade : public Red::IScriptable 8 | { 9 | public: 10 | static Red::CString GetVersion(); 11 | 12 | static bool HotInstall(const Red::CString& aPath); 13 | static void ReloadArchives(); 14 | static void ReloadScripts(); 15 | static void ReloadTweaks(); 16 | 17 | static Red::CName GetTypeName(const Red::WeakHandle& aInstace); 18 | static bool IsInstanceOf(const Red::WeakHandle& aInstace, Red::CName aType); 19 | static uint64_t GetObjectHash(const Red::WeakHandle& aInstace); 20 | static Red::Handle CloneObject(const Red::Handle& aInstace); 21 | 22 | static Red::DynArray> GetEntityComponents(const Red::WeakHandle& aEntity); 23 | static Red::ResourceAsyncReference<> GetEntityTemplatePath(const Red::WeakHandle& aEntity); 24 | static Red::DynArray GetEntityVisualTags(const Red::WeakHandle& aEntity); 25 | 26 | static uint64_t GetComponentAppearanceResourceHash(const Red::Handle& aComponent); 27 | static Red::CName GetComponentAppearanceDefinition(const Red::Handle& aComponent); 28 | 29 | static Red::CString GetResourcePath(uint64_t aHash); 30 | static Red::CString GetReferencePath(const Red::Handle& aInstace, Red::CName aPropName); 31 | static uint64_t GetCRUIDHash(Red::CRUID aValue); 32 | 33 | static Red::DynArray> GetAllHandles(); 34 | 35 | RTTI_IMPL_TYPEINFO(Facade); 36 | }; 37 | } 38 | 39 | RTTI_DEFINE_CLASS(App::Facade, App::Project::Name, { 40 | RTTI_ABSTRACT(); 41 | RTTI_METHOD(GetVersion, "Version"); 42 | 43 | RTTI_METHOD(HotInstall); 44 | RTTI_METHOD(ReloadArchives); 45 | RTTI_METHOD(ReloadScripts); 46 | RTTI_METHOD(ReloadTweaks); 47 | 48 | RTTI_METHOD(GetTypeName); 49 | RTTI_METHOD(IsInstanceOf); 50 | RTTI_METHOD(GetObjectHash); 51 | RTTI_METHOD(CloneObject); 52 | 53 | RTTI_METHOD(GetEntityComponents); 54 | RTTI_METHOD(GetEntityTemplatePath); 55 | RTTI_METHOD(GetEntityVisualTags); 56 | 57 | RTTI_METHOD(GetComponentAppearanceResourceHash); 58 | RTTI_METHOD(GetComponentAppearanceDefinition); 59 | 60 | RTTI_METHOD(GetResourcePath); 61 | RTTI_METHOD(GetReferencePath); 62 | RTTI_METHOD(GetCRUIDHash); 63 | 64 | RTTI_METHOD(GetAllHandles); 65 | }) 66 | -------------------------------------------------------------------------------- /src/App/Foundation/AbstractWatcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Logging/LoggingAgent.hpp" 5 | 6 | namespace App 7 | { 8 | class AbstractWatcher 9 | : public Core::Feature 10 | , public Core::LoggingAgent 11 | { 12 | public: 13 | static constexpr auto DefaulProcessingDelay = std::chrono::milliseconds(250); 14 | static constexpr auto DefaulRetryDelay = std::chrono::milliseconds(100); 15 | static constexpr auto DefaulRetryCount = 50; 16 | 17 | AbstractWatcher(std::chrono::milliseconds aProcessingDelay = DefaulProcessingDelay, 18 | std::chrono::milliseconds aRetryDelay = DefaulRetryDelay, 19 | int32_t aRetryCount = DefaulRetryCount); 20 | AbstractWatcher(const std::filesystem::path& aTarget, 21 | std::chrono::milliseconds aProcessingDelay = DefaulProcessingDelay, 22 | std::chrono::milliseconds aRetryDelay = DefaulRetryDelay, 23 | int32_t aRetryCount = DefaulRetryCount); 24 | 25 | protected: 26 | using FileWatch = filewatch::FileWatch; 27 | using FileEvent = filewatch::Event; 28 | 29 | void Watch(const std::filesystem::path& aTarget); 30 | void Track(const std::filesystem::path& aTarget, const std::filesystem::path& aPath, FileEvent aEvent); 31 | void Schedule(); 32 | void Cancel(); 33 | 34 | virtual bool Filter(const std::filesystem::path& aPath); 35 | virtual bool Process() = 0; 36 | 37 | std::chrono::milliseconds m_processingDelay; 38 | std::chrono::milliseconds m_retryDelay; 39 | int32_t m_retryCount; 40 | std::vector> m_watches; 41 | std::chrono::time_point m_processingTime; 42 | std::mutex m_processingLock; 43 | bool m_processingScheduled; 44 | std::future m_future; 45 | bool m_threadStarted; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /src/App/Scripts/ObjectRegistry.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Hooking/HookingAgent.hpp" 5 | #include "Core/Logging/LoggingAgent.hpp" 6 | 7 | namespace App 8 | { 9 | class ObjectRegistry 10 | : public Core::Feature 11 | , public Core::LoggingAgent 12 | , public Core::HookingAgent 13 | { 14 | public: 15 | void CollectSerializables(Red::DynArray>& aOut); 16 | void CollectScriptables(Red::DynArray>& aOut); 17 | void CreateSnapshot(); 18 | void RestoreSnapshot(); 19 | 20 | protected: 21 | void OnBootstrap() override; 22 | 23 | static void* OnCreateHandle(Red::Handle& aHandle, Red::ISerializable* aObject); 24 | static void Shrink(); 25 | 26 | template 27 | inline static T* GetValuePtr(T* aPtr, uint32_t aOffset) 28 | { 29 | return reinterpret_cast(reinterpret_cast(aPtr) + aOffset); 30 | } 31 | 32 | struct PropDesc 33 | { 34 | Red::CName name; 35 | Red::CBaseRTTIType* type; 36 | uint32_t valueOffset; 37 | }; 38 | 39 | struct ClassDesc 40 | { 41 | Core::Vector props; 42 | }; 43 | 44 | inline static std::shared_mutex s_registryLock; 45 | inline static Core::Vector> s_objects; 46 | 47 | inline static std::shared_mutex s_snapshotLock; 48 | inline static Core::Map s_snapshot; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /src/App/Scripts/ScriptLoader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Hooking/HookingAgent.hpp" 5 | #include "Core/Logging/LoggingAgent.hpp" 6 | #include "Red/ScriptBundle.hpp" 7 | #include "Red/ScriptReport.hpp" 8 | 9 | 10 | namespace App 11 | { 12 | class ScriptLoader 13 | : public Core::Feature 14 | , public Core::LoggingAgent 15 | , public Core::HookingAgent 16 | { 17 | public: 18 | ScriptLoader(std::filesystem::path aSourceDir, std::filesystem::path aCachePath); 19 | 20 | bool CanReloadScripts(); 21 | void ReloadScripts(); 22 | 23 | protected: 24 | bool CompileScripts(Red::ScriptBundle& aBundle, bool aInjectCustomCacheArg = false); 25 | bool ValidateScripts(Red::ScriptBundle& aBundle, Red::ScriptReport& aReport); 26 | bool BindScripts(Red::ScriptBundle& aBundle, Red::ScriptReport& aReport); 27 | void CaptureScriptableData(); 28 | void RestoreScriptableData(); 29 | 30 | static bool HasScriptedProperties(Red::CClass* aClass); 31 | static void ClearScriptedProperties(Red::CClass* aClass); 32 | static void ShowErrorBox(const char* aCaption, const Red::CString& aMessage); 33 | 34 | std::filesystem::path m_scriptSourceDir; 35 | std::filesystem::path m_scriptBlobPath; 36 | std::mutex m_reloadMutex; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /src/App/Scripts/ScriptLogger.cpp: -------------------------------------------------------------------------------- 1 | #include "ScriptLogger.hpp" 2 | #include "Red/Logger.hpp" 3 | 4 | void App::ScriptLogger::OnBootstrap() 5 | { 6 | HookBefore(&OnLogChannel); 7 | } 8 | 9 | void App::ScriptLogger::OnLogChannel(void*, Red::CStackFrame* aFrame, void*, void*) 10 | { 11 | const auto rewind = aFrame->code; 12 | 13 | Red::CName channel; 14 | Red::GetParameter(aFrame, &channel); 15 | 16 | if (channel == "DEBUG") 17 | { 18 | Red::ScriptRef message; 19 | Red::GetParameter(aFrame, &message); 20 | 21 | if (message.ref) 22 | { 23 | LogInfo(message.ref->c_str()); 24 | } 25 | } 26 | 27 | aFrame->code = rewind; 28 | aFrame->currentParam = 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/App/Scripts/ScriptLogger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Hooking/HookingAgent.hpp" 5 | #include "Core/Logging/LoggingAgent.hpp" 6 | #include "Red/ScriptBundle.hpp" 7 | #include "Red/ScriptReport.hpp" 8 | 9 | namespace App 10 | { 11 | 12 | class ScriptLogger 13 | : public Core::Feature 14 | , public Core::LoggingAgent 15 | , public Core::HookingAgent 16 | { 17 | protected: 18 | void OnBootstrap() override; 19 | 20 | static void OnLogChannel(void*, Red::CStackFrame* aFrame, void*, void*); 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/App/Scripts/ScriptReporter.cpp: -------------------------------------------------------------------------------- 1 | #include "ScriptReporter.hpp" 2 | #include "Red/EngineConfig.hpp" 3 | #include "Red/GameEngine.hpp" 4 | 5 | App::ScriptReporter::ScriptReporter(bool aCompatMode) 6 | : m_compatMode(aCompatMode) 7 | { 8 | } 9 | 10 | void App::ScriptReporter::ShowErrorBox(const char* aCaption, const char* aMessage) 11 | { 12 | auto engine = Red::CGameEngine::Get(); 13 | auto hWnd = engine->unkD0 ? reinterpret_cast(engine->unkD0->hWnd) : nullptr; 14 | 15 | MessageBoxA(hWnd, aMessage, aCaption, MB_SYSTEMMODAL | MB_ICONERROR); 16 | } 17 | 18 | void App::ScriptReporter::ShowErrorBox(const char* aCaption, const std::string& aMessage) 19 | { 20 | ShowErrorBox(aCaption, aMessage.c_str()); 21 | } 22 | 23 | void App::ScriptReporter::ShowErrorBox(const char* aCaption, const Red::CString& aMessage) 24 | { 25 | ShowErrorBox(aCaption, aMessage.c_str()); 26 | } 27 | 28 | void App::ScriptReporter::ShowErrorBox(const char* aCaption, const Red::DynArray& aMessages) 29 | { 30 | std::string message; 31 | 32 | bool eol = false; 33 | for (const auto& entry : aMessages) 34 | { 35 | if (eol) 36 | { 37 | message.append("\n"); 38 | } 39 | 40 | message.append(entry.c_str()); 41 | eol = true; 42 | } 43 | 44 | ShowErrorBox(aCaption, message.c_str()); 45 | } 46 | 47 | void App::ScriptReporter::OnBootstrap() 48 | { 49 | Hook(&ScriptReporter::OnLoadScripts); 50 | } 51 | 52 | bool App::ScriptReporter::OnLoadScripts(Red::CBaseEngine& aEngine, Red::CString& aPath, 53 | uint64_t aTimestamp, uint64_t a4) 54 | { 55 | // This is a fallback mode which never works for the release game 56 | if (!aTimestamp) 57 | return false; 58 | 59 | const auto compilationEnabled = Red::EngineConfig::Get()->GetValue("Scripts", "EnableCompilation"); 60 | if (aEngine.scriptsCompilationErrors.Length() > 0 || (compilationEnabled && aEngine.scriptsBlobPath.Length() == 0)) 61 | { 62 | ExitProcess(0xDEAD); 63 | return false; 64 | } 65 | 66 | // When enabled the errors will be added to the CBaseEngine::scriptsValidationErrors 67 | aEngine.scriptsSilentValidation = true; 68 | 69 | bool success = Raw::CBaseEngine::LoadScripts(aEngine, aPath, aTimestamp, a4); 70 | 71 | if (!success && aEngine.scriptsValidationErrors.size > 0) 72 | { 73 | ShowErrorBox("Validation error", aEngine.scriptsValidationErrors); 74 | ExitProcess(0xDEAD); 75 | return false; 76 | } 77 | 78 | return success; 79 | } 80 | -------------------------------------------------------------------------------- /src/App/Scripts/ScriptReporter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Hooking/HookingAgent.hpp" 5 | 6 | namespace App 7 | { 8 | class ScriptReporter 9 | : public Core::Feature 10 | , public Core::HookingAgent 11 | { 12 | public: 13 | ScriptReporter(bool aCompatMode); 14 | 15 | void ShowErrorBox(const char* aCaption, const char* aMessage); 16 | void ShowErrorBox(const char* aCaption, const std::string& aMessage); 17 | void ShowErrorBox(const char* aCaption, const Red::CString& aMessage); 18 | void ShowErrorBox(const char* aCaption, const Red::DynArray& aMessages); 19 | 20 | protected: 21 | void OnBootstrap() override; 22 | bool OnLoadScripts(Red::CBaseEngine& aEngine, Red::CString& aPath, uint64_t a3, uint64_t a4); 23 | 24 | bool m_compatMode; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /src/App/Scripts/ScriptWatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "ScriptWatcher.hpp" 2 | #include "App/Scripts/ScriptLoader.hpp" 3 | #include "Core/Facades/Container.hpp" 4 | 5 | App::ScriptWatcher::ScriptWatcher(std::filesystem::path aRequestPath) 6 | : AbstractWatcher(aRequestPath, std::chrono::milliseconds(100)) 7 | , m_requestPath(std::move(aRequestPath)) 8 | { 9 | CleanUp(); 10 | } 11 | 12 | bool App::ScriptWatcher::Process() 13 | { 14 | if (!CleanUp()) 15 | return false; 16 | 17 | Core::Resolve()->ReloadScripts(); 18 | return true; 19 | } 20 | 21 | bool App::ScriptWatcher::CleanUp() 22 | { 23 | std::error_code error; 24 | return std::filesystem::remove(m_requestPath, error); 25 | } 26 | -------------------------------------------------------------------------------- /src/App/Scripts/ScriptWatcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "App/Foundation/AbstractWatcher.hpp" 4 | 5 | namespace App 6 | { 7 | class ScriptWatcher : public AbstractWatcher 8 | { 9 | public: 10 | ScriptWatcher(std::filesystem::path aRequestPath); 11 | 12 | protected: 13 | bool Process() override; 14 | bool CleanUp(); 15 | 16 | std::filesystem::path m_requestPath; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/App/Shared/ResourcePathRegistry.cpp: -------------------------------------------------------------------------------- 1 | #include "ResourcePathRegistry.hpp" 2 | #include "App/Project.hpp" 3 | #include "Red/SharedStorage.hpp" 4 | 5 | namespace 6 | { 7 | constexpr auto SharedName = Red::CName("ResourcePathRegistryV3" BUILD_SUFFIX); 8 | } 9 | 10 | App::ResourcePathRegistry::ResourcePathRegistry(const std::filesystem::path& aPreloadPath) 11 | { 12 | s_instance = Red::AcquireSharedInstance(); 13 | s_preloadPath = aPreloadPath; 14 | } 15 | 16 | void App::ResourcePathRegistry::OnBootstrap() 17 | { 18 | std::unique_lock lock(s_instance->m_lock); 19 | 20 | if (!s_instance->m_initialized) 21 | { 22 | s_instance->m_initialized = true; 23 | s_instance->m_map.reserve(400000); 24 | 25 | HookAfter(&OnCreatePath); 26 | } 27 | 28 | if (!s_instance->m_preloaded && !s_preloadPath.empty() && std::filesystem::exists(s_preloadPath)) 29 | { 30 | s_instance->m_preloaded = true; 31 | 32 | std::thread([lock = std::move(lock)]() { 33 | LogInfo("[ResourcePathRegistry] Loading metadata..."); 34 | 35 | std::ifstream f(s_preloadPath); 36 | std::string s; 37 | while (std::getline(f, s)) 38 | { 39 | s_instance->m_map[Red::ResourcePath::HashSanitized(s.data())] = std::move(s); 40 | } 41 | 42 | LogInfo("[ResourcePathRegistry] Loaded {} predefined hashes.", s_instance->m_map.size()); 43 | }).detach(); 44 | } 45 | } 46 | 47 | void App::ResourcePathRegistry::OnCreatePath(Red::ResourcePath* aPath, Red::StringView* aPathStr) 48 | { 49 | if (aPathStr) 50 | { 51 | std::scoped_lock _(s_instance->m_lock); 52 | s_instance->m_map[*aPath] = {aPathStr->data, aPathStr->size}; 53 | } 54 | } 55 | 56 | std::string App::ResourcePathRegistry::ResolvePath(Red::ResourcePath aPath) 57 | { 58 | if (!aPath) 59 | return {}; 60 | 61 | std::shared_lock _(s_instance->m_lock); 62 | const auto& it = s_instance->m_map.find(aPath); 63 | 64 | if (it == s_instance->m_map.end()) 65 | return {}; 66 | 67 | return it.value(); 68 | } 69 | 70 | std::string App::ResourcePathRegistry::ResolvePathOrHash(Red::ResourcePath aPath) 71 | { 72 | auto str = ResolvePath(aPath); 73 | 74 | if (str.empty()) 75 | { 76 | str = std::to_string(aPath.hash); 77 | } 78 | 79 | return str; 80 | } 81 | 82 | Red::ResourcePath App::ResourcePathRegistry::RegisterPath(const std::string& aPathStr) 83 | { 84 | if (aPathStr.empty()) 85 | return {}; 86 | 87 | auto path = Red::ResourcePath(aPathStr.data()); 88 | 89 | RegisterPath(path, aPathStr); 90 | 91 | return path; 92 | } 93 | 94 | void App::ResourcePathRegistry::RegisterPath(Red::ResourcePath aPath, const std::string& aPathStr) 95 | { 96 | if (!aPath) 97 | return; 98 | 99 | { 100 | std::shared_lock _(s_instance->m_lock); 101 | if (s_instance->m_map.contains(aPath)) 102 | return; 103 | } 104 | 105 | { 106 | std::scoped_lock _(s_instance->m_lock); 107 | s_instance->m_map[aPath] = aPathStr; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/App/Shared/ResourcePathRegistry.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Hooking/HookingAgent.hpp" 5 | #include "Core/Logging/LoggingAgent.hpp" 6 | #include "Red/ResourcePath.hpp" 7 | 8 | namespace App 9 | { 10 | class ResourcePathRegistry 11 | : public Core::Feature 12 | , public Core::LoggingAgent 13 | , public Core::HookingAgent 14 | { 15 | public: 16 | ResourcePathRegistry(const std::filesystem::path& aPreloadPath = {}); 17 | 18 | [[nodiscard]] std::string ResolvePath(Red::ResourcePath aPath); 19 | [[nodiscard]] std::string ResolvePathOrHash(Red::ResourcePath aPath); 20 | 21 | Red::ResourcePath RegisterPath(const std::string& aPathStr); 22 | void RegisterPath(Red::ResourcePath aPath, const std::string& aPathStr); 23 | 24 | protected: 25 | struct SharedInstance 26 | { 27 | Red::SharedSpinLock m_lock; 28 | Core::Map m_map; 29 | bool m_preloaded{false}; 30 | bool m_initialized{false}; 31 | }; 32 | 33 | void OnBootstrap() override; 34 | static void OnCreatePath(Red::ResourcePath* aPath, Red::StringView* aPathStr); 35 | 36 | inline static SharedInstance* s_instance; 37 | inline static std::filesystem::path s_preloadPath; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /src/App/Tweaks/TweakLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "TweakLoader.hpp" 2 | #include "Red/GameEngine.hpp" 3 | 4 | App::TweakLoader::TweakLoader(std::filesystem::path aTweaksDir) 5 | : m_tweaksDir(std::move(aTweaksDir)) 6 | { 7 | } 8 | 9 | void App::TweakLoader::ReloadTweaks() 10 | { 11 | HookOnceAfter(+[]() { 12 | Red::CallStatic("TweakXL", "Reload", nullptr); 13 | }); 14 | } 15 | 16 | void App::TweakLoader::ReloadTweaks(const Core::Vector& aTargets) 17 | { 18 | ReloadTweaks(); 19 | 20 | // if (aTargets.empty()) 21 | // { 22 | // ReloadTweaks(); 23 | // return; 24 | // } 25 | // 26 | // Core::Vector importables; 27 | // Core::Vector scriptables; 28 | // 29 | // std::error_code error; 30 | // 31 | // for (const auto& target : aTargets) 32 | // { 33 | // if (std::filesystem::exists(m_tweaksDir / target.c_str(), error)) 34 | // { 35 | // importables.emplace_back(target); 36 | // } 37 | // else 38 | // { 39 | // scriptables.emplace_back(target); 40 | // } 41 | // } 42 | } 43 | -------------------------------------------------------------------------------- /src/App/Tweaks/TweakLoader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Core/Foundation/Feature.hpp" 4 | #include "Core/Hooking/HookingAgent.hpp" 5 | #include "Core/Logging/LoggingAgent.hpp" 6 | 7 | namespace App 8 | { 9 | class TweakLoader 10 | : public Core::Feature 11 | , public Core::LoggingAgent 12 | , public Core::HookingAgent 13 | { 14 | public: 15 | TweakLoader(std::filesystem::path aTweaksDir); 16 | 17 | void ReloadTweaks(); 18 | void ReloadTweaks(const Core::Vector& aTargets); 19 | 20 | private: 21 | 22 | std::filesystem::path m_tweaksDir; 23 | std::mutex m_updateLock; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/App/Tweaks/TweakWatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "TweakWatcher.hpp" 2 | #include "App/Tweaks/TweakLoader.hpp" 3 | #include "Core/Facades/Container.hpp" 4 | 5 | App::TweakWatcher::TweakWatcher(std::filesystem::path aRequestPath) 6 | : AbstractWatcher(aRequestPath, std::chrono::milliseconds(100)) 7 | , m_requestPath(std::move(aRequestPath)) 8 | { 9 | CleanUp(); 10 | } 11 | 12 | bool App::TweakWatcher::Process() 13 | { 14 | Core::Vector targets; 15 | 16 | if (!Read(targets)) 17 | return false; 18 | 19 | if (!CleanUp()) 20 | return false; 21 | 22 | Core::Resolve()->ReloadTweaks(targets); 23 | return true; 24 | } 25 | 26 | bool App::TweakWatcher::Read(Core::Vector& aTargets) 27 | { 28 | try 29 | { 30 | std::ifstream in(m_requestPath); 31 | std::string line; 32 | while (std::getline(in, line)) 33 | { 34 | if (!line.empty()) 35 | { 36 | aTargets.emplace_back(line.c_str()); 37 | } 38 | } 39 | return true; 40 | } 41 | catch (const std::exception& ex) 42 | { 43 | LogError(ex.what()); 44 | return false; 45 | } 46 | } 47 | 48 | bool App::TweakWatcher::CleanUp() 49 | { 50 | std::error_code error; 51 | return std::filesystem::remove(m_requestPath, error); 52 | } 53 | -------------------------------------------------------------------------------- /src/App/Tweaks/TweakWatcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "App/Foundation/AbstractWatcher.hpp" 4 | 5 | namespace App 6 | { 7 | class TweakWatcher : public AbstractWatcher 8 | { 9 | public: 10 | TweakWatcher(std::filesystem::path aRequestPath); 11 | 12 | protected: 13 | bool Process() override; 14 | bool Read(Core::Vector& aTargets); 15 | bool CleanUp(); 16 | 17 | std::filesystem::path m_requestPath; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/App/UI/InkInspector.cpp: -------------------------------------------------------------------------------- 1 | #include "InkInspector.hpp" 2 | #include "Core/Facades/Container.hpp" 3 | #include "Core/Facades/Log.hpp" 4 | #include "Red/InkSystem.hpp" 5 | 6 | App::InkInspector::InkInspector() 7 | { 8 | m_collector = Core::Resolve(); 9 | } 10 | 11 | Red::DynArray App::InkInspector::CollectInkLayers() 12 | { 13 | return m_collector->CollectLayers(); 14 | } 15 | 16 | App::InkWidgetCollectionData App::InkInspector::CollectHoveredWidgets() 17 | { 18 | return m_collector->CollectHoveredWidgets(); 19 | } 20 | 21 | App::InkWidgetSpawnData App::InkInspector::GetWidgetSpawnInfo(const Red::Handle& aWidget) 22 | { 23 | return m_collector->GetWidgetSpawnInfo(aWidget); 24 | } 25 | 26 | void App::InkInspector::EnablePointerInput() 27 | { 28 | m_collector->TogglePointerInput(true); 29 | } 30 | 31 | void App::InkInspector::DisablePointerInput() 32 | { 33 | m_collector->TogglePointerInput(false); 34 | } 35 | 36 | void App::InkInspector::TogglePointerInput() 37 | { 38 | m_collector->TogglePointerInput(); 39 | } 40 | 41 | void App::InkInspector::EnsurePointerInput() 42 | { 43 | m_collector->EnsurePointerInput(); 44 | } 45 | 46 | Red::Vector2 App::InkInspector::GetPointerScreenPosition() 47 | { 48 | return Red::InkSystem::Get()->pointerScreenPosition; 49 | } 50 | 51 | Red::inkRectangle App::InkInspector::GetWidgetDrawRect(const Red::Handle& aWidget) 52 | { 53 | Red::inkRectangle drawRect{}; 54 | 55 | if (const auto drawContext = Red::inkDrawContext::Resolve(aWidget)) 56 | { 57 | Raw::inkDrawArea::GetBoundingRect(drawContext->drawArea, drawRect, true); 58 | } 59 | 60 | return drawRect; 61 | } 62 | -------------------------------------------------------------------------------- /src/App/UI/InkInspector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "App/UI/InkWidgetCollector.hpp" 4 | 5 | namespace App 6 | { 7 | class InkInspector : public Red::IScriptable 8 | { 9 | public: 10 | InkInspector(); 11 | 12 | Red::DynArray CollectInkLayers(); 13 | InkWidgetCollectionData CollectHoveredWidgets(); 14 | InkWidgetSpawnData GetWidgetSpawnInfo(const Red::Handle& aWidget); 15 | void EnablePointerInput(); 16 | void DisablePointerInput(); 17 | void TogglePointerInput(); 18 | void EnsurePointerInput(); 19 | Red::Vector2 GetPointerScreenPosition(); 20 | Red::inkRectangle GetWidgetDrawRect(const Red::Handle& aWidget); 21 | 22 | static Red::Handle Get() 23 | { 24 | return Red::MakeHandle(); 25 | } 26 | 27 | private: 28 | Core::SharedPtr m_collector; 29 | 30 | RTTI_IMPL_TYPEINFO(App::InkInspector); 31 | RTTI_IMPL_ALLOCATOR(); 32 | }; 33 | } 34 | 35 | RTTI_DEFINE_CLASS(App::InkInspector, { 36 | RTTI_METHOD(CollectInkLayers); 37 | RTTI_METHOD(CollectHoveredWidgets); 38 | RTTI_METHOD(GetWidgetSpawnInfo); 39 | RTTI_METHOD(EnablePointerInput); 40 | RTTI_METHOD(DisablePointerInput); 41 | RTTI_METHOD(TogglePointerInput); 42 | RTTI_METHOD(EnsurePointerInput); 43 | RTTI_METHOD(GetPointerScreenPosition); 44 | RTTI_METHOD(GetWidgetDrawRect); 45 | }); 46 | 47 | RTTI_EXPAND_CLASS(Red::ScriptGameInstance, { 48 | RTTI_METHOD_FQN(App::InkInspector::Get, "GetInkInspector"); 49 | }); 50 | -------------------------------------------------------------------------------- /src/App/World/PhysicsTraceResult.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace App 4 | { 5 | struct PhysicsTraceResultObject 6 | { 7 | Red::WeakHandle nodeInstance; 8 | Red::WeakHandle nodeDefinition; 9 | Red::WeakHandle entity; 10 | uint64_t hash; 11 | bool resolved; 12 | }; 13 | } 14 | 15 | RTTI_DEFINE_CLASS(App::PhysicsTraceResultObject, { 16 | RTTI_PROPERTY(nodeInstance); 17 | RTTI_PROPERTY(nodeDefinition); 18 | RTTI_PROPERTY(entity); 19 | RTTI_PROPERTY(resolved); 20 | RTTI_PROPERTY(hash); 21 | }); 22 | -------------------------------------------------------------------------------- /src/Red/AsyncFileAPI.cpp: -------------------------------------------------------------------------------- 1 | #include "AsyncFileAPI.hpp" 2 | 3 | Red::AsyncFileAPI* Red::AsyncFileAPI::Get() 4 | { 5 | return Raw::AsyncFileAPI::Instance; 6 | } 7 | 8 | Red::AsyncFileHandleCache* Red::AsyncFileHandleCache::Get() 9 | { 10 | return Red::AsyncFileAPI::Get()->fileHandleCache; 11 | } 12 | 13 | bool Red::AsyncFileHandleCache::CloseSystemHandle(uint32_t aHandle) 14 | { 15 | auto& file = files[aHandle]; 16 | auto& path = paths[aHandle]; 17 | 18 | if (!file.handle || *file.handle == INVALID_HANDLE_VALUE || !path[0]) 19 | return false; 20 | 21 | auto success = CloseHandle(*file.handle); 22 | *file.handle = INVALID_HANDLE_VALUE; 23 | 24 | return success; 25 | } 26 | 27 | bool Red::AsyncFileHandleCache::ReopenSystemHandle(uint32_t aHandle) 28 | { 29 | auto& file = files[aHandle]; 30 | auto& path = paths[aHandle]; 31 | 32 | if (!file.handle || *file.handle != INVALID_HANDLE_VALUE || !path[0]) 33 | return false; 34 | 35 | auto systemFlags = (0xF * (file.flags & 0x4)) | (((file.flags & 0x8) == 0) ? 0x21 : 0xA1); 36 | auto success = Raw::AsyncFileAPI::OpenSystemHandle(file.handle, path, systemFlags); 37 | 38 | return success; 39 | } 40 | 41 | bool Red::AsyncFileHandleCache::UpdateSystemHandle(uint32_t aHandle, uint32_t aNewHandle) 42 | { 43 | auto& file = files[aHandle]; 44 | auto& path = paths[aHandle]; 45 | 46 | if (!file.handle || *file.handle != INVALID_HANDLE_VALUE || !path[0]) 47 | return false; 48 | 49 | auto& newFile = files[aNewHandle]; 50 | auto& newPath = paths[aNewHandle]; 51 | 52 | if (!newFile.handle || *newFile.handle == INVALID_HANDLE_VALUE || !newPath[0] || strcmp(path, newPath) != 0) 53 | return false; 54 | 55 | auto allocator = Red::Memory::EngineAllocator::Get(); 56 | allocator->Free(file.handle); 57 | 58 | file.handle = newFile.handle; 59 | 60 | files[aNewHandle] = {}; 61 | paths[aNewHandle][0] = 0; 62 | 63 | return true; 64 | } 65 | -------------------------------------------------------------------------------- /src/Red/AsyncFileAPI.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | struct AsyncFileHandleCache 6 | { 7 | static constexpr auto MaxFiles = 65536; 8 | static constexpr auto MaxPathLength = 512; 9 | 10 | using FilePath = char[MaxPathLength]; 11 | 12 | struct FileState 13 | { 14 | HANDLE* handle; // 00 15 | int32_t refCount; // 08 16 | uint8_t flags; // 0C 17 | }; 18 | 19 | virtual ~AsyncFileHandleCache() = 0; 20 | virtual uint32_t OpenFile(const char* aPath, uint8_t aFlags) = 0; 21 | virtual void IncFileRef(uint32_t aHandle) = 0; 22 | virtual void CloseFile(uint32_t aHandle) = 0; 23 | 24 | bool CloseSystemHandle(uint32_t aHandle); 25 | bool ReopenSystemHandle(uint32_t aHandle); 26 | bool UpdateSystemHandle(uint32_t aHandle, uint32_t aNewHandle); 27 | 28 | static AsyncFileHandleCache* Get(); 29 | 30 | uint64_t unk08; // 08 31 | uint64_t unk10; // 10 32 | uint64_t unk18; // 18 33 | uint64_t unk20; // 20 34 | uint64_t unk28; // 28 35 | uint64_t unk30; // 30 36 | uint64_t unk38; // 38 37 | uint32_t unk40; // 40 38 | uint32_t unk44; // 44 39 | uint64_t unk48; // 48 40 | uint8_t unk50; // 50 41 | FileState files[MaxFiles]; // 58 42 | FilePath paths[MaxFiles]; // 100058 43 | uint64_t unk2100058; // 2100058 44 | }; 45 | RED4EXT_ASSERT_OFFSET(AsyncFileHandleCache, files, 0x58); 46 | RED4EXT_ASSERT_OFFSET(AsyncFileHandleCache, paths, 0x100058); 47 | 48 | struct AsyncFileAPI 49 | { 50 | static AsyncFileAPI* Get(); 51 | 52 | uint8_t unk00[0xF0]; // 00 53 | AsyncFileHandleCache* fileHandleCache; // F0 54 | }; 55 | RED4EXT_ASSERT_OFFSET(AsyncFileAPI, fileHandleCache, 0xF0); 56 | } 57 | 58 | namespace Raw::AsyncFileAPI 59 | { 60 | constexpr auto Instance = Core::RawPtr< 61 | /* addr = */ Red::AddressLib::AsyncFileAPI_Instance, 62 | /* type = */ Red::AsyncFileAPI>(); 63 | 64 | constexpr auto OpenSystemHandle = Core::RawFunc< 65 | /* addr = */ Red::AddressLib::AsyncFileAPI_OpenSystemHandle, 66 | /* type = */ bool (*)(HANDLE* aHandle, const char* aPath, uint8_t aFlags)>{}; 67 | 68 | constexpr auto CloseAndFreeSystemHandle = Core::RawFunc< 69 | /* addr = */ Red::AddressLib::AsyncFileAPI_CloseAndFreeSystemHandle, 70 | /* type = */ void (*)(HANDLE* aHandle)>{}; 71 | } 72 | -------------------------------------------------------------------------------- /src/Red/CameraSystem.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | enum FrustumResult 6 | { 7 | Undefined = 0, 8 | Outside = 1, 9 | Intersecting = 2, 10 | Inside = 3, 11 | }; 12 | 13 | struct Frustum 14 | { 15 | static constexpr auto NumberOfPlanes = 6; 16 | 17 | inline FrustumResult Test(const Vector4& aPoint) 18 | { 19 | for (auto i = 0; i < NumberOfPlanes; ++i) 20 | { 21 | auto v3 = _mm_mul_ps(*reinterpret_cast(&aPoint), planes[i]); 22 | auto v4 = _mm_hadd_ps(v3, v3); 23 | if (_mm_hadd_ps(v4, v4).m128_f32[0] < 0.0) 24 | { 25 | return FrustumResult::Outside; 26 | } 27 | } 28 | 29 | return FrustumResult::Inside; 30 | } 31 | 32 | inline FrustumResult Test(const Box& aBox) 33 | { 34 | auto result = FrustumResult::Inside; 35 | 36 | const auto& boxMin = *reinterpret_cast(&aBox.Min); 37 | const auto& boxMax = *reinterpret_cast(&aBox.Max); 38 | 39 | for (auto i = 0; i < NumberOfPlanes; ++i) 40 | { 41 | auto v24 = _mm_mul_ps(_mm_or_ps(_mm_and_ps(masks[i], boxMax), _mm_andnot_ps(masks[i], boxMin)), planes[i]); 42 | auto v25 = _mm_mul_ps(_mm_or_ps(_mm_andnot_ps(masks[i], boxMax), _mm_and_ps(masks[i], boxMin)), planes[i]); 43 | auto v26 = _mm_hadd_ps(v24, v24); 44 | auto v27 = _mm_hadd_ps(v25, v25); 45 | auto v28 = _mm_hadd_ps(v27, v27); 46 | 47 | if (_mm_movemask_ps(_mm_xor_ps(_mm_hadd_ps(v26, v26), v28))) 48 | { 49 | result = FrustumResult::Intersecting; 50 | } 51 | else if (_mm_movemask_ps(v28)) 52 | { 53 | result = FrustumResult::Outside; 54 | break; 55 | } 56 | } 57 | 58 | return result; 59 | } 60 | 61 | __m128 planes[NumberOfPlanes]{}; 62 | __m128 masks[NumberOfPlanes]{}; 63 | }; 64 | 65 | struct Camera 66 | { 67 | }; 68 | } 69 | 70 | namespace Raw::Camera 71 | { 72 | constexpr auto ProjectPoint = Core::RawFunc< 73 | /* addr = */ Red::AddressLib::Camera_ProjectPoint, 74 | /* type = */ void* (*)(Red::Camera* aCamera, Red::Vector4& aOut, const Red::Vector3& aPoint)>(); 75 | } 76 | 77 | namespace Raw::CameraSystem 78 | { 79 | using Camera = Core::OffsetPtr<0x60, Red::Camera>; 80 | 81 | constexpr auto GetCameraPosition = Core::RawVFunc< 82 | /* addr = */ 0x218, 83 | /* type = */ void* (Red::gameICameraSystem::*)(Red::Vector3& aOut)>(); 84 | 85 | constexpr auto GetCameraFrustum = Core::RawVFunc< 86 | /* addr = */ 0x250, 87 | /* type = */ void (Red::gameICameraSystem::*)(Red::Frustum& aOut)>(); 88 | 89 | constexpr auto GetCameraForward = Core::RawVFunc< 90 | /* addr = */ 0x290, 91 | /* type = */ void* (Red::gameICameraSystem::*)(Red::Vector4& aOut)>(); 92 | } 93 | -------------------------------------------------------------------------------- /src/Red/Common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | template 6 | struct Range 7 | { 8 | constexpr operator bool() const noexcept 9 | { 10 | return ptr != end; 11 | } 12 | 13 | constexpr operator T*() const noexcept 14 | { 15 | return ptr; 16 | } 17 | 18 | constexpr operator T&() const noexcept 19 | { 20 | return *ptr; 21 | } 22 | 23 | [[nodiscard]] auto GetSize() const 24 | { 25 | return end - ptr; 26 | } 27 | 28 | T* ptr; // 00 29 | T* end; // 08 30 | }; 31 | RED4EXT_ASSERT_SIZE(Range, 0x10); 32 | RED4EXT_ASSERT_OFFSET(Range, ptr, 0x0); 33 | RED4EXT_ASSERT_OFFSET(Range, end, 0x8); 34 | } 35 | -------------------------------------------------------------------------------- /src/Red/Debug.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Raw::EntityID 4 | { 5 | constexpr auto ToStringDEBUG = Core::RawFunc< 6 | /* addr = */ Red::AddressLib::EntityID_ToStringDEBUG, 7 | /* type = */ Red::CString* (*)(const Red::EntityID& aEntityID, Red::CString& aOut)>(); 8 | } 9 | -------------------------------------------------------------------------------- /src/Red/EngineConfig.cpp: -------------------------------------------------------------------------------- 1 | #include "EngineConfig.hpp" 2 | 3 | Red::EngineConfigVar* Red::EngineConfigStore::GetVar(const RED4ext::StringView& aGroupPath, 4 | const RED4ext::StringView& aOption) 5 | { 6 | return Raw::EngineConfigStore::GetVar(this, aGroupPath, aOption); 7 | } 8 | 9 | Red::EngineConfigVar* Red::EngineConfig::GetVar(const RED4ext::StringView& aGroupPath, 10 | const RED4ext::StringView& aVarName) 11 | { 12 | return Raw::EngineConfigStore::GetVar(store, aGroupPath, aVarName); 13 | } 14 | 15 | Red::EngineConfig* Red::EngineConfig::Get() 16 | { 17 | return Raw::EngineConfig::GetInstance(); 18 | } 19 | -------------------------------------------------------------------------------- /src/Red/FileSystem.cpp: -------------------------------------------------------------------------------- 1 | #include "FileSystem.hpp" 2 | 3 | Red::FileSystem* Red::FileSystem::Get() 4 | { 5 | return Raw::FileSystem::Instance; 6 | } 7 | 8 | Red::FileHandle::~FileHandle() 9 | { 10 | Raw::FileSystem::Close(this); 11 | } 12 | -------------------------------------------------------------------------------- /src/Red/FileSystem.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | struct FileHandle 6 | { 7 | ~FileHandle(); 8 | 9 | operator bool() const noexcept 10 | { 11 | return ptr; 12 | } 13 | 14 | uint64_t ptr; // 00 15 | }; 16 | 17 | struct FileSystem 18 | { 19 | virtual ~FileSystem() = 0; // 00 20 | virtual FileHandle Open(CString aPath, uint8_t aFlags) = 0; // 08 21 | 22 | static FileSystem* Get(); 23 | }; 24 | } 25 | 26 | namespace Raw::FileSystem 27 | { 28 | constexpr auto Instance = Core::RawPtr< 29 | /* addr = */ Red::AddressLib::FileSystem_Instance, 30 | /* type = */ Red::FileSystem*>(); 31 | 32 | constexpr auto Close = Core::RawFunc< 33 | /* addr = */ Red::AddressLib::FileSystem_Close, 34 | /* type = */ void (*)(Red::FileHandle* aHandle)>(); 35 | } 36 | -------------------------------------------------------------------------------- /src/Red/GameEngine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Raw::CBaseEngine 4 | { 5 | constexpr auto LoadScripts = Core::RawFunc< 6 | /* addr = */ Red::AddressLib::CBaseEngine_LoadScripts, 7 | /* type = */ bool (*)(Red::CBaseEngine& aEngine, Red::CString& aPath, uint64_t aTimestamp, uint64_t)>(); 8 | 9 | constexpr auto MainLoopTick = Core::RawFunc< 10 | /* addr = */ Red::AddressLib::CBaseEngine_MainLoopTick, 11 | /* type = */ bool (*)(Red::CBaseEngine& aEngine, float aDelta)>(); 12 | } 13 | -------------------------------------------------------------------------------- /src/Red/ISerializable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Raw::ISerializable 4 | { 5 | constexpr auto CreateHandle = Core::RawFunc< 6 | /* addr = */ RED4ext::Detail::AddressHashes::Handle_ctor, 7 | /* type = */ void* (*)(Red::Handle& aHandle, Red::ISerializable* aObject)>(); 8 | } 9 | -------------------------------------------------------------------------------- /src/Red/InkContext.cpp: -------------------------------------------------------------------------------- 1 | #include "InkContext.hpp" 2 | #include "Red/InkWidget.hpp" 3 | 4 | Red::inkWidgetContext::inkWidgetContext(Red::inkWidgetContext& aOther) 5 | { 6 | Raw::inkWidgetContext::Clone(*this, aOther); 7 | } 8 | 9 | Red::inkWidgetContext::inkWidgetContext(Red::inkWidgetContext& aOther, Red::inkPointerHandler* aPointerHandler) 10 | { 11 | Raw::inkWidgetContext::Clone(*this, aOther); 12 | pointerHandler = aPointerHandler; 13 | } 14 | 15 | void Red::inkWidgetContext::AddInteractiveWidget(const Red::WeakHandle& aWidget, 16 | bool aVisible, bool aAffectsLayout) 17 | { 18 | Raw::inkWidgetContext::AddWidget(*this, aWidget, aVisible, aAffectsLayout); 19 | } 20 | 21 | const Red::inkDrawContext* Red::inkDrawContext::Resolve(const RED4ext::Handle& aWidget) 22 | { 23 | Red::Handle parentWidget; 24 | 25 | { 26 | std::shared_lock _(aWidget->parentLock); 27 | parentWidget = aWidget->parentWidget.Lock(); 28 | } 29 | 30 | if (parentWidget) 31 | { 32 | const auto& drawContexts = Raw::inkWidget::DrawContexts::Ref(parentWidget); 33 | for (const auto& drawContext : drawContexts) 34 | { 35 | if (drawContext.widget.instance == aWidget) 36 | { 37 | return &drawContext; 38 | } 39 | } 40 | } 41 | 42 | return nullptr; 43 | } 44 | -------------------------------------------------------------------------------- /src/Red/InkContext.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Red/InkInput.hpp" 4 | 5 | namespace Red 6 | { 7 | struct inkDrawContext 8 | { 9 | static const inkDrawContext* Resolve(const Handle& aWidget); 10 | 11 | WeakHandle widget; // 00 12 | inkDrawArea drawArea; // 10 13 | }; 14 | RED4EXT_ASSERT_SIZE(inkDrawContext, 0x60); 15 | RED4EXT_ASSERT_OFFSET(inkDrawContext, drawArea, 0x10); 16 | 17 | struct inkWidgetContext 18 | { 19 | inkWidgetContext() = default; 20 | inkWidgetContext(inkWidgetContext& aOther); 21 | inkWidgetContext(inkWidgetContext& aOther, inkPointerHandler* aPointerHandler); 22 | 23 | void AddInteractiveWidget(const Red::WeakHandle& aWidget, 24 | bool aVisible = true, bool aAffectsLayout = false); 25 | 26 | uint64_t unk00; // 00 27 | inkPointerHandler* pointerHandler; // 08 28 | uint8_t unk10[0x70]; // 10 29 | }; 30 | RED4EXT_ASSERT_SIZE(inkWidgetContext, 0x80); 31 | RED4EXT_ASSERT_OFFSET(inkWidgetContext, pointerHandler, 0x08); 32 | } 33 | 34 | namespace Raw::inkDrawArea 35 | { 36 | constexpr auto GetBoundingRect = Core::RawFunc< 37 | /* addr = */ Red::AddressLib::InkDrawArea_GetBoundingRect, 38 | /* type = */ void (*)(const Red::inkDrawArea& aArea, Red::inkRectangle& aOut, bool aGlobal)>(); 39 | } 40 | 41 | namespace Raw::inkWidgetContext 42 | { 43 | constexpr auto Clone = Core::RawFunc< 44 | /* addr = */ Red::AddressLib::InkWidgetContext_Clone, 45 | /* type = */ void (*)(Red::inkWidgetContext& aContext, const Red::inkWidgetContext& aFrom)>(); 46 | 47 | constexpr auto AddWidget = Core::RawFunc< 48 | /* addr = */ Red::AddressLib::InkWidgetContext_AddWidget, 49 | /* type = */ void (*)(Red::inkWidgetContext& aContext, const Red::WeakHandle& aWidget, 50 | bool aVisible, bool aAffectsLayoutWhenHidden)>(); 51 | } 52 | -------------------------------------------------------------------------------- /src/Red/InkInput.cpp: -------------------------------------------------------------------------------- 1 | #include "InkInput.hpp" 2 | 3 | Red::SharedPtr Red::inkPointerHandler::Create() 4 | { 5 | Red::SharedPtr handler; 6 | Raw::inkPointerHandler::Create(handler); 7 | return handler; 8 | } 9 | 10 | void Red::inkPointerHandler::Reset(int32_t* aArea) 11 | { 12 | Raw::inkPointerHandler::Reset(this, aArea); 13 | } 14 | 15 | void Red::inkPointerHandler::Override(const Red::SharedPtr& aOverride, int32_t aIndex) 16 | { 17 | Raw::inkPointerHandler::Override(this, aOverride, aIndex); 18 | } 19 | -------------------------------------------------------------------------------- /src/Red/InkInput.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | struct inkPointerHandler 6 | { 7 | using AllocatorType = Memory::Ink_HitTestAllocator; 8 | 9 | virtual ~inkPointerHandler() = 0; // 00 10 | virtual void CollectWidgets(DynArray>& aOut, Vector2& aPointerScreenPosition, 11 | Vector2& aPointerWindowPosition, Vector2& aPointerSize) = 0; // 08 12 | virtual WeakHandle GetActiveWindow() = 0; // 10 13 | 14 | void Reset(int32_t* aArea); 15 | void Override(const Red::SharedPtr& aOverride, int32_t aIndex); 16 | 17 | static Red::SharedPtr Create(); 18 | }; 19 | } 20 | 21 | namespace Raw::inkPointerHandler 22 | { 23 | constexpr auto Create = Core::RawFunc< 24 | /* addr = */ Red::AddressLib::inkPointerHandler_Create, 25 | /* type = */ void (*)(Red::SharedPtr& aOut)>(); 26 | 27 | constexpr auto Reset = Core::RawFunc< 28 | /* addr = */ Red::AddressLib::inkPointerHandler_Reset, 29 | /* type = */ void (*)(Red::inkPointerHandler* aHandler, int32_t* aArea)>(); 30 | 31 | constexpr auto Override = Core::RawFunc< 32 | /* addr = */ Red::AddressLib::inkPointerHandler_Override, 33 | /* type = */ void (*)(Red::inkPointerHandler* aHandler, 34 | const Red::SharedPtr& aOverride, 35 | int32_t aIndex)>(); 36 | } 37 | -------------------------------------------------------------------------------- /src/Red/InkLayer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Red/InkWindow.hpp" 4 | 5 | namespace Raw::inkLayer 6 | { 7 | constexpr auto GetPointerHandler = Core::RawVFunc< 8 | /* addr = */ 0x128, 9 | /* type = */ Red::inkPointerHandler* (Red::inkLayer::*)()>(); 10 | 11 | constexpr auto AttachLibraryInstance = Core::RawFunc< 12 | /* addr = */ Red::AddressLib::InkLayer_AttachLibraryInstance, 13 | /* type = */ void (*)(Red::inkLayer* aLayer, 14 | const Red::Handle& aInstance, 15 | const Red::Handle& aLibrary)>(); 16 | } 17 | -------------------------------------------------------------------------------- /src/Red/InkLibrary.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Red/InkSpawner.hpp" 4 | 5 | namespace Raw::InkWidgetLibrary 6 | { 7 | constexpr auto AsyncSpawnFromExternal = Core::RawFunc< 8 | /* addr = */ Red::AddressLib::InkWidgetLibrary_AsyncSpawnFromExternal, 9 | /* type = */ bool (*)( 10 | Red::inkWidgetLibraryResource* aLibrary, 11 | Red::InkSpawningInfo& aSpawningInfo, 12 | Red::ResourcePath aExternalPath, 13 | Red::CName aItemName, 14 | uint8_t aParam)>(); 15 | 16 | constexpr auto AsyncSpawnFromLocal = Core::RawFunc< 17 | /* addr = */ Red::AddressLib::InkWidgetLibrary_AsyncSpawnFromLocal, 18 | /* type = */ bool (*)( 19 | Red::inkWidgetLibraryResource* aLibrary, 20 | Red::InkSpawningInfo& aSpawningInfo, 21 | Red::CName aItemName, 22 | uint8_t aParam)>(); 23 | 24 | constexpr auto SpawnFromExternal = Core::RawFunc< 25 | /* addr = */ Red::AddressLib::InkWidgetLibrary_SpawnFromExternal, 26 | /* type = */ uintptr_t (*)( 27 | Red::inkWidgetLibraryResource* aLibrary, 28 | Red::Handle& aInstance, 29 | Red::ResourcePath aExternalPath, 30 | Red::CName aItemName)>(); 31 | 32 | constexpr auto SpawnFromLocal = Core::RawFunc< 33 | /* addr = */ Red::AddressLib::InkWidgetLibrary_SpawnFromLocal, 34 | /* type = */ uintptr_t (*)( 35 | Red::inkWidgetLibraryResource* aLibrary, 36 | Red::Handle& aInstance, 37 | Red::CName aItemName)>(); 38 | 39 | constexpr auto SpawnRoot = Core::RawFunc< 40 | /* addr = */ Red::AddressLib::InkWidgetLibrary_SpawnRoot, 41 | /* type = */ uintptr_t (*)( 42 | Red::inkWidgetLibraryResource* aLibrary, 43 | Red::Handle& aInstance)>(); 44 | } 45 | -------------------------------------------------------------------------------- /src/Red/InkSpawner.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | struct InkSpawningRequest 6 | { 7 | void* unk00; // 00 8 | Handle unk08; // 08 - inkAsyncSpawnRequest 9 | uint8_t unk18[0x30]; // 18 10 | CName itemName; // 48 11 | WeakHandle parentWidget; // 50 12 | Handle rootWidget; // 60 13 | Handle gameController; // 70 14 | Handle library; // 80 15 | Handle instance; // 90 16 | ResourcePath externalLibrary; // A0 17 | Handle unkA8; // A8 - inkLoadingLayer 18 | }; 19 | RED4EXT_ASSERT_OFFSET(InkSpawningRequest, itemName, 0x48); 20 | RED4EXT_ASSERT_OFFSET(InkSpawningRequest, rootWidget, 0x60); 21 | 22 | struct InkSpawningContext 23 | { 24 | virtual void sub_00() = 0; 25 | virtual void sub_08() = 0; 26 | virtual void sub_10() = 0; 27 | virtual void sub_18() = 0; 28 | virtual void sub_20() = 0; 29 | virtual void sub_28() = 0; 30 | 31 | SharedPtr unk08; // 08 32 | SharedPtr request; // 18 33 | }; 34 | RED4EXT_ASSERT_SIZE(InkSpawningContext, 0x28); 35 | RED4EXT_ASSERT_OFFSET(InkSpawningContext, request, 0x18); 36 | 37 | struct InkSpawningInfo 38 | { 39 | uint8_t unk00[0x38]; // 00 40 | InkSpawningContext* context; // 38 41 | }; 42 | RED4EXT_ASSERT_SIZE(InkSpawningInfo, 0x40); 43 | RED4EXT_ASSERT_OFFSET(InkSpawningInfo, context, 0x38); 44 | } 45 | 46 | namespace Raw::InkSpawner 47 | { 48 | constexpr auto FinishAsyncSpawn = Core::RawFunc< 49 | /* addr = */ Red::AddressLib::InkSpawner_FinishAsyncSpawn, 50 | /* type = */ bool (*)( 51 | Red::InkSpawningContext& aContext, 52 | Red::Handle& aInstance)>(); 53 | } 54 | -------------------------------------------------------------------------------- /src/Red/InkSystem.cpp: -------------------------------------------------------------------------------- 1 | #include "InkSystem.hpp" 2 | 3 | Red::InkSystem* Red::InkSystem::Get(bool aCompatMode) 4 | { 5 | if (aCompatMode) 6 | return Raw::inkSystem::Instance_Pre212a; 7 | 8 | return Raw::inkSystem::Instance; 9 | } 10 | 11 | Red::InkLayerManager* Red::InkSystem::GetLayerManager() const 12 | { 13 | return layerManagers[0].instance; 14 | } 15 | 16 | const Red::DynArray>& Red::InkSystem::GetLayers() const 17 | { 18 | return layerManagers[0]->layers; 19 | } 20 | 21 | RED4ext::Handle Red::InkSystem::GetLayer(RED4ext::CName aLayerName) const 22 | { 23 | auto it = std::find_if(layerManagers[0]->layers.Begin(), layerManagers[0]->layers.End(), 24 | [aLayerName](const Handle& aLayer) -> bool 25 | { 26 | return aLayer->GetNativeType()->GetName() == aLayerName; 27 | }); 28 | 29 | if (it == layerManagers[0]->layers.End()) 30 | return {}; 31 | 32 | return *it; 33 | } 34 | 35 | const Red::WeakHandle& Red::InkSystem::GetSystemRequestsHandler() const 36 | { 37 | return requestsHandler; 38 | } 39 | 40 | Red::inkISystemRequestsHandler* Red::InkSystem::GetSystemRequestsHandlerPtr() const 41 | { 42 | return requestsHandler.instance; 43 | } 44 | -------------------------------------------------------------------------------- /src/Red/InkSystem.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Red/Input.hpp" 4 | 5 | namespace Red 6 | { 7 | struct InkLayerManager 8 | { 9 | uint8_t unk00[0x38]; // 00 10 | DynArray> layers; // 38 11 | }; 12 | RED4EXT_ASSERT_OFFSET(InkLayerManager, layers, 0x38); 13 | 14 | struct InkSystem 15 | { 16 | static InkSystem* Get(bool aCompatMode = false); 17 | 18 | [[nodiscard]] InkLayerManager* GetLayerManager() const; 19 | [[nodiscard]] const DynArray>& GetLayers() const; 20 | [[nodiscard]] Handle GetLayer(CName aLayerName) const; 21 | 22 | template 23 | requires std::is_base_of_v 24 | [[nodiscard]] Handle GetLayer() const 25 | { 26 | auto layer = GetLayer(T::NAME); 27 | 28 | if (!layer) 29 | return {}; 30 | 31 | return *reinterpret_cast*>(&layer); 32 | } 33 | 34 | [[nodiscard]] const WeakHandle& GetSystemRequestsHandler() const; 35 | [[nodiscard]] inkISystemRequestsHandler* GetSystemRequestsHandlerPtr() const; 36 | 37 | uint8_t unk00[0x2C0]; // 000 38 | Vector2 pointerScreenPosition; // 2C0 39 | Vector2 pointerWindowPosition; // 2C8 40 | uint8_t unk2D0[0x2E8 - 0x2D0]; // 2FA 41 | WeakHandle inputWidget; // 2E8 42 | KeyboardState keyboardState; // 2F8 43 | uint8_t unk2FA[0x328 - 0x2FA]; // 2FA 44 | DynArray> hoverWidgets; // 328 45 | uint8_t unk330[0x368 - 0x338]; // 330 46 | WeakHandle requestsHandler; // 368 47 | DynArray> layerManagers; // 378 48 | WeakPtr activeManager; // 388 49 | WeakHandle activeLayer; // 398 50 | bool isPreGame; // 3A8 51 | }; 52 | RED4EXT_ASSERT_OFFSET(InkSystem, pointerScreenPosition, 0x2C0); 53 | RED4EXT_ASSERT_OFFSET(InkSystem, inputWidget, 0x2E8); 54 | RED4EXT_ASSERT_OFFSET(InkSystem, keyboardState, 0x2F8); 55 | RED4EXT_ASSERT_OFFSET(InkSystem, hoverWidgets, 0x328); 56 | RED4EXT_ASSERT_OFFSET(InkSystem, requestsHandler, 0x368); 57 | RED4EXT_ASSERT_OFFSET(InkSystem, layerManagers, 0x378); 58 | RED4EXT_ASSERT_OFFSET(InkSystem, activeManager, 0x388); 59 | RED4EXT_ASSERT_OFFSET(InkSystem, activeLayer, 0x398); 60 | } 61 | 62 | namespace Raw::inkSystem 63 | { 64 | constexpr auto Instance = Core::RawPtr< 65 | /* addr = */ Red::AddressLib::InkSystem_Instance, 66 | /* type = */ Red::InkSystem*>(); 67 | 68 | constexpr auto Instance_Pre212a = Core::RawPtr< 69 | /* addr = */ Red::AddressLib::InkSystem_Instance_Pre212a, 70 | /* type = */ Red::InkSystem*>(); 71 | } 72 | -------------------------------------------------------------------------------- /src/Red/InkWidget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Red/InkContext.hpp" 4 | 5 | namespace Raw::inkWidget 6 | { 7 | using DrawContexts = Core::OffsetPtr<0x1D0, Red::DynArray*>; 8 | 9 | constexpr auto Draw = Core::RawFunc< 10 | /* addr = */ Red::AddressLib::InkWidget_Draw, 11 | /* type = */ void (*)(Red::inkWidget* aWidget, Red::inkWidgetContext& aContext)>(); 12 | 13 | constexpr auto IsVisible = Core::RawFunc< 14 | /* addr = */ Red::AddressLib::InkWidget_IsVisible, 15 | /* type = */ bool (*)(Red::inkWidget*)>(); 16 | 17 | constexpr auto SpawnFromLibrary = Core::RawFunc< 18 | /* addr = */ Red::AddressLib::InkWidget_SpawnFromLibrary, 19 | /* type = */ void (*)(Red::Handle& aOut, 20 | const Red::Handle& aParent, 21 | const Red::Handle& aLibrary, 22 | Red::CName aItemName)>(); 23 | } 24 | -------------------------------------------------------------------------------- /src/Red/InkWindow.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Red/InkInput.hpp" 4 | 5 | namespace Raw::inkWindow 6 | { 7 | using PointerHandler = Core::OffsetPtr<0x258, Red::SharedPtr>; 8 | 9 | constexpr auto Construct = Core::RawFunc< 10 | /* addr = */ Red::AddressLib::InkWindow_ctor, 11 | /* type = */ void (*)(Red::inkWindow* aWindow)>(); 12 | 13 | constexpr auto Destruct = Core::RawFunc< 14 | /* addr = */ Red::AddressLib::InkWindow_ctor, 15 | /* type = */ void (*)(Red::inkWindow* aWindow)>(); 16 | 17 | constexpr auto TogglePointerInput = Core::RawFunc< 18 | /* addr = */ Red::AddressLib::InkWindow_TogglePointerInput, 19 | /* type = */ void (*)(Red::inkWindow* aWindow, bool aEnabled)>(); 20 | 21 | constexpr auto IsPointerVisible = Core::RawFunc< 22 | /* addr = */ Red::AddressLib::InkWindow_IsPointerVisible, 23 | /* type = */ bool (*)(Red::inkWindow* aWindow)>(); 24 | 25 | constexpr auto SetPointerVisibility = Core::RawFunc< 26 | /* addr = */ Red::AddressLib::InkWindow_SetPointerVisibility, 27 | /* type = */ void (*)(Red::inkWindow* aWindow, bool aVisible)>(); 28 | 29 | constexpr auto SetPointerWidget = Core::RawFunc< 30 | /* addr = */ Red::AddressLib::InkWindow_SetPointerWidget, 31 | /* type = */ void (*)(Red::inkWindow* aWindow, const Red::Handle& aCursor)>(); 32 | } 33 | -------------------------------------------------------------------------------- /src/Red/Input.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | struct KeyboardState 6 | { 7 | uint16_t shiftLeft : 1; 8 | uint16_t shiftRight : 1; 9 | uint16_t controlLeft : 1; 10 | uint16_t controlRight : 1; 11 | uint16_t altLeft : 1; 12 | uint16_t altRight : 1; 13 | uint16_t b10 : 10; 14 | }; 15 | RED4EXT_ASSERT_SIZE(KeyboardState, 0x2); 16 | 17 | struct RawInputData 18 | { 19 | EInputKey key; // 00 20 | EInputAction action; // 04 21 | float value; // 08 22 | uint32_t mouseX; // 0C 23 | uint32_t mouseY; // 10 24 | uint32_t unk14; // 14 25 | uint32_t unk18; // 18 26 | uint64_t unk20; // 20 27 | uint64_t unk28; // 28 28 | uint64_t unk30; // 30 29 | uint64_t unk38; // 38 30 | }; 31 | RED4EXT_ASSERT_SIZE(RawInputData, 0x40); 32 | 33 | struct RawInputBuffer 34 | { 35 | virtual ~RawInputBuffer() = 0; 36 | virtual DynArray& GetInputs() = 0; 37 | 38 | DynArray inputs; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /src/Red/JobHandle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Raw::JobHandle 4 | { 5 | constexpr auto Wait = Core::RawFunc< 6 | /* addr = */ Red::AddressLib::JobHandle_Wait, 7 | /* type = */ bool (*)(Red::JobHandle& aJob)>(); 8 | } 9 | -------------------------------------------------------------------------------- /src/Red/Logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Raw 4 | { 5 | constexpr auto LogChannel = Core::RawFunc< 6 | /* addr = */ Red::AddressLib::LogChannel, 7 | /* type = */ void (*)(void*, Red::CStackFrame* aFrame, void*, void*)>(); 8 | } 9 | -------------------------------------------------------------------------------- /src/Red/Math.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | inline float DistanceSquared(const Vector4& aPoint, const Box& aBox) 6 | { 7 | float distanceSquared = 0; 8 | 9 | if (aPoint.X < aBox.Min.X) 10 | { 11 | distanceSquared += (aBox.Min.X - aPoint.X) * (aBox.Min.X - aPoint.X); 12 | } 13 | else if (aPoint.X > aBox.Max.X) 14 | { 15 | distanceSquared += (aBox.Max.X - aPoint.X) * (aBox.Max.X - aPoint.X); 16 | } 17 | 18 | if (aPoint.Y < aBox.Min.Y) 19 | { 20 | distanceSquared += (aBox.Min.Y - aPoint.Y) * (aBox.Min.Y - aPoint.Y); 21 | } 22 | else if (aPoint.Y > aBox.Max.Y) 23 | { 24 | distanceSquared += (aBox.Max.Y - aPoint.Y) * (aBox.Max.Y - aPoint.Y); 25 | } 26 | 27 | if (aPoint.Z < aBox.Min.Z) 28 | { 29 | distanceSquared += (aBox.Min.Z - aPoint.Z) * (aBox.Min.Z - aPoint.Z); 30 | } 31 | else if (aPoint.Z > aBox.Max.Z) 32 | { 33 | distanceSquared += (aBox.Max.Z - aPoint.Z) * (aBox.Max.Z - aPoint.Z); 34 | } 35 | 36 | return distanceSquared; 37 | } 38 | 39 | inline float Distance(const Vector4& aPoint, const Box& aBox) 40 | { 41 | return sqrtf(DistanceSquared(aPoint, aBox)); 42 | } 43 | 44 | inline float Distance(const Vector4& aPoint1, const Vector4& aPoint2) 45 | { 46 | return sqrtf((aPoint1.X - aPoint2.X) * (aPoint1.X - aPoint2.X) + 47 | (aPoint1.Y - aPoint2.Y) * (aPoint1.Y - aPoint2.Y) + 48 | (aPoint1.Z - aPoint2.Z) * (aPoint1.Z - aPoint2.Z)); 49 | } 50 | 51 | inline bool Intersect(const Vector4& aOrigin, const Vector4& aInverseDirection, const Box& aBox, 52 | float& tmin, float& tmax) 53 | { 54 | float tx1 = (aBox.Min.X - aOrigin.X) * aInverseDirection.X; 55 | float tx2 = (aBox.Max.X - aOrigin.X) * aInverseDirection.X; 56 | 57 | tmin = std::min(tx1, tx2); 58 | tmax = std::max(tx1, tx2); 59 | 60 | float ty1 = (aBox.Min.Y - aOrigin.Y) * aInverseDirection.Y; 61 | float ty2 = (aBox.Max.Y - aOrigin.Y) * aInverseDirection.Y; 62 | 63 | tmin = std::max(tmin, std::min(ty1, ty2)); 64 | tmax = std::min(tmax, std::max(ty1, ty2)); 65 | 66 | float tz1 = (aBox.Min.Z - aOrigin.Z) * aInverseDirection.Z; 67 | float tz2 = (aBox.Max.Z - aOrigin.Z) * aInverseDirection.Z; 68 | 69 | tmin = std::max(tmin, std::min(tz1, tz2)); 70 | tmax = std::min(tmax, std::max(tz1, tz2)); 71 | 72 | return tmax >= std::max(0.0f, tmin); 73 | } 74 | 75 | inline bool Intersect(const Vector4& aOrigin, const Vector4& aInverseDirection, const Box& aBox) 76 | { 77 | float tmin, tmax; 78 | return Intersect(aOrigin, aInverseDirection, aBox, tmin, tmax); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Red/NodeRef.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Red/Strings.hpp" 4 | 5 | namespace Red 6 | { 7 | inline NodeRef ResolveNodeRef(NodeRef aNodeRef, NodeRef aContext = NodeRef::GlobalRoot) 8 | { 9 | GlobalNodeRef resolved{}; 10 | CallGlobal("ResolveNodeRef", resolved, aNodeRef, aContext); 11 | 12 | return resolved.hash; 13 | } 14 | 15 | inline EntityID ResolveEntityID(NodeRef aNodeRef, NodeRef aContext = NodeRef::GlobalRoot) 16 | { 17 | return ResolveNodeRef(aNodeRef).hash; 18 | } 19 | } 20 | 21 | namespace Raw::NodeRef 22 | { 23 | constexpr auto Create = Core::RawFunc< 24 | /* addr = */ Red::AddressLib::NodeRef_Create, 25 | /* type = */ Red::NodeRef* (*)(Red::NodeRef& aOut, Red::StringView& aReference)>(); 26 | } 27 | -------------------------------------------------------------------------------- /src/Red/Physics.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Raw::PhysicsTraceResult 4 | { 5 | using ResultID = Core::OffsetPtr<0x48, uint32_t>; 6 | 7 | constexpr auto GetHitObject = Core::RawFunc< 8 | /* addr = */ Red::AddressLib::PhysicsTraceResult_GetHitObject, 9 | /* type = */ void* (*)(Red::Handle& aOut, uint32_t aResultID, uint32_t aIndex)>(); 10 | } 11 | -------------------------------------------------------------------------------- /src/Red/Rendering.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | struct RenderProxy 6 | { 7 | virtual ~RenderProxy() = 0; 8 | }; 9 | 10 | struct HighlightParams 11 | { 12 | bool seeThroughWalls; // 00 13 | uint8_t patternType; // 01 14 | uint8_t fillIndex; // 02 15 | uint8_t outlineIndex; // 03 16 | float opacity; // 04 17 | bool forced; // 08 18 | }; 19 | } 20 | 21 | namespace Raw::RenderProxy 22 | { 23 | using Flags = Core::OffsetPtr<0x23, uint8_t>; 24 | 25 | inline bool IsVisible(Red::RenderProxy* aProxy) 26 | { 27 | return (Flags::Ref(aProxy) & 6) == 6; 28 | } 29 | 30 | constexpr auto SetHighlightParams = Core::RawFunc< 31 | /* addr = */ Red::AddressLib::RenderProxy_SetHighlightParams, 32 | /* type = */ uint8_t (*)(Red::RenderProxy* aProxy, const Red::HighlightParams& aParams)>(); 33 | 34 | constexpr auto SetScanningState = Core::RawFunc< 35 | /* addr = */ Red::AddressLib::RenderProxy_SetScanningState, 36 | /* type = */ uint8_t (*)(Red::RenderProxy* aProxy, Red::rendPostFx_ScanningState aState)>(); 37 | 38 | constexpr auto SetVisibility = Core::RawFunc< 39 | /* addr = */ Red::AddressLib::RenderProxy_SetVisibility, 40 | /* type = */ void (*)(Red::RenderProxy* aProxy, bool aVisible)>(); 41 | } 42 | -------------------------------------------------------------------------------- /src/Red/ResourceBank.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Raw::ResourceBank 4 | { 5 | constexpr auto ForgetResource = Core::RawFunc< 6 | /* addr = */ Red::AddressLib::ResourceBank_ForgetResource, 7 | /* type = */ uintptr_t (*)(uintptr_t aBank, Red::Handle& aResource, 8 | const Red::ResourcePath aPath)>{}; 9 | } 10 | -------------------------------------------------------------------------------- /src/Red/ResourceDepot.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Raw::ResourceDepot 4 | { 5 | constexpr auto LoadArchives = Core::RawFunc< 6 | /* addr = */ Red::AddressLib::ResourceDepot_LoadArchives, 7 | /* type = */ void (*)(Red::ResourceDepot* aDepot, 8 | Red::ArchiveGroup& aGroup, 9 | const Red::DynArray& aArchivePaths, 10 | Red::DynArray& aLoadedResourcePaths, 11 | bool aMemoryResident)>{}; 12 | 13 | constexpr auto DestructArchive = Core::RawFunc< 14 | /* addr = */ Red::AddressLib::ResourceDepot_DestructArchive, 15 | /* type = */ void (*)(Red::Archive* aArchive)>{}; 16 | 17 | constexpr auto DestructArchives = Core::RawFunc< 18 | /* addr = */ Red::AddressLib::ResourceDepot_DestructArchives, 19 | /* type = */ void (*)(Red::Archive aArchives[], uint32_t aCount)>{}; 20 | 21 | constexpr auto RequestResource = Core::RawFunc< 22 | /* addr = */ Red::AddressLib::ResourceDepot_RequestResource, 23 | /* type = */ uintptr_t* (*)(Red::ResourceDepot* aDepot, 24 | const uintptr_t* aOutResourceHandle, 25 | Red::ResourcePath aPath, 26 | const int32_t* aArchiveHandle)>{}; 27 | } 28 | -------------------------------------------------------------------------------- /src/Red/ResourcePath.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Red/Strings.hpp" 4 | 5 | namespace Raw::ResourcePath 6 | { 7 | constexpr auto Create = Core::RawFunc< 8 | /* addr = */ Red::AddressLib::ResourcePath_Create, 9 | /* type = */ Red::ResourcePath* (*)(Red::ResourcePath* aOut, Red::StringView* aPathStr)>(); 10 | } 11 | -------------------------------------------------------------------------------- /src/Red/ScriptBinder.cpp: -------------------------------------------------------------------------------- 1 | #include "ScriptBinder.hpp" 2 | 3 | Red::ScriptBinder::ScriptBinder(CRTTISystem* aRtti, const FileResolver& aFileResolver) 4 | : rtti(aRtti) 5 | , fileResolver(aFileResolver) 6 | { 7 | } 8 | 9 | // bool Red::ScriptBinder::Bind(ScriptBundle& aData, ScriptReport& aReport) 10 | // { 11 | // using func_t = bool (*)(ScriptBinder*, ScriptBundle&, ScriptReport&, bool); 12 | // static UniversalRelocFunc func(AddressLib::ScriptBinder_Bind); 13 | // return func(this, aData, aReport, false); 14 | // } 15 | 16 | bool Red::ScriptBinder::ResolveNatives(DynArray& aDefinitions, ScriptReport& aReport) 17 | { 18 | using func_t = bool (*)(ScriptBinder*, DynArray&, ScriptReport&); 19 | static UniversalRelocFunc func(AddressLib::ScriptBinder_ResolveNatives); 20 | return func(this, aDefinitions, aReport); 21 | } 22 | 23 | void Red::ScriptBinder::ResolveTypes(DynArray& aDefinitions) 24 | { 25 | using func_t = void (*)(ScriptBinder*, DynArray&); 26 | static UniversalRelocFunc func(AddressLib::ScriptBinder_ResolveTypes); 27 | func(this, aDefinitions); 28 | } 29 | 30 | bool Red::ScriptBinder::CreateClass(ScriptClass* aClass, ScriptReport& aReport) 31 | { 32 | using func_t = bool (*)(ScriptBinder*, ScriptClass*, ScriptReport&); 33 | static UniversalRelocFunc func(AddressLib::ScriptBinder_CreateClass); 34 | return func(this, aClass, aReport); 35 | } 36 | 37 | bool Red::ScriptBinder::CreateEnum(ScriptEnum* aEnum, ScriptReport& aReport) 38 | { 39 | using func_t = bool (*)(ScriptBinder*, ScriptEnum*, ScriptReport&); 40 | static UniversalRelocFunc func(AddressLib::ScriptBinder_CreateEnum); 41 | return func(this, aEnum, aReport); 42 | } 43 | 44 | bool Red::ScriptBinder::CreateBitfield(ScriptBitfield* aBitfield, ScriptReport& aReport) 45 | { 46 | using func_t = bool (*)(ScriptBinder*, ScriptBitfield*, ScriptReport&); 47 | static UniversalRelocFunc func(AddressLib::ScriptBinder_CreateBitfield); 48 | return func(this, aBitfield, aReport); 49 | } 50 | 51 | bool Red::ScriptBinder::CreateProperties(ScriptClass* aClass, ScriptReport& aReport) 52 | { 53 | using func_t = bool (*)(ScriptBinder*, ScriptClass*, ScriptReport&); 54 | static UniversalRelocFunc func(AddressLib::ScriptBinder_CreateProperties); 55 | return func(this, aClass, aReport); 56 | } 57 | 58 | bool Red::ScriptBinder::CreateFunction(ScriptFunction* aFunc, ScriptReport& aReport) 59 | { 60 | using func_t = bool (*)(ScriptBinder*, ScriptFunction*, ScriptReport&); 61 | static UniversalRelocFunc func(AddressLib::ScriptBinder_CreateFunction); 62 | return func(this, aFunc, aReport); 63 | } 64 | 65 | void Red::ScriptBinder::TranslateBytecode(DynArray& aDefinitions) 66 | { 67 | using func_t = void (*)(ScriptBinder*, DynArray&); 68 | static UniversalRelocFunc func(AddressLib::ScriptBinder_TranslateBytecode); 69 | func(this, aDefinitions); 70 | } 71 | -------------------------------------------------------------------------------- /src/Red/ScriptBinder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ScriptBundle.hpp" 4 | #include "ScriptReport.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace Red 11 | { 12 | struct ScriptFile 13 | { 14 | uint32_t hash; 15 | uint32_t crc; 16 | CString path; 17 | }; 18 | 19 | using FileResolver = std::function; 20 | 21 | struct ScriptBinder 22 | { 23 | ScriptBinder(CRTTISystem* aRtti, const FileResolver& aFileResolver); 24 | 25 | // bool Bind(ScriptBundle& aData, ScriptReport& aReport); 26 | bool ResolveNatives(DynArray& aDefinitions, ScriptReport& aReport); 27 | void ResolveTypes(DynArray& aDefinitions); 28 | bool CreateClass(ScriptClass* aClass, ScriptReport& aReport); 29 | bool CreateEnum(ScriptEnum* aEnum, ScriptReport& aReport); 30 | bool CreateBitfield(ScriptBitfield* aBitfield, ScriptReport& aReport); 31 | bool CreateProperties(ScriptClass* aClass, ScriptReport& aReport); 32 | bool CreateFunction(ScriptFunction* aFunc, ScriptReport& aReport); 33 | void TranslateBytecode(DynArray& aDefinitions); 34 | 35 | CRTTISystem* rtti; 36 | FileResolver fileResolver; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /src/Red/ScriptBundle.cpp: -------------------------------------------------------------------------------- 1 | #include "ScriptBundle.hpp" 2 | #include "Red/FileSystem.hpp" 3 | 4 | Red::ScriptBundle::ScriptBundle() 5 | { 6 | using func_t = void (*)(ScriptBundle*); 7 | static UniversalRelocFunc func(AddressLib::ScriptBundle_ctor); 8 | func(this); 9 | } 10 | 11 | Red::ScriptBundle::~ScriptBundle() 12 | { 13 | using func_t = void (*)(ScriptBundle*); 14 | static UniversalRelocFunc func(AddressLib::ScriptBundle_dtor); 15 | func(this); 16 | } 17 | 18 | bool Red::ScriptBundle::Read(const CString& aPath) 19 | { 20 | auto file = FileSystem::Get()->Open(aPath, 1); 21 | if (file) 22 | { 23 | using func_t = bool (*)(ScriptBundle*, uint64_t); 24 | static UniversalRelocFunc func(AddressLib::ScriptBundle_Read); 25 | return func(this, file.ptr); 26 | } 27 | else 28 | { 29 | return false; 30 | } 31 | } 32 | 33 | Red::DynArray Red::ScriptBundle::Collect(bool aDeep) const 34 | { 35 | DynArray definitions; 36 | 37 | for (auto* func : globals) 38 | { 39 | definitions.PushBack(func); 40 | 41 | if (aDeep) 42 | { 43 | for (auto* param : func->params) 44 | definitions.PushBack(param); 45 | 46 | for (auto* local : func->localVars) 47 | definitions.PushBack(local); 48 | } 49 | } 50 | 51 | for (auto* type : enums) 52 | { 53 | definitions.PushBack(type); 54 | } 55 | 56 | for (auto* type : bitfields) 57 | { 58 | definitions.PushBack(type); 59 | } 60 | 61 | for (auto* type : classes) 62 | { 63 | definitions.PushBack(type); 64 | 65 | if (aDeep) 66 | { 67 | for (auto* prop : type->properties) 68 | definitions.PushBack(prop); 69 | 70 | for (auto* prop : type->overrides) 71 | definitions.PushBack(prop); 72 | 73 | for (auto* func : type->functions) 74 | { 75 | definitions.PushBack(func); 76 | 77 | for (auto* param : func->params) 78 | definitions.PushBack(param); 79 | 80 | for (auto* local : func->localVars) 81 | definitions.PushBack(local); 82 | } 83 | } 84 | } 85 | 86 | for (auto* type : types) 87 | { 88 | definitions.PushBack(type); 89 | } 90 | 91 | return definitions; 92 | } 93 | -------------------------------------------------------------------------------- /src/Red/ScriptCompiler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace Red 7 | { 8 | enum class EScriptProfile : uint8_t 9 | { 10 | Master = 0, // master.redscripts | -profile=on 11 | Profiling = 2, // profiling.redscripts | -no-breakpoint -profile=on 12 | Final = 8, // final.redscripts | -no-testonly -no-breakpoint -no-exec -no-debug -profile=off -optimize 13 | NoOpts = 16, // final_noopts.redscripts | -no-testonly -no-breakpoint -no-exec -no-debug -profile=off 14 | }; 15 | 16 | struct ScriptCompiler 17 | { 18 | // inline static bool Compile(CString& aSourceDir, CString& aBlobPath) 19 | // { 20 | // return InvokeSCC(aSourceDir, aBlobPath, EScriptProfile::Final, 0, 0, 0, nullptr); 21 | // } 22 | 23 | inline static bool Compile() 24 | { 25 | using func_t = bool(*)(void* a1, const CString& aCommand, void*, void*, char); 26 | static UniversalRelocFunc func(AddressLib::ExecuteProcess); 27 | 28 | return func(nullptr, "scc.exe", nullptr, nullptr, 0); 29 | } 30 | 31 | inline static bool Compile(CString& aSourceDir, CString& aBlobPath, CString& aOutput) 32 | { 33 | return InvokeSCC(aSourceDir, aBlobPath, EScriptProfile::Final, 0, 0, 0, &aOutput); 34 | } 35 | 36 | inline static bool InvokeSCC(CString& aSourceDir, CString& aBlobPath, EScriptProfile aProfile, 37 | int64_t a4, int32_t a5, int32_t a6, CString* aOutput) 38 | { 39 | using func_t = decltype(&InvokeSCC); 40 | static UniversalRelocFunc func(AddressLib::InvokeSCC); 41 | 42 | return func(aSourceDir, aBlobPath, aProfile, a4, a5, a6, aOutput); 43 | } 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/Red/ScriptReport.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Red 10 | { 11 | struct ScriptReport 12 | { 13 | ScriptReport() noexcept 14 | : errors(&unk10) 15 | , maxErrors(0) 16 | , fillErrors(true) 17 | { 18 | } 19 | 20 | ScriptReport(DynArray& aErrors, uint32_t aMaxErrors = 0) noexcept 21 | : errors(&aErrors) 22 | , maxErrors(aMaxErrors) 23 | , fillErrors(true) 24 | { 25 | } 26 | 27 | virtual ~ScriptReport() = default; 28 | 29 | virtual void AddValidationError(const char* aFormat, ...) 30 | { 31 | if (errors && fillErrors) 32 | { 33 | std::va_list args; 34 | va_start(args, aFormat); 35 | errors->PushBack(Format(aFormat, args)); 36 | va_end(args); 37 | } 38 | } 39 | 40 | virtual void AddBindingError(const char* aFormat, ...) 41 | { 42 | if (errors && fillErrors) 43 | { 44 | std::va_list args; 45 | va_start(args, aFormat); 46 | errors->PushBack(Format(aFormat, args)); 47 | va_end(args); 48 | } 49 | } 50 | 51 | [[nodiscard]] inline bool HasErrors() const noexcept 52 | { 53 | return errors && errors->size > 0; 54 | } 55 | 56 | [[nodiscard]] inline CString ToString() const noexcept 57 | { 58 | if (!errors || errors->size == 0) 59 | { 60 | return {}; 61 | } 62 | 63 | std::string str; 64 | bool eol = false; 65 | 66 | for (const auto& error : *errors) 67 | { 68 | if (eol) 69 | { 70 | str.append("\n"); 71 | } 72 | 73 | str.append(error.c_str()); 74 | eol = true; 75 | } 76 | 77 | return str.c_str(); 78 | } 79 | 80 | inline static CString Format(const char* aFormat, std::va_list aArgs) 81 | { 82 | char buffer[4096]; 83 | vsnprintf(buffer, sizeof(buffer), aFormat, aArgs); 84 | return buffer; 85 | } 86 | 87 | bool fillErrors; // 08 - Usually equals to CBaseEngine::scriptsSilentValidation 88 | DynArray unk10; // 10 - Seems to be unused by the game 89 | DynArray* errors; // 20 - Usually points to CBaseEngine::scriptsValidationErrors 90 | uint32_t maxErrors; // 28 91 | }; 92 | RED4EXT_ASSERT_SIZE(ScriptReport, 0x30); 93 | RED4EXT_ASSERT_OFFSET(ScriptReport, fillErrors, 0x08); 94 | RED4EXT_ASSERT_OFFSET(ScriptReport, errors, 0x20); 95 | RED4EXT_ASSERT_OFFSET(ScriptReport, maxErrors, 0x28); 96 | } 97 | -------------------------------------------------------------------------------- /src/Red/ScriptValidator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ScriptBundle.hpp" 4 | #include "ScriptReport.hpp" 5 | 6 | namespace Red 7 | { 8 | struct ScriptValidator 9 | { 10 | inline static bool Validate(ScriptBundle& aData, ScriptReport& aReport) 11 | { 12 | using func_t = bool (*)(ScriptValidator*, ScriptBundle&, ScriptReport&); 13 | static UniversalRelocFunc func(AddressLib::ScriptValidator_Validate); 14 | 15 | return func(nullptr, aData, aReport); 16 | } 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/Red/Scripting.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Raw::ISerializable 4 | { 5 | constexpr auto Clone = Core::RawFunc< 6 | /* addr = */ Red::AddressLib::ISerializable_Clone, 7 | /* type = */ void* (*)(Red::Handle& aOut, 8 | const Red::Handle& aObject)>(); 9 | } 10 | -------------------------------------------------------------------------------- /src/Red/SharedStorage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | template 6 | inline T* AcquireSharedInstance(Args&&... args) 7 | { 8 | constexpr CName SharedName = FNV1a64("!", N); 9 | 10 | auto rtti = Red::CRTTISystem::Get(); 11 | if (!rtti) 12 | return nullptr; 13 | 14 | auto* mutex = const_cast(reinterpret_cast(&rtti->typesLock)); 15 | auto* storage = reinterpret_cast*>(&rtti->unkF8); 16 | 17 | std::lock_guard _(*mutex); 18 | 19 | auto* entry = storage->Get(SharedName); 20 | if (entry) 21 | return *entry; 22 | 23 | static const auto s_instance = std::make_unique(std::forward(args)...); 24 | storage->Insert(SharedName, s_instance.get()); 25 | 26 | return s_instance.get(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Red/StreamingSector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Red/Common.hpp" 4 | 5 | namespace Red 6 | { 7 | struct __declspec(align(0x10)) CompiledNodeInstanceSetupInfo 8 | { 9 | Transform transform; // 00 10 | Vector3 scale; // 20 11 | Vector3 secondaryRefPointPosition; // 2C 12 | Vector3 streamingRefPoint; // 38 13 | Vector3 unk44; // 44 14 | worldNode* node; // 50 15 | uint64_t globalNodeID; // 58 16 | uint64_t proxyNodeID; // 60 17 | ResourcePath unk68; // 68 18 | float secondaryRefPointDistance; // 70 19 | float streamingDistance; // 74 20 | uint16_t nodeIndex; // 78 21 | uint16_t unk7A; // 7A 22 | uint16_t unk7C; // 7C 23 | uint16_t unk7E; // 7E 24 | uint64_t unk80; // 80 25 | uint64_t unk88; // 88 26 | }; 27 | RED4EXT_ASSERT_SIZE(CompiledNodeInstanceSetupInfo, 0x90); 28 | RED4EXT_ASSERT_OFFSET(CompiledNodeInstanceSetupInfo, node, 0x50); 29 | RED4EXT_ASSERT_OFFSET(CompiledNodeInstanceSetupInfo, globalNodeID, 0x58); 30 | RED4EXT_ASSERT_OFFSET(CompiledNodeInstanceSetupInfo, nodeIndex, 0x78); 31 | 32 | struct CompiledNodeInstanceSetupInfoBuffer : DataBuffer 33 | { 34 | [[nodiscard]] inline CompiledNodeInstanceSetupInfo* begin() const 35 | { 36 | return reinterpret_cast(buffer.data); 37 | } 38 | 39 | [[nodiscard]] inline CompiledNodeInstanceSetupInfo* end() const 40 | { 41 | return reinterpret_cast(reinterpret_cast(buffer.data) + 42 | buffer.size); 43 | } 44 | }; 45 | RED4EXT_ASSERT_SIZE(CompiledNodeInstanceSetupInfoBuffer, 0x28); 46 | 47 | struct StreamingSectorNodeBuffer 48 | { 49 | CompiledNodeInstanceSetupInfoBuffer nodeSetups; // 00 50 | DynArray> nodes; // 28 51 | DynArray nodeRefs; // 38 52 | }; 53 | RED4EXT_ASSERT_OFFSET(StreamingSectorNodeBuffer, nodes, 0x28); 54 | RED4EXT_ASSERT_OFFSET(StreamingSectorNodeBuffer, nodeRefs, 0x38); 55 | } 56 | 57 | namespace Raw::StreamingSector 58 | { 59 | using NodeBuffer = Core::OffsetPtr<0x40, Red::StreamingSectorNodeBuffer>; 60 | 61 | constexpr auto Destruct = Core::RawFunc< 62 | /* addr = */ Red::AddressLib::StreamingSector_dtor, 63 | /* type = */ void (*)(Red::worldStreamingSector* aSector)>(); 64 | 65 | constexpr auto PostLoad = Core::RawFunc< 66 | /* addr = */ Red::AddressLib::StreamingSector_OnReady, 67 | /* type = */ void (*)(Red::worldStreamingSector* aSector, uint64_t a2)>(); 68 | } 69 | -------------------------------------------------------------------------------- /src/Red/Strings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Red 4 | { 5 | struct StringView 6 | { 7 | const char* data; 8 | uint32_t size; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /src/Red/Transform.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Raw::Transform 4 | { 5 | constexpr auto ApplyToBox = Core::RawFunc< 6 | /* addr = */ Red::AddressLib::Transform_ApplyToBox, 7 | /* type = */ void* (*)(const Red::Transform& aTransform, Red::Box& aOut, const Red::Box& aBox)>(); 8 | } 9 | 10 | // namespace Raw::WorldTransform 11 | // { 12 | // constexpr auto ApplyToBox = Core::RawFunc< 13 | // /* addr = */ 0x14014D910 - Red::AddressLib::ImageBase, // FIXME 14 | // /* type = */ void* (*)(const Red::WorldTransform& aTransform, Red::Box& aOut, const Red::Box& aBox)>(); 15 | // } 16 | 17 | namespace Red 18 | { 19 | inline void ScaleBox(Box& aBox, const Vector3& aScale) 20 | { 21 | Red::Vector4 scale{aScale.X, aScale.Y, aScale.Z, 1.0}; 22 | *reinterpret_cast<__m128*>(&aBox.Min) = 23 | _mm_mul_ps(*reinterpret_cast<__m128*>(&aBox.Min), *reinterpret_cast<__m128*>(&scale)); 24 | *reinterpret_cast<__m128*>(&aBox.Max) = 25 | _mm_mul_ps(*reinterpret_cast<__m128*>(&aBox.Max), *reinterpret_cast<__m128*>(&scale)); 26 | } 27 | 28 | inline void TransformBox(Box& aBox, const Transform& aTransform) 29 | { 30 | Box newBox{}; 31 | Raw::Transform::ApplyToBox(aTransform, newBox, aBox); 32 | aBox = newBox; 33 | } 34 | 35 | // inline void TransformBox(Box& aBox, const WorldTransform& aTransform) 36 | // { 37 | // Box newBox{}; 38 | // Raw::WorldTransform::ApplyToBox(aTransform, newBox, aBox); 39 | // aBox = newBox; 40 | // } 41 | 42 | inline bool IsValidBox(const Box& aBox) 43 | { 44 | return aBox.Max.X >= aBox.Min.X && aBox.Max.Y >= aBox.Min.Y && aBox.Max.Z >= aBox.Min.Z; 45 | } 46 | 47 | inline bool IsZeroBox(const Box& aBox) 48 | { 49 | return aBox.Max.X == 0 && aBox.Min.X == 0 && 50 | aBox.Max.Y == 0 && aBox.Min.Y == 0 && 51 | aBox.Max.Z == 0 && aBox.Min.Z == 0; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "App/Application.hpp" 2 | #include "App/Project.hpp" 3 | #include "Core/Facades/Hook.hpp" 4 | #include "Core/Facades/Runtime.hpp" 5 | 6 | namespace 7 | { 8 | Core::UniquePtr g_app; 9 | } 10 | 11 | // RED4ext 12 | 13 | RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::PluginHandle aHandle, RED4ext::EMainReason aReason, 14 | const RED4ext::Sdk* aSdk) 15 | { 16 | switch (aReason) 17 | { 18 | case RED4ext::EMainReason::Load: 19 | { 20 | g_app = Core::MakeUnique(aHandle, aSdk); 21 | g_app->Bootstrap(); 22 | break; 23 | } 24 | case RED4ext::EMainReason::Unload: 25 | { 26 | g_app->Shutdown(); 27 | g_app = nullptr; 28 | break; 29 | } 30 | } 31 | 32 | return true; 33 | } 34 | 35 | RED4EXT_C_EXPORT void RED4EXT_CALL Query(RED4ext::PluginInfo* aInfo) 36 | { 37 | aInfo->name = App::Project::NameW; 38 | aInfo->author = App::Project::AuthorW; 39 | aInfo->version = RED4EXT_SEMVER(App::Project::Version.major, 40 | App::Project::Version.minor, 41 | App::Project::Version.patch); 42 | 43 | aInfo->runtime = RED4EXT_RUNTIME_INDEPENDENT; 44 | aInfo->sdk = RED4EXT_SDK_LATEST; 45 | } 46 | 47 | RED4EXT_C_EXPORT uint32_t RED4EXT_CALL Supports() 48 | { 49 | return RED4EXT_API_VERSION_LATEST; 50 | } 51 | 52 | // ASI 53 | 54 | BOOL APIENTRY DllMain(HMODULE aHandle, DWORD aReason, LPVOID) 55 | { 56 | using GameMain = Core::RawFunc; 58 | 59 | static const bool s_isGame = Core::Runtime::IsEXE(L"Cyberpunk2077.exe"); 60 | static const bool s_isASI = Core::Runtime::IsASI(aHandle); 61 | 62 | switch (aReason) // NOLINT(hicpp-multiway-paths-covered) 63 | { 64 | case DLL_PROCESS_ATTACH: 65 | { 66 | DisableThreadLibraryCalls(aHandle); 67 | 68 | if (s_isGame && s_isASI) 69 | { 70 | g_app = Core::MakeUnique(aHandle); 71 | 72 | Core::Hook::Before(+[]() { 73 | g_app->Bootstrap(); 74 | }); 75 | } 76 | break; 77 | } 78 | case DLL_PROCESS_DETACH: 79 | { 80 | if (s_isGame && s_isASI) 81 | { 82 | g_app->Shutdown(); 83 | g_app = nullptr; 84 | } 85 | break; 86 | } 87 | } 88 | 89 | return TRUE; 90 | } 91 | -------------------------------------------------------------------------------- /src/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.hpp" 2 | -------------------------------------------------------------------------------- /support/cet/libs/Enumeration.lua: -------------------------------------------------------------------------------- 1 | return function(...) 2 | local map = { values = {} } 3 | for index, value in ipairs({...}) do 4 | map[value] = value 5 | map.values[index] = value 6 | map.values[value] = index 7 | end 8 | return map 9 | end 10 | -------------------------------------------------------------------------------- /support/cet/libs/ImGuiEx.lua: -------------------------------------------------------------------------------- 1 | local function opacity(abgr) 2 | return bit32.band(bit32.rshift(abgr, 24), 0xFF) 3 | end 4 | 5 | local function fade(abgr, a) 6 | return bit32.bor(bit32.lshift(bit32.band(a, 0xFF), 24), bit32.band(abgr, 0xFFFFFF)) 7 | end 8 | 9 | local function sanitizeInputText(text) 10 | return text:gsub('`', '') 11 | end 12 | 13 | local function buildComboOptions(values, capitalize) 14 | local options = {} 15 | for index, value in ipairs(values) do 16 | options[index] = value:gsub('([a-z])([A-Z])', function(l, u) 17 | return l .. ' ' .. (capitalize and u or u:lower()) 18 | end) 19 | end 20 | return options 21 | end 22 | 23 | local function buildEnumOptions(enumType) 24 | local options = {} 25 | local value = 0 26 | while true do 27 | local instance = Enum.new(enumType, value) 28 | if EnumInt(instance) ~= value then 29 | break 30 | end 31 | 32 | table.insert(options, instance.value) 33 | value = value + 1 34 | 35 | if value > 256 then 36 | break 37 | end 38 | end 39 | return options 40 | end 41 | 42 | local function beginWindow(title, closable, flags) 43 | if closable then 44 | return ImGui.Begin(title, true, flags) 45 | else 46 | ImGui.Begin(title, flags) 47 | return true 48 | end 49 | end 50 | 51 | return { 52 | Begin = beginWindow, 53 | BuildComboOptions = buildComboOptions, 54 | BuildEnumOptions = buildEnumOptions, 55 | SanitizeInputText = sanitizeInputText, 56 | Opacity = opacity, 57 | Fade = fade, 58 | } 59 | -------------------------------------------------------------------------------- /support/cet/libs/MathEx.lua: -------------------------------------------------------------------------------- 1 | local function clamp(value, rangeMin, rangeMax) 2 | return math.max(rangeMin, math.min(rangeMax, value)) 3 | end 4 | 5 | return { 6 | Clamp = clamp, 7 | } 8 | -------------------------------------------------------------------------------- /support/cet/libs/Ref.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Ref.lua 3 | Strong & Weak References 4 | 5 | Copyright (c) 2021 psiberx 6 | ]] 7 | 8 | local Ref = { version = '1.0.0' } 9 | 10 | ---@type inkScriptWeakHashMap 11 | local weakMap 12 | 13 | ---@type inkScriptHashMap 14 | local strongMap 15 | 16 | ---@param o IScriptable 17 | ---@return boolean 18 | function Ref.IsDefined(o) 19 | return IsDefined(o) 20 | end 21 | 22 | ---@param o IScriptable 23 | ---@return boolean 24 | function Ref.IsExpired(o) 25 | return not IsDefined(o) 26 | end 27 | 28 | ---@param a IScriptable 29 | ---@param b IScriptable 30 | ---@return boolean 31 | function Ref.Equals(a, b) 32 | return Game['OperatorEqual;IScriptableIScriptable;Bool'](a, b) 33 | end 34 | 35 | ---@param a IScriptable 36 | ---@param b IScriptable 37 | ---@return boolean 38 | function Ref.NotEquals(a, b) 39 | return Game['OperatorNotEqual;IScriptableIScriptable;Bool'](a, b) 40 | end 41 | 42 | ---@param o IScriptable 43 | ---@return IScriptable 44 | function Ref.Weak(o) 45 | if not weakMap then 46 | weakMap = inkScriptWeakHashMap.new() 47 | weakMap:Insert(0, nil) 48 | end 49 | 50 | weakMap:Set(0, o) 51 | 52 | return weakMap:Get(0) 53 | end 54 | 55 | ---@param o IScriptable 56 | ---@return IScriptable 57 | function Ref.Strong(o) 58 | if not strongMap then 59 | strongMap = inkScriptHashMap.new() 60 | strongMap:Insert(0, nil) 61 | end 62 | 63 | strongMap:Set(0, o) 64 | 65 | local ref = strongMap:Get(0) 66 | 67 | strongMap:Set(0, nil) 68 | 69 | return ref 70 | end 71 | 72 | ---@param o IScriptable 73 | ---@return Int32 74 | function Ref.Hash(o) 75 | return CalcSeed(o) 76 | end 77 | 78 | return Ref -------------------------------------------------------------------------------- /support/cet/version.lua: -------------------------------------------------------------------------------- 1 | return '1.2.3' 2 | -------------------------------------------------------------------------------- /support/vscode/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": [ 10 | "--extensionDevelopmentPath=${workspaceRoot}" 11 | ], 12 | "sourceMaps": false, 13 | "outFiles": [ 14 | "${workspaceRoot}/out/src/**/*.js" 15 | ], 16 | "preLaunchTask": "npm: compile" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /support/vscode/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "node_modules": true, 4 | "out": true, 5 | }, 6 | "search.exclude": { 7 | "node_modules": true, 8 | "out": true 9 | }, 10 | "typescript.tsdk": "./node_modules/typescript/lib", 11 | "typescript.tsc.autoDetect": "off" 12 | } 13 | -------------------------------------------------------------------------------- /support/vscode/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | node_modules/** 3 | typings/** 4 | out/test/** 5 | test/** 6 | src/** 7 | **/*.map 8 | **/*.vsix 9 | .gitignore 10 | tsconfig.json 11 | -------------------------------------------------------------------------------- /support/vscode/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Pavel Siberx 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 | -------------------------------------------------------------------------------- /support/vscode/README.md: -------------------------------------------------------------------------------- 1 | ## Setup 2 | 3 | To activate the extension, you must specify the absolute path to the Cyberpunk 2077 directory in the settings (`redHotTools.gameDir` in `settings.json`). 4 | 5 | If you have installed and configured the [redscript extension](https://github.com/jac3km4/redscript-ide-vscode), then you can skip the configutation. 6 | This extension will use the redscript settings. 7 | 8 | ## Usage 9 | 10 | ### Scripts 11 | 12 | To reload scripts, open any redscript file and select "Hot Reload Scripts" in the editor menu. 13 | 14 | ### Tweaks 15 | 16 | To reload tweaks, open any tweak file from `r6/tweaks` directory and select "Hot Reload Tweaks" in the editor menu. 17 | -------------------------------------------------------------------------------- /support/vscode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "red-hot-vscode", 3 | "displayName": "Red Hot Tools", 4 | "description": "Hot reload scripts and tweaks from VS Code", 5 | "publisher": "psiberx", 6 | "version": "0.1.0", 7 | "license": "MIT", 8 | "repository": "https://github.com/psiberx/cp2077-red-hot-tools", 9 | "engines": { 10 | "vscode": "^1.52.0" 11 | }, 12 | "categories": [ 13 | "Other" 14 | ], 15 | "main": "./out/extension.js", 16 | "activationEvents": [ 17 | "workspaceContains:**/*.reds", 18 | "workspaceContains:**/*.tweak", 19 | "workspaceContains:**/*.yaml" 20 | ], 21 | "contributes": { 22 | "commands": [ 23 | { 24 | "command": "redHotTools.reloadScripts", 25 | "title": "Hot Reload Scripts", 26 | "category": "RedHotTools" 27 | }, 28 | { 29 | "command": "redHotTools.reloadTweaks", 30 | "title": "Hot Reload Tweaks", 31 | "category": "RedHotTools" 32 | } 33 | ], 34 | "menus": { 35 | "editor/title": [ 36 | { 37 | "command": "redHotTools.reloadScripts", 38 | "group": "1_run", 39 | "when": "redHotTools.resourceIsScript" 40 | }, 41 | { 42 | "command": "redHotTools.reloadTweaks", 43 | "group": "1_run", 44 | "when": "redHotTools.resourceIsTweak" 45 | } 46 | ] 47 | }, 48 | "configuration": { 49 | "title": "Red Hot Tools", 50 | "properties": { 51 | "redHotTools.gameDir": { 52 | "type": "string", 53 | "description": "Path to Cyberpunk 2077 directory" 54 | } 55 | } 56 | } 57 | }, 58 | "scripts": { 59 | "vscode:prepublish": "npm run compile", 60 | "compile": "tsc -p ./", 61 | "lint": "eslint . --ext .ts,.tsx", 62 | "watch": "tsc -watch -p ./" 63 | }, 64 | "devDependencies": { 65 | "@types/node": "^16.11.7", 66 | "@types/vscode": "^1.32.0", 67 | "typescript": "^4.8.4" 68 | }, 69 | "dependencies": { 70 | "vsce": "^2.13.0" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /support/vscode/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2020", 5 | "lib": ["es2020"], 6 | "outDir": "out", 7 | "sourceMap": true, 8 | "strict": true, 9 | "rootDir": "src" 10 | }, 11 | "exclude": [ 12 | "node_modules", 13 | ".vscode-test" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tools/dist/package-addons.ps1: -------------------------------------------------------------------------------- 1 | param ($ReleaseBin, $ProjectName = "RedHotTools") 2 | 3 | #$StageDir = "build/package" 4 | $DistDir = "build/dist" 5 | 6 | #& $($PSScriptRoot + "\steps\compose-cet-mod.ps1") -StageDir ${StageDir} -ProjectName ${ProjectName} 7 | #& $($PSScriptRoot + "\steps\create-zip-from-stage.ps1") -StageDir ${StageDir} -ProjectName ${ProjectName} -Suffix "CET" -DistDir ${DistDir} 8 | # 9 | #Remove-Item -Recurse ${StageDir} 10 | 11 | & $($PSScriptRoot + "\steps\compile-vscode-ext.ps1") -DistDir ${DistDir} 12 | -------------------------------------------------------------------------------- /tools/dist/package-mod.ps1: -------------------------------------------------------------------------------- 1 | param ($ReleaseBin, $ProjectName = "RedHotTools") 2 | 3 | $StageDir = "build/package" 4 | $DistDir = "build/dist" 5 | 6 | & $($PSScriptRoot + "\steps\compose-plugin-red4ext.ps1") -StageDir ${StageDir} -ProjectName ${ProjectName} -ReleaseBin ${ReleaseBin} 7 | & $($PSScriptRoot + "\steps\compose-plugin-data.ps1") -StageDir ${StageDir} -ProjectName ${ProjectName} 8 | & $($PSScriptRoot + "\steps\create-zip-from-stage.ps1") -StageDir ${StageDir} -ProjectName ${ProjectName} -Version Auto -DistDir ${DistDir} 9 | 10 | Remove-Item -Recurse ${StageDir} 11 | 12 | & $($PSScriptRoot + "\steps\compose-cet-mod.ps1") -StageDir ${StageDir} -ProjectName ${ProjectName} 13 | & $($PSScriptRoot + "\steps\create-zip-from-stage.ps1") -StageDir ${StageDir} -ProjectName ${ProjectName} -Version Auto -Suffix "Overlay" -DistDir ${DistDir} 14 | 15 | Remove-Item -Recurse ${StageDir} 16 | -------------------------------------------------------------------------------- /tools/dist/readme.md: -------------------------------------------------------------------------------- 1 | ### How to use 2 | 3 | There are two scripts for each ASI and RED4ext setup: 4 | 5 | - Install: Deploys the current build. Intended to be used as an after build script. 6 | - Package: Prepares the current build as a distribution archive for end users. 7 | 8 | ### How to use 9 | 10 | Examples: 11 | 12 | - `.\tools\dist\install-red4ext.ps1 -GameDir 'C:\Games\Cyberpunk 2077' -ReleaseBin 'C:\Dev\RedHotTools\build\debug\RedHotTools.dll'` 13 | - `.\tools\dist\package-red4ext.ps1 -ReleaseBin 'C:\Dev\RedHotTools\build\release\RedHotTools.dll'` 14 | -------------------------------------------------------------------------------- /tools/dist/steps/compile-vscode-ext.ps1: -------------------------------------------------------------------------------- 1 | param ($DistDir) 2 | 3 | Set-Location -Path "support/vscode" 4 | 5 | & .\node_modules\.bin\vsce package -o "../../${DistDir}" 6 | 7 | Set-Location -Path "../.." 8 | -------------------------------------------------------------------------------- /tools/dist/steps/compose-cet-mod.ps1: -------------------------------------------------------------------------------- 1 | param ($StageDir, $ProjectName) 2 | 3 | $ModsDir = "${StageDir}/bin/x64/plugins/cyber_engine_tweaks/mods" 4 | 5 | New-Item -ItemType directory -Force -Path ${ModsDir} | Out-Null 6 | Copy-Item -Path "support/cet/" -Filter "*.lua" -Destination ${ModsDir} -Recurse -Container 7 | Rename-Item -path "${ModsDir}/cet" -NewName ${ProjectName} 8 | -------------------------------------------------------------------------------- /tools/dist/steps/compose-plugin-asi.ps1: -------------------------------------------------------------------------------- 1 | param ($StageDir, $ReleaseBin, $ProjectName) 2 | 3 | $PluginDir = "${StageDir}/bin/x64/plugins" 4 | 5 | New-Item -ItemType directory -Force -Path ${PluginDir} | Out-Null 6 | Copy-Item -Path ${ReleaseBin} -Destination "${PluginDir}/${ProjectName}.asi" 7 | -------------------------------------------------------------------------------- /tools/dist/steps/compose-plugin-data.ps1: -------------------------------------------------------------------------------- 1 | param ($StageDir, $ReleaseBin, $ProjectName) 2 | 3 | $DataDir = "${StageDir}/red4ext/plugins/${ProjectName}" 4 | 5 | New-Item -ItemType directory -Force -Path ${DataDir} | Out-Null 6 | Copy-Item -Path "data/*" -Destination ${DataDir} 7 | -------------------------------------------------------------------------------- /tools/dist/steps/compose-plugin-red4ext.ps1: -------------------------------------------------------------------------------- 1 | param ($StageDir, $ReleaseBin, $ProjectName) 2 | 3 | $PluginDir = "${StageDir}/red4ext/plugins/${ProjectName}" 4 | 5 | New-Item -ItemType directory -Force -Path ${PluginDir} | Out-Null 6 | Copy-Item -Path ${ReleaseBin} -Destination "${PluginDir}/${ProjectName}.dll" 7 | -------------------------------------------------------------------------------- /tools/dist/steps/create-zip-from-stage.ps1: -------------------------------------------------------------------------------- 1 | param ($StageDir, $ProjectName, $DistDir, $Version = "", $Suffix = "") 2 | 3 | if ($Version -eq "Auto") { 4 | $Version = Select-String -path "src/App/Project.hpp" -pattern """(\d+\.\d+\.\d+)""" | % {"$($_.Matches.Groups[1])"} 5 | } 6 | 7 | if ($Version -ne "") { 8 | $Version = "-${Version}" 9 | } 10 | 11 | if ($Suffix -ne "") { 12 | $Suffix = "-${Suffix}" 13 | } 14 | 15 | New-Item -ItemType directory -Force -Path ${DistDir} | Out-Null 16 | Compress-Archive -Path "${StageDir}/*" -CompressionLevel Optimal -Force -DestinationPath "${DistDir}/${ProjectName}${Version}${Suffix}.zip" 17 | -------------------------------------------------------------------------------- /tools/dist/steps/install-from-stage.ps1: -------------------------------------------------------------------------------- 1 | param ($StageDir, $GameDir) 2 | 3 | Copy-Item -Path "${StageDir}/*" -Recurse -Force -Destination ${GameDir} 4 | -------------------------------------------------------------------------------- /vendor/semver/.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["paypal.me/Neargye", "btc.com/btc/address/bc1qzevldln8tqz5xf4lyufu9msgl7t97xstth9zq8", "etherscan.io/address/0xbb42fdef9204fa6f3d535d202b088dee35fcbd31"] 2 | liberapay: neargye 3 | -------------------------------------------------------------------------------- /vendor/semver/.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macos 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{matrix.config.os}} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | config: 12 | - { os: macos-10.15 } 13 | - { os: macos-11.0 } 14 | build: [Debug, Release] 15 | 16 | name: "${{matrix.config.os}}:${{matrix.build}}" 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Configure CMake 21 | run: cmake -DCMAKE_BUILD_TYPE=${{matrix.build}} 22 | 23 | - name: Build 24 | run: cmake --build . --config ${{matrix.build}} 25 | 26 | - name: Tests 27 | run: ctest --output-on-failure -C ${{matrix.build}} 28 | -------------------------------------------------------------------------------- /vendor/semver/.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: ubuntu 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ubuntu: 7 | runs-on: ubuntu-20.04 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | build: [Debug, Release] 12 | compiler: 13 | - { cc: "gcc-9", cxx: "g++-9" } 14 | - { cc: "gcc-10", cxx: "g++-10" } 15 | - { cc: "clang-10", cxx: "clang++-10" } 16 | - { cc: "clang-11", cxx: "clang++-11" } 17 | - { cc: "clang-12", cxx: "clang++-12" } 18 | 19 | name: "${{matrix.compiler.cxx}}:${{matrix.build}}" 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: Configure CMake 24 | run: cmake -DCMAKE_BUILD_TYPE=${{matrix.build}} -DCMAKE_C_COMPILER=${{matrix.compiler.cc}} -DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}} 25 | 26 | - name: Build 27 | run: cmake --build . --config ${{matrix.build}} 28 | 29 | - name: Tests 30 | run: ctest --output-on-failure -C ${{matrix.build}} 31 | -------------------------------------------------------------------------------- /vendor/semver/.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: windows 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{matrix.config.os}} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | config: 12 | - { os: windows-2016, vs: "Visual Studio 2017" } # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md#visual-studio-enterprise-2017 13 | - { os: windows-2019, vs: "Visual Studio 2019" } # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#visual-studio-enterprise-2019 14 | build: [Debug, Release] 15 | platform: [Win32, x64] 16 | 17 | name: "${{matrix.config.vs}}:${{matrix.platform}}:${{matrix.build}}" 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - name: Configure CMake 22 | run: cmake -A ${{matrix.platform}} 23 | 24 | - name: Build 25 | run: cmake --build . --config ${{matrix.build}} 26 | 27 | - name: Tests 28 | run: ctest --output-on-failure -C ${{matrix.build}} 29 | -------------------------------------------------------------------------------- /vendor/semver/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ 3 | .vs/ 4 | 5 | ### C++ gitignore ### 6 | # Prerequisites 7 | *.d 8 | 9 | # Compiled Object files 10 | *.slo 11 | *.lo 12 | *.o 13 | *.obj 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Compiled Dynamic libraries 20 | *.so 21 | *.dylib 22 | *.dll 23 | 24 | # Fortran module files 25 | *.mod 26 | *.smod 27 | 28 | # Compiled Static libraries 29 | *.lai 30 | *.la 31 | *.a 32 | *.lib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | 39 | ### CMake gitignore ### 40 | CMakeLists.txt.user 41 | CMakeCache.txt 42 | CMakeFiles 43 | CMakeScripts 44 | Testing 45 | Makefile 46 | cmake_install.cmake 47 | install_manifest.txt 48 | compile_commands.json 49 | CTestTestfile.cmake 50 | _deps 51 | -------------------------------------------------------------------------------- /vendor/semver/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 - 2021 Daniil Goncharov 4 | Copyright (c) 2020 - 2021 Alexander Gorbunov 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /vendor/semver/example/basic_example.cpp: -------------------------------------------------------------------------------- 1 | // Licensed under the MIT License . 2 | // SPDX-License-Identifier: MIT 3 | // Copyright (c) 2018 - 2021 Daniil Goncharov . 4 | // Copyright (c) 2020 - 2021 Alexander Gorbunov . 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #include 25 | 26 | #include 27 | 28 | using namespace semver; 29 | 30 | int main() { 31 | constexpr version v_default; 32 | static_assert(v_default == version(0, 1, 0, prerelease::none, 0)); 33 | std::cout << v_default << std::endl; // 0.1.0 34 | 35 | constexpr version v1{1, 4, 3}; 36 | constexpr version v2{"1.2.4-alpha.10"}; 37 | std::cout << v1 << std::endl; // 1.4.3 38 | std::cout << v2 << std::endl; // 1.2.4-alpha.10 39 | static_assert(v1 != v2); 40 | static_assert(!(v1 == v2)); 41 | static_assert(v1 > v2); 42 | static_assert(v1 >= v2); 43 | static_assert(!(v1 < v2)); 44 | static_assert(!(v1 <= v2)); 45 | 46 | version v_s; 47 | v_s.from_string("1.2.3-rc.1"); 48 | std::string s1 = v_s.to_string(); 49 | std::cout << s1 << std::endl; // 1.2.3-rc.1 50 | v_s.prerelease_number = 0; 51 | std::string s2 = v_s.to_string(); 52 | std::cout << s2 << std::endl; // 1.2.3-rc 53 | 54 | constexpr version vo = "2.0.0-rc.3"_version; 55 | std::cout << vo << std::endl; // 2.0.0-rc.3 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /vendor/semver/example/range_example.cpp: -------------------------------------------------------------------------------- 1 | // Licensed under the MIT License . 2 | // SPDX-License-Identifier: MIT 3 | // Copyright (c) 2020 - 2021 Daniil Goncharov . 4 | // Copyright (c) 2020 - 2021 Alexander Gorbunov . 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #include "semver.hpp" 25 | 26 | using namespace semver; 27 | 28 | int main() { 29 | constexpr std::string_view r1 = ">=1.2.7 <1.3.0"; 30 | static_assert(range::satisfies("1.2.7"_version, r1)); 31 | static_assert(range::satisfies("1.2.8"_version, r1)); 32 | static_assert(range::satisfies("1.2.99"_version, r1)); 33 | static_assert(!range::satisfies("1.2.6"_version, r1)); 34 | static_assert(!range::satisfies("1.3.0"_version, r1)); 35 | static_assert(!range::satisfies("1.1.0"_version, r1)); 36 | 37 | constexpr std::string_view r2 = "1.2.7 || >=1.2.9 <2.0.0"; 38 | static_assert(range::satisfies(version{1, 2, 7}, r2)); 39 | static_assert(range::satisfies({1, 2, 9}, r2)); 40 | static_assert(!range::satisfies("1.2.8"_version, r2)); 41 | static_assert(!range::satisfies("2.0.0"_version, r2)); 42 | 43 | // By default, we exclude prerelease tag from comparison. 44 | constexpr std::string_view r3 = ">1.2.3-alpha.3"; 45 | static_assert(range::satisfies("1.2.3-alpha.7"_version, r3)); 46 | static_assert(!range::satisfies("3.4.5-alpha.9"_version, r3)); 47 | 48 | // But we can suppress this behavior by passing semver::range::option::include_prerelease. 49 | // For details see: https://github.com/npm/node-semver#prerelease-tags 50 | static_assert(range::satisfies("3.4.5-alpha.9"_version, r3, range::satisfies_option::include_prerelease)); 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /vendor/semver/test/3rdparty/Catch2/LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /vendor/wil/.gitattributes: -------------------------------------------------------------------------------- 1 | # Disable CRLF-mapping for all files in the depot. 2 | * -text 3 | -------------------------------------------------------------------------------- /vendor/wil/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 2 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 3 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 4 | -------------------------------------------------------------------------------- /vendor/wil/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 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 | -------------------------------------------------------------------------------- /vendor/wil/ThirdPartyNotices.txt: -------------------------------------------------------------------------------- 1 | THIRD PARTY SOFTWARE NOTICES AND INFORMATION 2 | Do Not Translate or Localize 3 | 4 | This software incorporates material from third parties. Microsoft makes certain open source code available at http://3rdpartysource.microsoft.com, or you may send a check or money order for US $5.00, including the product name, the open source component name, and version number, to: 5 | 6 | Source Code Compliance Team 7 | Microsoft Corporation 8 | One Microsoft Way 9 | Redmond, WA 98052 10 | USA 11 | 12 | Notwithstanding any other terms, you may reverse engineer this software to the extent required to debug changes to any libraries licensed under the GNU Lesser General Public License. 13 | 14 | Libc++ 15 | 16 | Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in 26 | all copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 34 | THE SOFTWARE. 35 | 36 | Catch2 37 | 38 | Boost Software License - Version 1.0 - August 17th, 2003 39 | 40 | Permission is hereby granted, free of charge, to any person or organization 41 | obtaining a copy of the software and accompanying documentation covered by 42 | this license (the "Software") to use, reproduce, display, distribute, 43 | execute, and transmit the Software, and to prepare derivative works of the 44 | Software, and to permit third-parties to whom the Software is furnished to 45 | do so, all subject to the following: 46 | 47 | The copyright notices in the Software and this entire statement, including 48 | the above license grant, this restriction and the following disclaimer, 49 | must be included in all copies of the Software, in whole or in part, and 50 | all derivative works of the Software, unless such copies or derivative 51 | works are solely in the form of machine-executable object code generated by 52 | a source language processor. 53 | 54 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 55 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 56 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 57 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 58 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 59 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 60 | DEALINGS IN THE SOFTWARE. 61 | -------------------------------------------------------------------------------- /vendor/wil/cmake/common_build_flags.cmake: -------------------------------------------------------------------------------- 1 | 2 | # E.g. replace_cxx_flag("/W[0-4]", "/W4") 3 | macro(replace_cxx_flag pattern text) 4 | foreach (flag 5 | CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 6 | CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) 7 | 8 | string(REGEX REPLACE "${pattern}" "${text}" ${flag} "${${flag}}") 9 | 10 | endforeach() 11 | endmacro() 12 | 13 | macro(append_cxx_flag text) 14 | foreach (flag 15 | CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 16 | CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) 17 | 18 | string(APPEND ${flag} " ${text}") 19 | 20 | endforeach() 21 | endmacro() 22 | 23 | # Fixup default compiler settings 24 | 25 | # Be as strict as reasonably possible, since we want to support consumers using strict warning levels 26 | replace_cxx_flag("/W[0-4]" "/W4") 27 | append_cxx_flag("/WX") 28 | 29 | # We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl) 30 | append_cxx_flag("/permissive-") 31 | 32 | # wistd::function has padding due to alignment. This is expected 33 | append_cxx_flag("/wd4324") 34 | 35 | if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") 36 | # Ignore a few Clang warnings. We may want to revisit in the future to see if any of these can/should be removed 37 | append_cxx_flag("-Wno-switch") 38 | append_cxx_flag("-Wno-c++17-compat-mangling") 39 | append_cxx_flag("-Wno-missing-field-initializers") 40 | 41 | # For tests, we want to be able to test self assignment, so disable this warning 42 | append_cxx_flag("-Wno-self-assign-overloaded") 43 | append_cxx_flag("-Wno-self-move") 44 | 45 | # C++/WinRT does not declare 'override' in a number of places 46 | append_cxx_flag("-Wno-inconsistent-missing-override") 47 | 48 | # clang-cl does not understand the /permissive- flag (or at least it opts to ignore it). We can achieve similar 49 | # results through the following flags. 50 | append_cxx_flag("-fno-delayed-template-parsing") 51 | 52 | # clang-cl needs this to enable _InterlockedCompareExchange128 53 | append_cxx_flag("-mcx16") 54 | 55 | # NOTE: Windows headers not clean enough for us to realistically attempt to start fixing these errors yet. That 56 | # said, errors that originate from WIL headers may benefit 57 | # append_cxx_flag("-fno-ms-compatibility") 58 | # append_cxx_flag("-ferror-limit=999") 59 | # append_cxx_flag("-fmacro-backtrace-limit=0") 60 | # -fno-ms-compatibility turns off preprocessor compatability, which currently only works when __VA_OPT__ support is 61 | # available (i.e. >= C++20) 62 | # append_cxx_flag("-Xclang -std=c++2a") 63 | else() 64 | # Flags that are either ignored or unrecognized by clang-cl 65 | # TODO: https://github.com/Microsoft/wil/issues/6 66 | # append_cxx_flag("/experimental:preprocessor") 67 | 68 | # CRT headers are not yet /experimental:preprocessor clean, so work around the known issues 69 | # append_cxx_flag("/Wv:18") 70 | 71 | append_cxx_flag("/bigobj") 72 | 73 | # NOTE: Temporary workaround while https://github.com/microsoft/wil/issues/102 is being investigated 74 | append_cxx_flag("/d2FH4-") 75 | endif() 76 | -------------------------------------------------------------------------------- /vendor/wil/include/wil/cppwinrt_wrl.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_CPPWINRT_WRL_INCLUDED 12 | #define __WIL_CPPWINRT_WRL_INCLUDED 13 | 14 | #include "cppwinrt.h" 15 | #include 16 | 17 | #include "result_macros.h" 18 | #include 19 | 20 | // wil::wrl_factory_for_winrt_com_class provides interopability between a 21 | // C++/WinRT class and the WRL Module system, allowing the winrt class to be 22 | // CoCreatable. 23 | // 24 | // Usage: 25 | // - In your cpp, add: 26 | // CoCreatableCppWinRtClass(className) 27 | // 28 | // - In the dll.cpp (or equivalent) for the module containing your class, add: 29 | // CoCreatableClassWrlCreatorMapInclude(className) 30 | // 31 | namespace wil 32 | { 33 | namespace details 34 | { 35 | template 36 | class module_count_wrapper : public TCppWinRTClass 37 | { 38 | public: 39 | module_count_wrapper() 40 | { 41 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 42 | { 43 | modulePtr->IncrementObjectCount(); 44 | } 45 | } 46 | 47 | virtual ~module_count_wrapper() 48 | { 49 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 50 | { 51 | modulePtr->DecrementObjectCount(); 52 | } 53 | } 54 | }; 55 | } 56 | 57 | template 58 | class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<> 59 | { 60 | public: 61 | IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void **object) noexcept try 62 | { 63 | *object = nullptr; 64 | RETURN_HR_IF(CLASS_E_NOAGGREGATION, unknownOuter != nullptr); 65 | 66 | return winrt::make>().as(riid, object); 67 | } 68 | CATCH_RETURN() 69 | }; 70 | } 71 | 72 | #define CoCreatableCppWinRtClass(className) CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class) 73 | 74 | #endif // __WIL_CPPWINRT_WRL_INCLUDED 75 | -------------------------------------------------------------------------------- /vendor/wil/packaging/nuget/Microsoft.Windows.ImplementationLibrary.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.Windows.ImplementationLibrary 5 | 0.0.0 6 | Windows Implementation Library 7 | Microsoft 8 | false 9 | The Windows Implementation Libraries (wil) were created to improve productivity and solve problems commonly seen by Windows developers. 10 | windows utility wil native 11 | © Microsoft Corporation. All rights reserved. 12 | LICENSE 13 | https://github.com/Microsoft/wil 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /vendor/wil/packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories) 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /vendor/wil/scripts/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Windows Implementation Library Pipeline 2 | 3 | trigger: 4 | - master 5 | 6 | jobs: 7 | - job: BuildAndTest 8 | timeoutInMinutes: 360 9 | 10 | pool: 11 | vmImage: 'windows-2019' 12 | 13 | steps: 14 | - script: | 15 | choco install llvm 16 | if %ERRORLEVEL% NEQ 0 goto :eof 17 | echo ##vso[task.setvariable variable=PATH]%PATH%;C:\Program Files\LLVM\bin 18 | displayName: 'Install Clang' 19 | 20 | - script: | 21 | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat" 22 | if %ERRORLEVEL% NEQ 0 goto :eof 23 | 24 | call scripts\init_all.cmd --fast 25 | if %ERRORLEVEL% NEQ 0 goto :eof 26 | 27 | call scripts\build_all.cmd 28 | displayName: 'Build x86' 29 | 30 | - script: | 31 | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" 32 | if %ERRORLEVEL% NEQ 0 goto :eof 33 | 34 | call scripts\init_all.cmd --fast 35 | if %ERRORLEVEL% NEQ 0 goto :eof 36 | 37 | call scripts\build_all.cmd 38 | displayName: 'Build x64' 39 | 40 | - script: call scripts\runtests.cmd ~[LocalOnly] 41 | displayName: 'Run Tests' 42 | -------------------------------------------------------------------------------- /vendor/wil/scripts/build_all.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal EnableDelayedExpansion 3 | 4 | set BUILD_ROOT=%~dp0\..\build 5 | 6 | if "%Platform%"=="x64" ( 7 | set BUILD_ARCH=64 8 | ) else if "%Platform%"=="x86" ( 9 | set BUILD_ARCH=32 10 | ) else if [%Platform%]==[] ( 11 | echo ERROR: The build_all.cmd script must be run from a Visual Studio command window 12 | exit /B 1 13 | ) else ( 14 | echo ERROR: Unrecognized/unsupported platform %Platform% 15 | exit /B 1 16 | ) 17 | 18 | call :build clang debug 19 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 20 | call :build clang release 21 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 22 | call :build clang relwithdebinfo 23 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 24 | call :build clang minsizerel 25 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 26 | 27 | call :build msvc debug 28 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 29 | call :build msvc release 30 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 31 | call :build msvc relwithdebinfo 32 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 33 | call :build msvc minsizerel 34 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 35 | 36 | echo All build completed successfully! 37 | 38 | goto :eof 39 | 40 | :: build [compiler] [type] 41 | :build 42 | set BUILD_DIR=%BUILD_ROOT%\%1%BUILD_ARCH%%2 43 | if not exist %BUILD_DIR% ( 44 | goto :eof 45 | ) 46 | 47 | pushd %BUILD_DIR% 48 | echo Building from %CD% 49 | ninja 50 | popd 51 | goto :eof 52 | -------------------------------------------------------------------------------- /vendor/wil/scripts/init_all.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: NOTE: Architecture is picked up from the command window, so we can't control that here :( 4 | 5 | call %~dp0\init.cmd -c clang -g ninja -b debug %* 6 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 7 | call %~dp0\init.cmd -c clang -g ninja -b relwithdebinfo %* 8 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 9 | 10 | call %~dp0\init.cmd -c msvc -g ninja -b debug %* 11 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 12 | call %~dp0\init.cmd -c msvc -g ninja -b relwithdebinfo %* 13 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 14 | -------------------------------------------------------------------------------- /vendor/wil/scripts/runtests.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | setlocal EnableDelayedExpansion 4 | 5 | set TEST_ARGS=%* 6 | 7 | set BUILD_ROOT=%~dp0\..\build 8 | 9 | :: Unlike building, we don't need to limit ourselves to the Platform of the command window 10 | call :execute_tests clang64debug 11 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 12 | call :execute_tests clang64release 13 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 14 | call :execute_tests clang64relwithdebinfo 15 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 16 | call :execute_tests clang64minsizerel 17 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 18 | 19 | call :execute_tests clang32debug 20 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 21 | call :execute_tests clang32release 22 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 23 | call :execute_tests clang32relwithdebinfo 24 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 25 | call :execute_tests clang32minsizerel 26 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 27 | 28 | call :execute_tests msvc64debug 29 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 30 | call :execute_tests msvc64release 31 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 32 | call :execute_tests msvc64relwithdebinfo 33 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 34 | call :execute_tests msvc64minsizerel 35 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 36 | 37 | call :execute_tests msvc32debug 38 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 39 | call :execute_tests msvc32release 40 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 41 | call :execute_tests msvc32relwithdebinfo 42 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 43 | call :execute_tests msvc32minsizerel 44 | if %ERRORLEVEL% NEQ 0 ( goto :eof ) 45 | 46 | goto :eof 47 | 48 | :execute_tests 49 | set BUILD_DIR=%BUILD_ROOT%\%1 50 | if not exist %BUILD_DIR% ( goto :eof ) 51 | 52 | pushd %BUILD_DIR% 53 | echo Running tests from %CD% 54 | call :execute_test app witest.app.exe 55 | if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) 56 | call :execute_test cpplatest witest.cpplatest.exe 57 | if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) 58 | call :execute_test noexcept witest.noexcept.exe 59 | if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) 60 | call :execute_test normal witest.exe 61 | if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) 62 | call :execute_test win7 witest.win7.exe 63 | if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) 64 | popd 65 | 66 | goto :eof 67 | 68 | :execute_test 69 | if not exist tests\%1\%2 ( goto :eof ) 70 | echo Running %1 tests... 71 | tests\%1\%2 %TEST_ARGS% 72 | goto :eof 73 | -------------------------------------------------------------------------------- /vendor/wil/tests/CppWinRT20Tests.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Prior to C++/WinRT 2.0 this would cause issues since we're not including wil/cppwinrt.h in this translation unit. 3 | // However, since we're going to link into the same executable as 'CppWinRTTests.cpp', the 'winrt_to_hresult_handler' 4 | // global function pointer should be set, so these should all run successfully 5 | 6 | #include // Must be included before base.h 7 | 8 | #include 9 | #include 10 | 11 | #include "common.h" 12 | 13 | TEST_CASE("CppWinRTTests::CppWinRT20Test", "[cppwinrt]") 14 | { 15 | auto test = [](HRESULT hr) 16 | { 17 | try 18 | { 19 | THROW_HR(hr); 20 | } 21 | catch (...) 22 | { 23 | REQUIRE(hr == winrt::to_hresult()); 24 | } 25 | }; 26 | 27 | test(E_OUTOFMEMORY); 28 | test(E_INVALIDARG); 29 | test(E_UNEXPECTED); 30 | } 31 | -------------------------------------------------------------------------------- /vendor/wil/tests/WinVerifyTrustTest.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "common.h" 13 | 14 | #pragma comment(lib, "Wintrust.lib") 15 | 16 | TEST_CASE("WilWintrustWrapperTest::VerifyWintrustDataAllocateAndFree", "[resource][wintrust]") 17 | { 18 | wil::unique_wintrust_data uwvtData; 19 | uwvtData.cbStruct = sizeof(WINTRUST_DATA); 20 | DWORD zero = 0; 21 | REQUIRE(sizeof(WINTRUST_DATA) == uwvtData.cbStruct); 22 | 23 | uwvtData.reset(); 24 | REQUIRE(zero == uwvtData.cbStruct); 25 | } 26 | 27 | TEST_CASE("WilWintrustWrapperTest::VerifyUniqueHCATADMINAllocateAndFree", "[resource][wintrust]") 28 | { 29 | wil::unique_hcatadmin hCatAdmin; 30 | 31 | REQUIRE( 32 | CryptCATAdminAcquireContext2( 33 | hCatAdmin.addressof(), 34 | NULL, 35 | BCRYPT_SHA256_ALGORITHM, 36 | NULL, 37 | 0)); 38 | 39 | REQUIRE(hCatAdmin.get() != nullptr); 40 | hCatAdmin.reset(); 41 | REQUIRE(hCatAdmin.get() == nullptr); 42 | } 43 | 44 | #ifdef WIL_ENABLE_EXCEPTIONS 45 | TEST_CASE("WilWintrustWrapperTest::VerifyUnqiueHCATINFOAllocate", "[resource][wintrust]") 46 | { 47 | wil::shared_hcatadmin hCatAdmin; 48 | HCATINFO hCatInfo = nullptr; 49 | 50 | REQUIRE( 51 | CryptCATAdminAcquireContext2( 52 | hCatAdmin.addressof(), 53 | NULL, 54 | BCRYPT_SHA256_ALGORITHM, 55 | NULL, 56 | 0)); 57 | 58 | wil::unique_hcatinfo hCatInfoWrapper(hCatInfo, hCatAdmin); 59 | REQUIRE(hCatInfoWrapper.get() == nullptr); 60 | } 61 | #endif 62 | -------------------------------------------------------------------------------- /vendor/wil/tests/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma comment(lib, "Pathcch.lib") 3 | #pragma comment(lib, "RuntimeObject.lib") 4 | #pragma comment(lib, "Synchronization.lib") 5 | #pragma comment(lib, "RpcRt4.lib") 6 | 7 | #define CATCH_CONFIG_MAIN 8 | #include "catch.hpp" 9 | -------------------------------------------------------------------------------- /vendor/wil/tests/test_objects.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "catch.hpp" 4 | 5 | // Useful for validating that the copy constructor is never called (e.g. to validate perfect forwarding). Note that 6 | // the copy constructor/assignment operator are not deleted since we want to be able to validate in scenarios that 7 | // require CopyConstructible (e.g. for wistd::function) 8 | struct fail_on_copy 9 | { 10 | fail_on_copy() = default; 11 | 12 | fail_on_copy(const fail_on_copy&) 13 | { 14 | FAIL("Copy constructor invoked for fail_on_copy type"); 15 | } 16 | 17 | fail_on_copy(fail_on_copy&&) = default; 18 | 19 | fail_on_copy& operator=(const fail_on_copy&) 20 | { 21 | FAIL("Copy assignment operator invoked for fail_on_copy type"); 22 | return *this; 23 | } 24 | 25 | fail_on_copy& operator=(fail_on_copy&&) = default; 26 | }; 27 | 28 | // Useful for validating that objects get copied e.g. as opposed to capturing a reference 29 | struct value_holder 30 | { 31 | int value = 0xbadf00d; 32 | 33 | ~value_holder() 34 | { 35 | value = 0xbadf00d; 36 | } 37 | }; 38 | 39 | // Useful for validating that functions, etc. are callable with move-only types 40 | // Example real type that is move only is Microsoft::WRL::Wrappers::HString 41 | struct cannot_copy 42 | { 43 | cannot_copy() = default; 44 | cannot_copy(const cannot_copy&) = delete; 45 | cannot_copy& operator=(const cannot_copy&) = delete; 46 | 47 | cannot_copy(cannot_copy&&) = default; 48 | cannot_copy& operator=(cannot_copy&&) = default; 49 | }; 50 | 51 | // State for object_counter type. This has the unfortunate side effect that the object_counter type cannot be used in 52 | // contexts that require a default constructible type, but has the nice property that it allows for tests to run 53 | // concurrently 54 | struct object_counter_state 55 | { 56 | volatile LONG constructed_count = 0; 57 | volatile LONG destructed_count = 0; 58 | volatile LONG copy_count = 0; 59 | volatile LONG move_count = 0; 60 | 61 | LONG instance_count() 62 | { 63 | return constructed_count - destructed_count; 64 | } 65 | }; 66 | 67 | struct object_counter 68 | { 69 | object_counter_state* state; 70 | 71 | object_counter(object_counter_state& s) : 72 | state(&s) 73 | { 74 | ::InterlockedIncrement(&state->constructed_count); 75 | } 76 | 77 | object_counter(const object_counter& other) : 78 | state(other.state) 79 | { 80 | ::InterlockedIncrement(&state->constructed_count); 81 | ::InterlockedIncrement(&state->copy_count); 82 | } 83 | 84 | object_counter(object_counter&& other) WI_NOEXCEPT : 85 | state(other.state) 86 | { 87 | ::InterlockedIncrement(&state->constructed_count); 88 | ::InterlockedIncrement(&state->move_count); 89 | } 90 | 91 | ~object_counter() 92 | { 93 | ::InterlockedIncrement(&state->destructed_count); 94 | state = nullptr; 95 | } 96 | 97 | object_counter& operator=(const object_counter&) 98 | { 99 | ::InterlockedIncrement(&state->copy_count); 100 | return *this; 101 | } 102 | 103 | object_counter& operator=(object_counter&&) WI_NOEXCEPT 104 | { 105 | ::InterlockedIncrement(&state->move_count); 106 | return *this; 107 | } 108 | }; 109 | -------------------------------------------------------------------------------- /vendor/wil/tests/workarounds/readme.md: -------------------------------------------------------------------------------- 1 | 2 | We try and be as conformant as possible, but sometimes dependencies make that difficult. For example, WRL has had a number of conformance issues that keep getting uncovered. The files here are fixed up copies of those files and the include path is modified such that these directories appear first. 3 | -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | set_xmakever("2.5.9") 2 | 3 | set_project("RedHotTools") 4 | set_version("1.2.3", {build = "%y%m%d%H%M"}) 5 | 6 | set_arch("x64") 7 | set_languages("cxx2a") 8 | add_cxxflags("/MP /GR- /EHsc") 9 | 10 | if is_mode("debug") then 11 | set_symbols("debug") 12 | set_optimize("none") 13 | add_cxxflags("/Od /Ob0 /Zi /RTC1") 14 | elseif is_mode("release") then 15 | set_symbols("hidden") 16 | set_strip("all") 17 | set_optimize("fastest") 18 | add_cxxflags("/Ob2") 19 | elseif is_mode("releasedbg") then 20 | set_symbols("debug") 21 | set_strip("all") 22 | set_optimize("fastest") 23 | add_cxxflags("/Ob1 /Zi") 24 | end 25 | 26 | if is_mode("debug") then 27 | set_runtimes("MDd") 28 | else 29 | set_runtimes("MD") 30 | end 31 | 32 | add_requires("fmt", "hopscotch-map", "minhook", "spdlog", "tiltedcore") 33 | 34 | target("RedHotTools") 35 | set_default(true) 36 | set_kind("shared") 37 | set_filename("RedHotTools.dll") 38 | set_pcxxheader("src/pch.hpp") 39 | add_files("src/**.cpp", "src/**.rc", "lib/**.cpp") 40 | add_headerfiles("src/**.hpp", "lib/**.hpp", "vendor/filewatch/**.hpp") 41 | add_includedirs("src/", "lib/", "vendor/filewatch/") 42 | add_deps("RED4ext.SDK", "nameof", "semver", "wil") 43 | add_packages("fmt", "hopscotch-map", "minhook", "spdlog", "tiltedcore") 44 | add_syslinks("Version", "User32") 45 | add_defines("WINVER=0x0601", "WIN32_LEAN_AND_MEAN", "NOMINMAX", "_DISABLE_EXTENDED_ALIGNED_STORAGE") 46 | set_configdir(".") 47 | add_configfiles("config/Project.hpp.in", {prefixdir = "src/App"}) 48 | add_configfiles("config/Version.rc.in", {prefixdir = "src/App"}) 49 | add_configfiles("config/version.lua.in", {prefixdir = "support/cet"}) 50 | set_configvar("AUTHOR", "psiberx") 51 | set_configvar("NAME", "RedHotTools") 52 | 53 | target("RED4ext.SDK") 54 | set_default(false) 55 | set_kind("static") 56 | set_group("vendor") 57 | add_headerfiles("vendor/RED4ext.SDK/include/**.hpp") 58 | add_includedirs("vendor/RED4ext.SDK/include/", { public = true }) 59 | 60 | target("nameof") 61 | set_default(false) 62 | set_kind("static") 63 | set_group("vendor") 64 | add_headerfiles("vendor/nameof/include/**.hpp") 65 | add_includedirs("vendor/nameof/include/", { public = true }) 66 | 67 | target("semver") 68 | set_default(false) 69 | set_kind("static") 70 | set_group("vendor") 71 | add_headerfiles("vendor/semver/include/**.hpp") 72 | add_includedirs("vendor/semver/include/", { public = true }) 73 | 74 | target("wil") 75 | set_default(false) 76 | set_kind("static") 77 | set_group("vendor") 78 | add_headerfiles("vendor/wil/include/**.h") 79 | add_includedirs("vendor/wil/include/", { public = true }) 80 | 81 | add_rules("plugin.vsxmake.autoupdate") 82 | --------------------------------------------------------------------------------