diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 85f58b51..37bd65f8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,18 +37,6 @@ jobs: - name: Update xmake repository run: xmake.exe repo --update - # Fetch xmake dephash - #- name: Retrieve dependencies hash - # id: dep_hash - # run: echo "::set-output name=hash::$(xmake.exe dephash)" - - # Cache xmake dependencies - #- name: Retrieve cached xmake dependencies - # uses: actions/cache@v2 - # with: - # path: xmake-global\.xmake\packages - # key: ${{ runner.os }}-${{ matrix.arch }}-${{ matrix.mode }}-${{ steps.dep_hash.outputs.hash }} - # Setup compilation mode and install project dependencies - name: Configure xmake and install dependencies run: xmake.exe config --arch=${{ matrix.arch }} --mode=${{ matrix.mode }} --yes diff --git a/ThirdParty_LICENSES b/ThirdParty_LICENSES index 755d4975..c86a3fe3 100644 --- a/ThirdParty_LICENSES +++ b/ThirdParty_LICENSES @@ -467,4 +467,100 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +============ Noto Sans fonts ============ + +Copyright 2015-2021 Google LLC. All Rights Reserved. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/fonts/NotoSans-Regular.ttf b/fonts/NotoSans-Regular.ttf new file mode 100644 index 00000000..973bc2ed Binary files /dev/null and b/fonts/NotoSans-Regular.ttf differ diff --git a/fonts/NotoSansJP-Regular.otf b/fonts/NotoSansJP-Regular.otf new file mode 100644 index 00000000..776427dc Binary files /dev/null and b/fonts/NotoSansJP-Regular.otf differ diff --git a/fonts/NotoSansKR-Regular.otf b/fonts/NotoSansKR-Regular.otf new file mode 100644 index 00000000..7c5c2fae Binary files /dev/null and b/fonts/NotoSansKR-Regular.otf differ diff --git a/fonts/NotoSansMono-Regular.ttf b/fonts/NotoSansMono-Regular.ttf new file mode 100644 index 00000000..ff9389ae Binary files /dev/null and b/fonts/NotoSansMono-Regular.ttf differ diff --git a/fonts/NotoSansSC-Regular.otf b/fonts/NotoSansSC-Regular.otf new file mode 100644 index 00000000..d350ffa7 Binary files /dev/null and b/fonts/NotoSansSC-Regular.otf differ diff --git a/fonts/NotoSansTC-Regular.otf b/fonts/NotoSansTC-Regular.otf new file mode 100644 index 00000000..29724773 Binary files /dev/null and b/fonts/NotoSansTC-Regular.otf differ diff --git a/fonts/NotoSansThai-Regular.ttf b/fonts/NotoSansThai-Regular.ttf new file mode 100644 index 00000000..a6c4d1c9 Binary files /dev/null and b/fonts/NotoSansThai-Regular.ttf differ diff --git a/ida/patterns.py b/ida/patterns.py index 786969a5..acea29e0 100644 --- a/ida/patterns.py +++ b/ida/patterns.py @@ -28,13 +28,15 @@ def get_groups() -> List[Group]: # Add new patterns here, please try to keep the groups ordering alphabetized. return [ Group(name='CRenderGlobal', pointers=[ - Item(name='InstanceOffset', pattern='49 8B 95 ? ? ? ? 48 8D 44 24 30 0F 57 C0', expected=1, offset=3), + # instance offset is used by CRenderNode_Present_DoInternal + Item(name='InstanceOffset', pattern='48 89 5C 24 08 48 89 6C 24 18 48 89 74 24 20 57 41 56 41 57 48 83 EC 30 8B 01 41 8B F8 4C 8B 35', expected=1, offset=32), Item(name='_DoNotUse_RenderQueueOffset', pattern='49 39 29 0F 84 ? ? ? ? 41 39 69 24 0F 84 ? ? ? ? 49 8B 95', expected=1) ], functions=[ - Item(name='Resize', pattern='44 88 4C 24 20 44 89 44 24 18 89 54 24 10 89 4C', expected=1) + Item(name='Resize', pattern='44 88 4C 24 20 44 89 44 24 18 89 54 24 10 89 4C', expected=1) , + Item(name='Shutdown', pattern='48 89 6C 24 20 41 56 48 83 EC 20 48 8D 05 3E 85 99 00', expected=1) ]), Group(name='CRenderNode_Present', functions=[ - Item(name='DoInternal', pattern='48 89 5C 24 08 48 89 6C 24 18 48 89 74 24 20 57 41 56 41 57 48 83 EC 30 8B 01 41 8B F8 4C 8B 35', expected=1) + Item(name='DoInternal', pattern='48 89 5C 24 08 48 89 6C 24 18 48 89 74 24 20 57 41 56 41 57 48 83 EC 30 8B 01 41 8B F8 4C 8B 35', expected=1) ]), Group(name='CScript', functions=[ Item(name='RunPureScript', pattern='40 55 48 81 EC D0 00 00 00 48 8D 6C 24 40 8B', expected=1), @@ -44,6 +46,7 @@ def get_groups() -> List[Group]: Item(name='LogChannel', pattern='4C 8B DC 49 89 5B 08 49 89 73 18 57 48 83 EC 70 48 8B 02 ? ? ? ? ? ? ? FE 42 62 4D 8D 43 10 33 FF 45 33 C9 49 89 7B 10 48 8B DA 48 89 7A', expected=1), Item(name='TDBIDConstructorDerive', pattern='40 53 48 83 EC 30 33 C0 4C 89 44 24 20 48 8B DA', expected=1), Item(name='ProcessRunningState', pattern='40 53 48 83 EC 20 48 8B 0D ? ? ? ? 48 8B DA E8 ? ? ? ? 84 C0', expected=1), + Item(name='ProcessShutdownState', pattern='48 89 6C 24 18 56 48 83 EC 30 48 8B 0D ? ? ? ?', expected=1), Item(name='TranslateBytecode', pattern='4C 8B DC 55 53 57 41 55 49 8D 6B A1 48 81 EC 98 00 00 00 48 8B 1A 4C 8B E9 8B 42 0C 48 8D 3C C3'), Item(name='TweakDBLoad', pattern='48 89 5C 24 18 55 57 41 56 48 8B EC 48 83 EC 70 48 8B D9 45 33 F6 48 8D', expected=1), Item(name='RegisterMemberFunction', pattern='48 89 5C 24 08 57 48 83 EC 20 49 8B C1 4D 8B D0 44 8B 4C 24 58 48 8B DA 41 83 C9 03', expected=1) @@ -75,5 +78,8 @@ def get_groups() -> List[Group]: Group(name='CGame', functions=[ Item(name='Main', pattern='40 57 48 83 EC 70 48 8B F9 0F 29 7C 24 50 48 8D 4C 24 38', expected=1) ]), + Group(name='PlayerSystem', functions=[ + Item(name='OnPlayerSpawned', pattern='48 8B C4 4C 89 48 20 55 56 57 48 8B EC 48 81 EC 80 00 00 00 44 8B 15 25 60 54 02 48 8B F1', expected=1) + ]), ] diff --git a/resources/archivehashes.zip b/resources/archivehashes.zip deleted file mode 100644 index 20568fff..00000000 Binary files a/resources/archivehashes.zip and /dev/null differ diff --git a/scripts/autoexec.lua b/scripts/autoexec.lua deleted file mode 100644 index 107fc17d..00000000 --- a/scripts/autoexec.lua +++ /dev/null @@ -1,3 +0,0 @@ -json = require 'json/json' - -print("Cyber Engine Tweaks startup complete.") \ No newline at end of file diff --git a/src/CET.cpp b/src/CET.cpp index 7b9100d6..03fd52e5 100644 --- a/src/CET.cpp +++ b/src/CET.cpp @@ -20,6 +20,8 @@ void CET::Shutdown() CET& CET::Get() { + // we should always call this after initialization, never before! + assert(s_pInstance); return *s_pInstance; } @@ -61,21 +63,15 @@ bool CET::IsRunning() noexcept CET::CET() : m_options(m_paths) , m_bindings(m_paths, m_options) - , m_window(&m_overlay, &m_bindings, &m_d3d12) + , m_window(&m_bindings, &m_d3d12) , m_d3d12(m_window, m_paths, m_options) - , m_vm(m_paths, m_bindings, m_d3d12, m_options) + , m_vm(m_paths, m_bindings, m_d3d12) , m_overlay(m_d3d12, m_bindings, m_options, m_vm) - , m_tasks(m_options) { - m_bindings.ConnectUpdate(m_d3d12); - m_vm.Initialize(); } CET::~CET() { s_isRunning = false; - - m_bindings.DisconnectUpdate(m_d3d12); - m_bindings.Clear(); } diff --git a/src/Image.cpp b/src/Image.cpp index 74062bc8..d91242ef 100644 --- a/src/Image.cpp +++ b/src/Image.cpp @@ -43,7 +43,7 @@ void Image::Initialize() auto* pImage = GetModuleHandle(nullptr); - auto sectionHeaders = mainModule.section_headers(); + const auto sectionHeaders = mainModule.section_headers(); base_address = reinterpret_cast(pImage); @@ -58,14 +58,14 @@ void Image::Initialize() auto& dosHeader = mainModule.dos_header(); auto* pFileHeader = reinterpret_cast(base_address + dosHeader.e_lfanew + 4); - auto* pOptionalHeader = reinterpret_cast(((char*)pFileHeader) + sizeof(IMAGE_FILE_HEADER)); - auto* pDataDirectory = &pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; - auto* pDebugDirectory = reinterpret_cast(base_address + pDataDirectory->VirtualAddress); + const auto* pOptionalHeader = reinterpret_cast(reinterpret_cast(pFileHeader) + sizeof(IMAGE_FILE_HEADER)); + const auto* pDataDirectory = &pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; + const auto* pDebugDirectory = reinterpret_cast(base_address + pDataDirectory->VirtualAddress); // Check to see that the data has the right type if (IMAGE_DEBUG_TYPE_CODEVIEW == pDebugDirectory->Type) { - PdbInfo* pdb_info = (PdbInfo*)(base_address + pDebugDirectory->AddressOfRawData); + auto* pdb_info = reinterpret_cast(base_address + pDebugDirectory->AddressOfRawData); if (0 == memcmp(&pdb_info->Signature, "RSDS", 4)) { for (const auto& v : cVersions) @@ -79,8 +79,8 @@ void Image::Initialize() if (version == 0) { - for (auto c : pdb_info->Guid) - Log::Error("{:X}", (uint32_t)c); + for (const auto c : pdb_info->Guid) + Log::Error("{:X}", static_cast(c)); throw std::runtime_error("Abort loading"); } diff --git a/src/Image.h b/src/Image.h index 718cc30d..5c52adc8 100644 --- a/src/Image.h +++ b/src/Image.h @@ -9,9 +9,9 @@ struct Image return std::make_tuple(1, 60); } - static uint64_t MakeVersion(uint32_t aMajor, uint16_t aMinor) noexcept + static uint64_t MakeVersion(const uint32_t acMajor, const uint16_t acMinor) noexcept { - return static_cast(aMajor) << 32 | static_cast(aMinor) << 16; + return static_cast(acMajor) << 32 | static_cast(acMinor) << 16; } [[nodiscard]] std::tuple GetVersion() const noexcept diff --git a/src/Options.cpp b/src/Options.cpp index f140f9bd..f91d7a6c 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -5,12 +5,13 @@ void Options::Load() { - if (exists(m_paths.Config())) + const auto path = GetAbsolutePath(m_paths.Config(), "", false); + if (!path.empty()) { - std::ifstream configFile(m_paths.Config()); + std::ifstream configFile(path); if(configFile) { - auto config = nlohmann::json::parse(configFile); + const auto config = nlohmann::json::parse(configFile); PatchRemovePedestrians = config.value("remove_pedestrians", PatchRemovePedestrians); PatchSkipStartMenu = config.value("skip_start_menu", PatchSkipStartMenu); PatchAmdSmt = config.value("amd_smt", PatchAmdSmt); @@ -23,7 +24,7 @@ void Options::Load() PatchMinimapFlicker = config.value("minimap_flicker", PatchMinimapFlicker); RemoveDeadBindings = config.value("cetdev_remove_dead_bindings", RemoveDeadBindings); - EnableImGuiAssertions = config.value("cetdev_enable_imgui_assertions", EnableImGuiAssertions); + EnableImGuiAssertionsLogging = config.value("cetdev_enable_imgui_assertions_logging", EnableImGuiAssertionsLogging); DumpGameOptions = config.value("dump_game_options", DumpGameOptions); PatchEnableDebug = config.value("enable_debug", PatchEnableDebug); @@ -40,7 +41,7 @@ void Options::Load() } // set global "Enable ImGui Assertions" - g_ImGuiAssertionsEnabled = EnableImGuiAssertions; + g_ImGuiAssertionsEnabled = EnableImGuiAssertionsLogging; } void Options::Save() @@ -59,7 +60,7 @@ void Options::Save() config["minimap_flicker"] = PatchMinimapFlicker; config["cetdev_remove_dead_bindings"] = RemoveDeadBindings; - config["cetdev_enable_imgui_assertions"] = EnableImGuiAssertions; + config["cetdev_enable_imgui_assertions_logging"] = EnableImGuiAssertionsLogging; config["enable_debug"] = PatchEnableDebug; config["dump_game_options"] = DumpGameOptions; @@ -67,11 +68,12 @@ void Options::Save() config["font_glyph_ranges"] = FontGlyphRanges; config["font_size"] = FontSize; - std::ofstream o(m_paths.Config()); + const auto path = GetAbsolutePath(m_paths.Config(), "", true); + std::ofstream o(path); o << config.dump(4) << std::endl; // set global "Enable ImGui Assertions" - g_ImGuiAssertionsEnabled = EnableImGuiAssertions; + g_ImGuiAssertionsEnabled = EnableImGuiAssertionsLogging; } void Options::ResetToDefaults() @@ -88,10 +90,14 @@ void Options::ResetToDefaults() PatchMinimapFlicker = false; RemoveDeadBindings = true; - EnableImGuiAssertions = false; + EnableImGuiAssertionsLogging = false; PatchEnableDebug = false; DumpGameOptions = false; + FontPath = ""; + FontGlyphRanges = "Default"; + FontSize = 18.0f; + Save(); } @@ -99,10 +105,10 @@ Options::Options(Paths& aPaths) : m_paths(aPaths) { const auto* exePathStr = aPaths.Executable().native().c_str(); - int verInfoSz = GetFileVersionInfoSize(exePathStr, nullptr); + const auto verInfoSz = GetFileVersionInfoSize(exePathStr, nullptr); if(verInfoSz) { - auto verInfo = std::make_unique(verInfoSz); + const auto verInfo = std::make_unique(verInfoSz); if(GetFileVersionInfo(exePathStr, 0, verInfoSz, verInfo.get())) { struct @@ -131,13 +137,12 @@ Options::Options(Paths& aPaths) } } // check if exe name matches in case previous check fails - ExeValid = ExeValid || (aPaths.Executable().filename() == "Cyberpunk2077.exe"); + ExeValid = ExeValid || aPaths.Executable().filename() == "Cyberpunk2077.exe"; if (!ExeValid) throw std::runtime_error("Not Cyberpunk2077.exe"); - set_default_logger(CreateLogger(m_paths.CETRoot() / "cyber_engine_tweaks.log", "main")); - + set_default_logger(CreateLogger(GetAbsolutePath(L"cyber_engine_tweaks.log", m_paths.CETRoot(), true), "main", nullptr, "[%Y-%m-%d %H:%M:%S UTC%z] [%l] [%!] [%t] %v")); Log::Info("Cyber Engine Tweaks is starting..."); @@ -148,13 +153,13 @@ Options::Options(Paths& aPaths) Log::Info("CET version {} [{}]", CET_BUILD_COMMIT, CET_BUILD_BRANCH); auto [major, minor] = GameImage.GetVersion(); Log::Info("Game version {}.{:02d}", major, minor); - Log::Info("Root path: \"{}\"", aPaths.GameRoot().string()); - Log::Info("Cyber Engine Tweaks path: \"{}\"", aPaths.CETRoot().string()); - Log::Info("Lua scripts search path: \"{}\"", aPaths.ModsRoot().string()); + Log::Info("Root path: \"{}\"", UTF16ToUTF8(aPaths.GameRoot().native())); + Log::Info("Cyber Engine Tweaks path: \"{}\"", UTF16ToUTF8(aPaths.CETRoot().native())); + Log::Info("Lua scripts search path: \"{}\"", UTF16ToUTF8(aPaths.ModsRoot().native())); - if (GameImage.GetVersion() != GameImage.GetSupportedVersion()) + if (GameImage.GetVersion() != Image::GetSupportedVersion()) { - auto [smajor, sminor] = GameImage.GetSupportedVersion(); + const auto [smajor, sminor] = Image::GetSupportedVersion(); Log::Error("Unsupported game version! Only {}.{:02d} is supported.", smajor, sminor); throw std::runtime_error("Unsupported version"); } diff --git a/src/Options.h b/src/Options.h index 4ccd67f2..424373fc 100644 --- a/src/Options.h +++ b/src/Options.h @@ -8,11 +8,11 @@ struct Options { Options(Paths& aPaths); ~Options() = default; - + void Load(); void Save(); - void ResetToDefaults(); - + void ResetToDefaults(); + Image GameImage; bool PatchEnableDebug{ false }; bool PatchRemovePedestrians{ false }; @@ -26,14 +26,13 @@ struct Options bool PatchDisableWin7Vsync{ false }; bool PatchMinimapFlicker{ false }; bool DumpGameOptions{ false }; - std::string FontPath; - std::string FontGlyphRanges{""}; - float FontSize{ 13.0f }; + std::string FontPath{ }; + std::string FontGlyphRanges{"Default"}; + float FontSize{ 18.0f }; bool ExeValid{ false }; - bool IsFirstLaunch { true }; - bool RemoveDeadBindings { true }; - bool DrawImGuiDiagnosticWindow { false }; - bool EnableImGuiAssertions { false }; + bool RemoveDeadBindings{ true }; + bool DrawImGuiDiagnosticWindow{ false }; + bool EnableImGuiAssertionsLogging{ false }; private: diff --git a/src/Paths.cpp b/src/Paths.cpp index c4b30ac1..3fd0b276 100644 --- a/src/Paths.cpp +++ b/src/Paths.cpp @@ -1,38 +1,33 @@ #include -const std::filesystem::path& Paths::Executable() const -{ - return m_exe; -} - -const std::filesystem::path& Paths::GameRoot() const -{ - return m_gameRoot; +const std::filesystem::path& Paths::Executable() const +{ + return m_exe; } -const std::filesystem::path& Paths::CETRoot() const -{ - return m_cetRoot; +const std::filesystem::path& Paths::GameRoot() const +{ + return m_gameRoot; } -const std::filesystem::path& Paths::Config() const -{ - return m_config; +const std::filesystem::path& Paths::CETRoot() const +{ + return m_cetRoot; } -const std::filesystem::path& Paths::VKBindings() const -{ - return m_vkBindings; +const std::filesystem::path& Paths::Config() const +{ + return m_config; } -const std::filesystem::path& Paths::ModsRoot() const -{ - return m_modsRoot; +const std::filesystem::path& Paths::VKBindings() const +{ + return m_vkBindings; } -const std::filesystem::path& Paths::ArchiveModsRoot() const -{ - return m_archiveModsRoot; +const std::filesystem::path& Paths::ModsRoot() const +{ + return m_modsRoot; } const std::filesystem::path& Paths::R6CacheModdedRoot() const @@ -40,43 +35,50 @@ const std::filesystem::path& Paths::R6CacheModdedRoot() const return m_r6CacheModdedRoot; } +const std::filesystem::path& Paths::Fonts() const +{ + return m_fonts; +} + +const std::filesystem::path& Paths::TweakDB() const +{ + return m_tweakdb; +} + Paths::Paths() { TCHAR exePathBuf[MAX_PATH] = { 0 }; - GetModuleFileName(GetModuleHandle(nullptr), exePathBuf, std::size(exePathBuf)); + GetModuleFileName(GetModuleHandle(nullptr), exePathBuf, static_cast(std::size(exePathBuf))); m_exe = exePathBuf; m_gameRoot = m_exe.parent_path(); m_cetRoot = m_gameRoot; - m_cetRoot /= "plugins"; - m_cetRoot /= "cyber_engine_tweaks"; + m_cetRoot /= L"plugins"; + m_cetRoot /= L"cyber_engine_tweaks"; create_directories(m_cetRoot); - - m_config = m_cetRoot / "config.json"; + + m_config = m_cetRoot / L"config.json"; // remove empty config.json if (exists(m_config) && !file_size(m_config)) std::filesystem::remove(m_config); - m_vkBindings = m_cetRoot / "bindings.json"; + m_vkBindings = m_cetRoot / L"bindings.json"; // remove empty vkbindings.json if (exists(m_vkBindings) && !file_size(m_vkBindings)) std::filesystem::remove(m_vkBindings); - m_modsRoot = m_cetRoot / "mods"; + m_modsRoot = m_cetRoot / L"mods"; create_directories(m_modsRoot); - m_archiveModsRoot = m_gameRoot; - m_archiveModsRoot /= ".."; - m_archiveModsRoot /= ".."; - m_archiveModsRoot /= "archive"; - m_archiveModsRoot /= "pc"; - m_archiveModsRoot /= "mod"; - m_r6CacheModdedRoot = m_gameRoot; - m_r6CacheModdedRoot /= ".."; - m_r6CacheModdedRoot /= ".."; - m_r6CacheModdedRoot /= "r6"; - m_r6CacheModdedRoot /= "cache"; - m_r6CacheModdedRoot /= "modded"; + m_r6CacheModdedRoot /= L".."; + m_r6CacheModdedRoot /= L".."; + m_r6CacheModdedRoot /= L"r6"; + m_r6CacheModdedRoot /= L"cache"; + m_r6CacheModdedRoot /= L"modded"; + + m_fonts = m_cetRoot / L"fonts"; + + m_tweakdb = m_cetRoot / L"tweakdb"; } diff --git a/src/Paths.h b/src/Paths.h index 389d97ff..c7b48533 100644 --- a/src/Paths.h +++ b/src/Paths.h @@ -10,21 +10,23 @@ struct Paths const std::filesystem::path& Config() const; const std::filesystem::path& VKBindings() const; const std::filesystem::path& ModsRoot() const; - const std::filesystem::path& ArchiveModsRoot() const; const std::filesystem::path& R6CacheModdedRoot() const; + const std::filesystem::path& Fonts() const; + const std::filesystem::path& TweakDB() const; private: friend struct CET; Paths(); - - std::filesystem::path m_exe{ }; - std::filesystem::path m_gameRoot{ }; - std::filesystem::path m_cetRoot{ }; - std::filesystem::path m_config{ }; - std::filesystem::path m_vkBindings{ }; - std::filesystem::path m_modsRoot{ }; - std::filesystem::path m_archiveModsRoot{ }; + + std::filesystem::path m_exe{}; + std::filesystem::path m_gameRoot{}; + std::filesystem::path m_cetRoot{}; + std::filesystem::path m_config{}; + std::filesystem::path m_vkBindings{}; + std::filesystem::path m_modsRoot{}; std::filesystem::path m_r6CacheModdedRoot{}; + std::filesystem::path m_fonts{}; + std::filesystem::path m_tweakdb{}; }; \ No newline at end of file diff --git a/src/Utils.cpp b/src/Utils.cpp index e40d9b6d..d80ed569 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -2,21 +2,16 @@ #include "Utils.h" -#include #include void ltrim(std::string& s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { - return !std::isspace(ch); - })); + s.erase(s.begin(), std::ranges::find_if(s, [](unsigned char ch) { return !std::isspace(ch); })); } void rtrim(std::string& s) { - s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { - return !std::isspace(ch); - }).base(), s.end()); + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end()); } void trim(std::string& s) @@ -25,22 +20,51 @@ void trim(std::string& s) rtrim(s); } -template -class CustomSink : public spdlog::sinks::base_sink +std::string UTF16ToUTF8(std::wstring_view utf16) +{ + const auto utf16Length = static_cast(utf16.length()); + const auto utf8Length = WideCharToMultiByte(CP_UTF8, 0, utf16.data(), utf16Length, nullptr, 0, nullptr, nullptr); + if (!utf8Length) + return {}; + + std::string utf8; + utf8.resize(utf8Length); + WideCharToMultiByte(CP_UTF8, 0, utf16.data(), utf16Length, utf8.data(), utf8Length, nullptr, nullptr); + + return utf8; +} + +std::wstring UTF8ToUTF16(std::string_view utf8) +{ + const auto utf8Length = static_cast(utf8.length()); + const auto utf16Length = MultiByteToWideChar(CP_UTF8, 0, utf8.data(), utf8Length, nullptr, 0); + if (!utf16Length) + return {}; + + std::wstring utf16; + utf16.resize(utf16Length); + MultiByteToWideChar(CP_UTF8, 0, utf8.data(), utf8Length, utf16.data(), utf16Length); + + return utf16; +} + +template +class CustomSink final : public spdlog::sinks::base_sink { public: - CustomSink(std::function aSinkItHandler, std::function aFlushHandler) + CustomSink(const std::function& acpSinkItHandler, const std::function& acpFlushHandler) : spdlog::sinks::base_sink() - , m_sinkItHandler(aSinkItHandler) - , m_flushHandler(aFlushHandler) - {} + , m_sinkItHandler(acpSinkItHandler) + , m_flushHandler(acpFlushHandler) + { + } protected: void sink_it_(const spdlog::details::log_msg& msg) override { if (!m_sinkItHandler) return; - + spdlog::memory_buf_t formatted; spdlog::sinks::base_sink::formatter_->format(msg, formatted); m_sinkItHandler(fmt::to_string(formatted)); @@ -53,44 +77,49 @@ class CustomSink : public spdlog::sinks::base_sink } private: - - std::function m_sinkItHandler{ nullptr }; - std::function m_flushHandler{ nullptr }; + std::function m_sinkItHandler{nullptr}; + std::function m_flushHandler{nullptr}; }; -template -spdlog::sink_ptr CreateCustomSink(std::function aSinkItHandler, std::function aFlushHandler) +template +spdlog::sink_ptr CreateCustomSink(const std::function& acpSinkItHandler, + const std::function& acpFlushHandler) { - return std::make_shared>(aSinkItHandler, aFlushHandler); + return std::make_shared>(acpSinkItHandler, acpFlushHandler); } -spdlog::sink_ptr CreateCustomSinkST(std::function aSinkItHandler, std::function aFlushHandler) +spdlog::sink_ptr CreateCustomSinkST(const std::function& acpSinkItHandler, + const std::function& acpFlushHandler) { - return CreateCustomSink(aSinkItHandler, aFlushHandler); + return CreateCustomSink(acpSinkItHandler, acpFlushHandler); } -spdlog::sink_ptr CreateCustomSinkMT(std::function aSinkItHandler, std::function aFlushHandler) +spdlog::sink_ptr CreateCustomSinkMT(const std::function& acpSinkItHandler, + const std::function& acpFlushHandler) { - return CreateCustomSink(aSinkItHandler, aFlushHandler); + return CreateCustomSink(acpSinkItHandler, acpFlushHandler); } -std::shared_ptr CreateLogger(const std::filesystem::path& aPath, const std::string& aID, spdlog::sink_ptr aExtraSink, const std::string& aPattern) +std::shared_ptr CreateLogger(const std::filesystem::path& acpPath, const std::string& acpID, + const spdlog::sink_ptr& acpExtraSink, const std::string& acpPattern, + const size_t acMaxFileSize, const size_t acMaxFileCount) { - auto existingLogger = spdlog::get(aID); - if (existingLogger) + if (auto existingLogger = spdlog::get(acpID)) return existingLogger; - const auto rotSink = std::make_shared(aPath.string(), 1048576 * 5, 3); - rotSink->set_pattern(aPattern); - auto logger = std::make_shared(aID, spdlog::sinks_init_list{ rotSink }); + const auto rotSink = std::make_shared(acpPath.native(), acMaxFileSize, acMaxFileCount); + rotSink->set_pattern(acpPattern); + + auto logger = std::make_shared(acpID, spdlog::sinks_init_list{rotSink}); + logger->set_level(spdlog::level::level_enum::trace); - if (aExtraSink) - logger->sinks().emplace_back(aExtraSink); + if (acpExtraSink) + logger->sinks().emplace_back(acpExtraSink); #ifdef CET_DEBUG logger->flush_on(spdlog::level::trace); #else - logger->flush_on(spdlog::level::err); + logger->flush_on(spdlog::level::warn); #endif register_logger(logger); @@ -98,25 +127,25 @@ std::shared_ptr CreateLogger(const std::filesystem::path& aPath, } // deep copies sol object (doesnt take into account potential duplicates) -sol::object DeepCopySolObject(sol::object aObj, const sol::state_view& aStateView) -{ - if ((aObj == sol::nil) || (aObj.get_type() != sol::type::table)) - return aObj; - sol::table src{aObj.as()}; - sol::table copy{aStateView, sol::create}; - for (auto kv : src) - copy[DeepCopySolObject(kv.first, aStateView)] = DeepCopySolObject(kv.second, aStateView); +sol::object DeepCopySolObject(const sol::object& acpObj, const sol::state_view& acpStateView) +{ + if (acpObj == sol::nil || acpObj.get_type() != sol::type::table) + return acpObj; + sol::table src = acpObj; + sol::table copy{acpStateView, sol::create}; + for (const auto& kv : src) + copy[DeepCopySolObject(kv.first, acpStateView)] = DeepCopySolObject(kv.second, acpStateView); copy[sol::metatable_key] = src[sol::metatable_key]; return copy; } // makes sol usertype or userdata immutable when accessed from lua -void MakeSolUsertypeImmutable(sol::object aObj, const sol::state_view& aStateView) +void MakeSolUsertypeImmutable(const sol::object& acpObj, const sol::state_view& acpStateView) { - if (!aObj.is() && !aObj.is()) + if (!acpObj.is() && !acpObj.is()) return; - sol::table target = aObj; + sol::table target = acpObj; sol::table metatable; sol::object metaref = target[sol::metatable_key]; @@ -126,23 +155,187 @@ void MakeSolUsertypeImmutable(sol::object aObj, const sol::state_view& aStateVie } else { - metatable = {aStateView, sol::create}; + metatable = {acpStateView, sol::create}; target[sol::metatable_key] = metatable; } // prevent adding new properties - metatable[sol::meta_function::new_index] = []() {}; + metatable[sol::meta_function::new_index] = [] {}; // prevent overriding metatable - metatable[sol::meta_function::metatable] = []() { return sol::nil; }; + metatable[sol::meta_function::metatable] = [] { return sol::nil; }; } // Check if Lua object is of cdata type -bool IsLuaCData(sol::object aObject) +bool IsLuaCData(const sol::object& acpObject) { // Sol doesn't have enum for LuaJIT's cdata type since it's not a standard type. // But it's possible to check the type using numeric code (10). // LuaJIT packs int64/uint64 into cdata and some other types. // Since we're not using other types, this should be enough to check for int64/uint64 value. - return (static_cast(aObject.get_type()) == 10); + return static_cast(acpObject.get_type()) == 10; +} + +float GetAlignedItemWidth(const int64_t acItemsCount) +{ + return (ImGui::GetWindowContentRegionWidth() - static_cast(acItemsCount - 1) * ImGui::GetStyle().ItemSpacing.x) / static_cast(acItemsCount); +} + +float GetCenteredOffsetForText(const char* acpText) +{ + return (ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(acpText).x) / 2.0f; +} + +TChangedCBResult UnsavedChangesPopup(const std::string& acpOwnerName, bool& aFirstTime, const bool acMadeChanges, const TWidgetCB& acpSaveCB, const TWidgetCB& acpLoadCB, const TWidgetCB& acpCancelCB) +{ + auto popupTitle = acpOwnerName.empty() ? "Unsaved changes" : fmt::format("{} - Unsaved changes", acpOwnerName); + + if (acMadeChanges) + { + auto res = TChangedCBResult::CHANGED; + if (aFirstTime) + { + + ImGui::OpenPopup(popupTitle.c_str()); + aFirstTime = false; + } + + if (ImGui::BeginPopupModal(popupTitle.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) + { + const auto shorterTextSz { ImGui::CalcTextSize("You have some unsaved changes.").x }; + const auto longerTextSz { ImGui::CalcTextSize("Do you wish to apply them or discard them?").x }; + const auto diffTextSz { longerTextSz - shorterTextSz }; + + ImGui::SetCursorPosX(diffTextSz / 2); + ImGui::TextUnformatted("You have some unsaved changes."); + ImGui::TextUnformatted("Do you wish to apply them or discard them?"); + ImGui::Separator(); + + const auto itemWidth = GetAlignedItemWidth(3); + + if (ImGui::Button("Apply", ImVec2(itemWidth, 0))) + { + if (acpSaveCB) + acpSaveCB(); + res = TChangedCBResult::APPLY; + aFirstTime = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Discard", ImVec2(itemWidth, 0))) + { + if (acpLoadCB) + acpLoadCB(); + res = TChangedCBResult::DISCARD; + aFirstTime = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(itemWidth, 0))) + { + if (acpCancelCB) + acpCancelCB(); + res = TChangedCBResult::CANCEL; + aFirstTime = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SetItemDefaultFocus(); + + ImGui::EndPopup(); + } + return res; + } + return TChangedCBResult::APPLY; // no changes, same as if we were to Apply +} + +std::filesystem::path GetAbsolutePath(const std::string& acFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting, const bool acAllowSymlink) +{ + return GetAbsolutePath(UTF8ToUTF16(acFilePath), acRootPath, acAllowNonExisting, acAllowSymlink); +} + +std::filesystem::path GetAbsolutePath(std::filesystem::path aFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting, const bool acAllowSymlink) +{ + assert(!aFilePath.empty()); + if (aFilePath.empty()) + return {}; + + aFilePath.make_preferred(); + + if (aFilePath.is_relative()) + { + if (!acRootPath.empty()) + aFilePath = acRootPath / aFilePath; + + aFilePath = absolute(aFilePath); + } + + if (!exists(aFilePath)) + { + if (!acAllowNonExisting) + return {}; + } + else if (is_symlink(aFilePath)) + { + if (acAllowSymlink) + return absolute(read_symlink(aFilePath)); + + return {}; + } + + return aFilePath; +} + +std::filesystem::path GetLuaPath(const std::string& acFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting) +{ + return GetLuaPath(UTF8ToUTF16(acFilePath), acRootPath, acAllowNonExisting); +} + +std::filesystem::path GetLuaPath(std::filesystem::path aFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting) +{ + assert(!aFilePath.empty()); + assert(!aFilePath.is_absolute()); + + if (aFilePath.empty() || aFilePath.is_absolute()) + return {}; + + aFilePath.make_preferred(); + + if (aFilePath.native().starts_with(L"..\\")) + return {}; + + aFilePath = GetAbsolutePath(aFilePath, acRootPath, acAllowNonExisting, false); + if (aFilePath.empty()) + return {}; + + const auto relativeFilePathToRoot = relative(aFilePath, acRootPath); + if (relativeFilePathToRoot.native().starts_with(L"..\\") || relativeFilePathToRoot.native().find(L"\\..\\") != std::wstring::npos) + return {}; + + return relative(aFilePath, std::filesystem::current_path()); +} + +std::vector ReadWholeBinaryFile(const std::filesystem::path& acpPath) +{ + if (acpPath.empty() || !exists(acpPath)) + return {}; + + std::ifstream file(acpPath, std::ios::binary); + file.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit); + std::vector bytes(std::istreambuf_iterator{file}, {}); + file.close(); + + return bytes; +} + +std::string ReadWholeTextFile(const std::filesystem::path& acpPath) +{ + if (acpPath.empty() || !exists(acpPath)) + return {}; + + std::ifstream file(acpPath, std::ios::binary); + file.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit); + std::string lines(std::istreambuf_iterator{file}, {}); + file.close(); + + return lines; } diff --git a/src/Utils.h b/src/Utils.h index f152a026..8bd13fea 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -1,18 +1,23 @@ #pragma once +#include + void ltrim(std::string& s); void rtrim(std::string& s); void trim(std::string& s); -spdlog::sink_ptr CreateCustomSinkST(std::function aSinkItHandler, std::function aFlushHandler = nullptr); -spdlog::sink_ptr CreateCustomSinkMT(std::function aSinkItHandler, std::function aFlushHandler = nullptr); -std::shared_ptr CreateLogger(const std::filesystem::path& aPath, const std::string& aID, spdlog::sink_ptr aExtraSink = nullptr, const std::string& aPattern = "[%Y-%m-%d %H:%M:%S UTC%z] [%l] [%!] %v"); +std::string UTF16ToUTF8(std::wstring_view utf16); +std::wstring UTF8ToUTF16(std::string_view utf8); + +spdlog::sink_ptr CreateCustomSinkST(const std::function& acpSinkItHandler, const std::function& acpFlushHandler = nullptr); +spdlog::sink_ptr CreateCustomSinkMT(const std::function& acpSinkItHandler, const std::function& acpFlushHandler = nullptr); +std::shared_ptr CreateLogger(const std::filesystem::path& acpPath, const std::string& acpID, const spdlog::sink_ptr& acpExtraSink = nullptr, const std::string& acpPattern = "[%Y-%m-%d %H:%M:%S UTC%z] [%t] %v", const size_t acMaxFileSize = 5 * 1024 * 1024, const size_t acMaxFileCount = 3); // deep copies sol object (doesnt take into account potential duplicates) -sol::object DeepCopySolObject(sol::object aObj, const sol::state_view& aStateView); +sol::object DeepCopySolObject(const sol::object& acpObj, const sol::state_view& acpStateView); // makes sol usertype or userdata immutable when accessed from lua -void MakeSolUsertypeImmutable(sol::object aObj, const sol::state_view& aStateView); +void MakeSolUsertypeImmutable(const sol::object& acpObj, const sol::state_view& acpStateView); // Add unnamed function to the Lua registry template @@ -23,7 +28,7 @@ sol::function MakeSolFunction(sol::state& aState, F aFunc) // 2. Calling a lambda as an object has a tiny overhead of dealing with metatables. // 3. In Lua `type(f)` for a lambda as an object will return "userdata" instead of the expected "function". - static constexpr const char* s_cTempFuncName = "___func_temp_holder_"; + static constexpr auto s_cTempFuncName = "___func_temp_holder_"; aState[s_cTempFuncName] = aFunc; sol::function luaFunc = aState[s_cTempFuncName]; @@ -33,4 +38,19 @@ sol::function MakeSolFunction(sol::state& aState, F aFunc) } // Check if Lua object is of cdata type -bool IsLuaCData(sol::object aObject); \ No newline at end of file +bool IsLuaCData(const sol::object& acpObject); + +float GetAlignedItemWidth(const int64_t acItemsCount); + +float GetCenteredOffsetForText(const char* acpText); + +TChangedCBResult UnsavedChangesPopup(const std::string& acpOwnerName, bool& aFirstTime, const bool acMadeChanges, const TWidgetCB& acpSaveCB, const TWidgetCB& acpLoadCB, const TWidgetCB& acpCancelCB = nullptr); + +[[nodiscard]] std::filesystem::path GetAbsolutePath(const std::string& acFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting, const bool acAllowSymlink = true); +[[nodiscard]] std::filesystem::path GetAbsolutePath(std::filesystem::path aFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting, const bool acAllowSymlink = true); + +[[nodiscard]] std::filesystem::path GetLuaPath(const std::string& acFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting); +[[nodiscard]] std::filesystem::path GetLuaPath(std::filesystem::path aFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting); + +[[nodiscard]] std::vector ReadWholeBinaryFile(const std::filesystem::path& acpPath); +[[nodiscard]] std::string ReadWholeTextFile(const std::filesystem::path& acpPath); diff --git a/src/VKBindings.cpp b/src/VKBindings.cpp index 6665226b..43b522fb 100644 --- a/src/VKBindings.cpp +++ b/src/VKBindings.cpp @@ -1,27 +1,59 @@ #include #include "CET.h" +#include "Utils.h" -#include +std::function VKBind::DelayedCall(const bool acIsDown) const +{ + if (!acIsDown) // hotkeys only on key up + { + if (const auto* fn = std::get_if>(&Handler)) + return *fn; + } + + if (const auto* fn = std::get_if>(&Handler)) + return [cb = *fn, acIsDown]{ cb(acIsDown); }; + + assert(acIsDown); // nullptr should ever return only for key down events, in case binding is a hotkey + return nullptr; +} + +void VKBind::Call(const bool acIsDown) const +{ + if (const auto fn { DelayedCall(acIsDown) }) + fn(); +} + +bool VKBind::IsHotkey() const +{ + return std::holds_alternative>(Handler); +} + +bool VKBind::IsInput() const +{ + return std::holds_alternative>(Handler); +} -void VKBindInfo::Fill(uint64_t aVKCodeBind, const VKBind& aVKBind) +bool VKBind::HasSimpleDescription() const { - Bind = aVKBind; - CodeBind = aVKCodeBind; - SavedCodeBind = aVKCodeBind; - IsBinding = false; + return std::holds_alternative(Description); } -uint64_t VKBindInfo::Apply() +bool VKBind::HasComplexDescription() const { - SavedCodeBind = CodeBind; - return CodeBind; + return std::holds_alternative>(Description); +} + +bool VKBind::operator==(const std::string& acpId) const +{ + return ID == acpId; } VKBindings::VKBindings(Paths& aPaths, const Options& acOptions) : m_paths(aPaths) , m_cOptions(acOptions) { + Load(); } bool VKBindings::IsInitialized() const noexcept @@ -29,160 +61,150 @@ bool VKBindings::IsInitialized() const noexcept return m_initialized; } -TiltedPhoques::Vector VKBindings::InitializeMods(TiltedPhoques::Vector aVKBindInfos) +void VKBindings::InitializeMods(const TiltedPhoques::Map>>& acVKBinds) { + m_binds.clear(); + + const auto& overlayToggleModBind = Bindings::GetOverlayToggleModBind(); + // first, check existing bindings and try to bind in case there is no binding - for (auto& vkBindInfo : aVKBindInfos) + for (auto& [vkModName, vkBinds] : acVKBinds) { - const auto bind = GetBindCodeForID(vkBindInfo.Bind.ID); - if (bind != 0) - { - const auto ret = Bind(bind, vkBindInfo.Bind); // rebind - assert(ret); // we never really want ret == false here! - vkBindInfo.SavedCodeBind = bind; - vkBindInfo.CodeBind = bind; - continue; - } - // VKBind not bound yet - try to bind to default key (if any was chosen) - if (vkBindInfo.CodeBind != 0) + VKModBind vkModBind; + vkModBind.ModName = vkModName; + for (auto& modBind : vkBinds.get()) { - const auto ret = Bind(vkBindInfo.CodeBind, vkBindInfo.Bind); - if (ret) + // check for existing binding + vkModBind.ID = modBind.ID; + const auto bind = GetBindCodeForModBind(vkModBind, true); + if (!Bind(bind, vkModBind)) { - // we successfully bound! - vkBindInfo.SavedCodeBind = vkBindInfo.CodeBind; - continue; + // register new binding + m_modIdToBinds[vkModBind.ModName][vkModBind.ID] = 0; } } - // Cannot be bound now, key is most probably bound to something - vkBindInfo.SavedCodeBind = 0; - vkBindInfo.CodeBind = 0; } - // filter dead bindings if option is enabled (default) - if (m_cOptions.RemoveDeadBindings) + // now, find all dead bindings + TiltedPhoques::Vector deadMods; + for (const auto& [modName, modIdToBinds] : m_modIdToBinds) { - // now, find all dead bindings - TiltedPhoques::Vector> deadIDToBinds; - for (auto& idToBind : m_idToBind) + // always ignore internal bindings + if (modName == overlayToggleModBind.ModName) + continue; + + // check if mod is present + const auto currentModBindingsIt = acVKBinds.find(modName); + if (currentModBindingsIt == acVKBinds.cend()) { - // always ignore internal CET binds here! - if (!idToBind.first.compare(0, 4, "cet.")) + // when we want to filter dead bindings, mark mod for removal (default) + if (m_cOptions.RemoveDeadBindings) + { + deadMods.emplace_back(modName); continue; + } + } - // TODO - try to avoid O(n^2) situation here + // find all dead bindings for current mod + TiltedPhoques::Vector deadIDs; + for (auto& idToBind : modIdToBinds) + { auto found = false; - for (auto& vkBindInfo : aVKBindInfos) + if (currentModBindingsIt != acVKBinds.cend()) { - found = (idToBind.first == vkBindInfo.Bind.ID); - if (found) + for (auto& vkBind : currentModBindingsIt->second.get()) { - // test if bind is hotkey and if not, check if bind is valid for input (which means it has single input key, not combo) - found = (vkBindInfo.Bind.IsHotkey() || ((idToBind.second & 0xFFFF000000000000) == idToBind.second)); - break; // we just reset found flag accordingly and exit here, we found valid entry, no need to continue regardless of result + found = idToBind.first == vkBind.ID; + if (found) + { + // test if bind is hotkey and if not, check if bind is valid for input (which means it has + // simple input key, not combo) + found = vkBind.IsHotkey() || (idToBind.second & 0xFFFF000000000000) == idToBind.second; + break; // we just reset found flag accordingly and exit here, we found valid entry, no need to + // continue regardless of result + } } } if (!found) - deadIDToBinds.emplace_back(idToBind); + deadIDs.emplace_back(idToBind.first); } - // and remove them - for (auto& idToBind : deadIDToBinds) + // filter dead bindings if option is enabled (default) + // register them otherwise to prevent rebinds + for (const auto& deadId : deadIDs) { - m_idToBind.erase(idToBind.first); - m_binds.erase(idToBind.second); + if (m_cOptions.RemoveDeadBindings) + m_modIdToBinds[modName].erase(deadId); + else + Bind(m_modIdToBinds[modName][deadId], {modName, deadId}); } - - // finally, save our filtered bindings back to not lose them - Save(); } - // insert CET overlay bind info - assert(m_cpOverlay); // overlay must be set before first use! - const auto overlayKeyBind { m_cpOverlay->GetBind() }; - const auto overlayKeyCodeIt { m_idToBind.find(overlayKeyBind.ID) }; - const auto overlayKeyCode { (overlayKeyCodeIt == m_idToBind.cend()) ? (0) : (overlayKeyCodeIt->second) }; - aVKBindInfos.insert(aVKBindInfos.cbegin(), VKBindInfo{overlayKeyBind, overlayKeyCode, overlayKeyCode, false}); + // and lastly, remove all dead mods (if we have any to remove) + for (const auto& deadMod : deadMods) + m_modIdToBinds.erase(deadMod); + + // lastly, insert CET overlay bind info + const auto overlayBindCode{GetBindCodeForModBind(overlayToggleModBind, true)}; + if (!Bind(overlayBindCode, overlayToggleModBind)) + m_modIdToBinds[overlayToggleModBind.ModName][overlayToggleModBind.ID] = 0; - // return corrected bindings - return aVKBindInfos; + // finally, save our filtered bindings back to not lose them + Save(); } -bool VKBindings::Load(const Overlay& acOverlay) +void VKBindings::Load() { - auto parseConfig {[this, &acOverlay](const std::filesystem::path& path, bool old = false) -> std::pair { - std::ifstream ifs { path }; - if (ifs) - { - auto config { nlohmann::json::parse(ifs) }; - VKBind overlayBind { acOverlay.GetBind() }; - uint64_t overlayBindCode { 0 }; - for (auto& it : config.items()) - { - uint64_t key { it.value() }; - if (old) - { - // encoded key bind was 32-bit number in old config, convert it to new 64-bit format - key = - { - ((key & 0x000000FF) << 8*0) - | ((key & 0x0000FF00) << 8*1) - | ((key & 0x00FF0000) << 8*2) - | ((key & 0xFF000000) << 8*3) - }; - } - - // properly auto-bind Overlay if it is present here (could be this is first start so it may not be - // in here yet) - VKBind vkBind { it.key() }; - if (it.key() == overlayBind.ID) - { - vkBind = overlayBind; - overlayBindCode = key; - } + StopRecordingBind(); - const auto ret { Bind(key, vkBind) }; - assert(ret); // we want this to never fail! - } - return std::make_pair(true, overlayBindCode); - } - return std::make_pair(false, 0); - }}; - - // try to load from current path - auto [res, key] = parseConfig(m_paths.VKBindings()); - if (!res) + // try to load config + const auto path = GetAbsolutePath(m_paths.VKBindings(), "", false); + if (std::ifstream ifs{path}) { - // if failed, try to look for old config - auto oldPath = m_paths.CETRoot() / "hotkeys.json"; - const auto [resOld, keyOld] = parseConfig(oldPath, true); - if (resOld) + auto config{nlohmann::json::parse(ifs)}; + for (auto& mod : config.items()) { - // old config found and parsed, remove it and save it to current location - remove(oldPath); - Save(); - - // replace former res and key with ones from old config - res = resOld; - key = keyOld; + const auto& modName{mod.key()}; + const auto concatPoint = modName.find('.'); + if (concatPoint < modName.length()) + { + // load old config type + auto modBindName = modName.substr(0, concatPoint); + auto modBindId = modName.substr(concatPoint + 1); + m_modIdToBinds[modBindName][modBindId] = mod.value(); + } + else + { + // load new config type + for (auto& bind : mod.value().items()) + m_modIdToBinds[modName][bind.key()] = bind.value(); + } } } - m_cpOverlay = &acOverlay; - m_initialized = true; + Save(); - return res && key; + m_initialized = true; } void VKBindings::Save() { + StopRecordingBind(); + nlohmann::json config; - for (auto& idToBind : m_idToBind) - config[idToBind.first.c_str()] = idToBind.second; + for (const auto& [modName, modIdToBinds] : m_modIdToBinds) + { + auto& node = config[modName]; + for (const auto& [id, bind] : modIdToBinds) + { + node[id] = bind; + } + } - std::ofstream ofs{m_paths.VKBindings()}; + const auto path = GetAbsolutePath(m_paths.VKBindings(), "", true); + std::ofstream ofs{path}; ofs << config.dump(4) << std::endl; } @@ -191,169 +213,195 @@ void VKBindings::Update() m_queuedCallbacks.Drain(); } -void VKBindings::Clear() +static bool FirstKeyMatches(const uint64_t acFirst, const uint64_t acSecond) { - m_binds.clear(); - m_recordingBind = {}; + return (acFirst & 0xFFFF000000000000ull) == (acSecond & 0xFFFF000000000000ull); } -inline static bool FirstKeyMatches(uint64_t aFirst, uint64_t aSecond) +bool VKBindings::Bind(const uint64_t acVKCodeBind, const VKModBind& acVKModBind) { - return ((aFirst & 0xFFFF000000000000ull) == (aSecond & 0xFFFF000000000000ull)); -} + if (acVKCodeBind == 0) + return false; -bool VKBindings::Bind(uint64_t aVKCodeBind, const VKBind& aBind) -{ - // bind check 1 (check for rebind and already bound case) + const auto bind = m_binds.lower_bound(acVKCodeBind); + if (bind != m_binds.end()) { - auto bind = m_binds.lower_bound(aVKCodeBind); - if (bind != m_binds.end()) + // check if code binds are equal + if (bind->first == acVKCodeBind) { - if (bind->first == aVKCodeBind) - { - if (bind->second.ID == aBind.ID) - { - bind->second.Handler = aBind.Handler; // rebind - return true; - } - return false; // combo already bound and it is not the same VKBind! - } - // in this case, we may have found a hotkey or single input starting with same key - if (FirstKeyMatches(aVKCodeBind, bind->first)) - { - // first char matches! lets check that both binds are hotkey in this case - if (aBind.IsInput() || bind->second.IsInput()) - return false; // one of these is not a hotkey! single inputs cannot start with same key as some hotkey and vice versa! - } + // if these do not match, code bind is already bound to other mod bind! + return bind->second == acVKModBind; } - } - // bind check 2 (includes unbind in case it is probably desired) - { - const auto idToBind = m_idToBind.find(aBind.ID); - if (idToBind != m_idToBind.end()) + // in this case, we may have found a hotkey or simple input starting with same key + if (FirstKeyMatches(bind->first, acVKCodeBind)) { - auto bind = m_binds.find(idToBind->second); - assert (bind != m_binds.end()); // if these are not true for some reason, we have Fd up binding maps! - assert (bind->second.ID == aBind.ID); // if these are not true for some reason, we have Fd up binding maps! - if (!bind->second.IsValid()) + // first char matches! lets check that both binds are hotkey in this case + const auto isHotkey = [vm = m_cpVm](const VKModBind& vkModBind) { + if (!vm) + { + // this should never happen! + assert(false); + return false; + } + + const auto vkBind = vm->GetBind(vkModBind); + return vkBind && vkBind->IsHotkey(); + }; + + if (!isHotkey(bind->second) || !isHotkey(acVKModBind)) { - bind->second.Handler = aBind.Handler; // rebind - return true; + // one of these is not a hotkey! simple inputs cannot start with same key as some + // hotkey and vice versa! + return false; } - // this is not a rebind, this is new combo assignment to handler - m_binds.erase(bind); - m_idToBind.erase(idToBind); - // dont return or anything in this case, continue! } } - m_binds[aVKCodeBind] = aBind; - m_idToBind[aBind.ID] = aVKCodeBind; + UnBind(acVKModBind); + m_binds[acVKCodeBind] = acVKModBind; + m_modIdToBinds[acVKModBind.ModName][acVKModBind.ID] = acVKCodeBind; return true; } -bool VKBindings::UnBind(uint64_t aVKCodeBind) +bool VKBindings::UnBind(const uint64_t acVKCodeBind) { - auto bind = m_binds.find(aVKCodeBind); - if (bind == m_binds.end()) + if (m_binds.contains(acVKCodeBind)) + { + const auto& modBind = m_binds.at(acVKCodeBind); + m_modIdToBinds.at(modBind.ModName).at(modBind.ID) = 0; + m_binds.erase(acVKCodeBind); + return true; + } + return false; +} + +bool VKBindings::UnBind(const VKModBind& acVKModBind) +{ + const auto modBinds = m_modIdToBinds.find(acVKModBind.ModName); + if (modBinds == m_modIdToBinds.cend()) return false; - m_idToBind.erase(bind->second.ID); - m_binds.erase(bind); - return true; + auto& idToBinds = modBinds.value(); + const auto idToBind = idToBinds.find(acVKModBind.ID); + if (idToBind == idToBinds.cend()) + return false; + + return UnBind(idToBind->second); } -bool VKBindings::UnBind(const std::string& aID) +bool VKBindings::IsBound(const uint64_t acVKCodeBind) const { - const auto idToBind = m_idToBind.find(aID); - if (idToBind == m_idToBind.end()) - return false; - - m_binds.erase(idToBind->second); - m_idToBind.erase(idToBind); - return true; + const auto bind = m_binds.find(acVKCodeBind); + return bind != m_binds.end(); } -bool VKBindings::IsBound(uint64_t aVKCodeBind) const +bool VKBindings::IsBound(const VKModBind& acVKModBind) const { - return m_binds.count(aVKCodeBind); + if (const auto modIdToBindsIt = m_modIdToBinds.find(acVKModBind.ModName); modIdToBindsIt != m_modIdToBinds.cend()) + { + const auto& modIdToBinds = modIdToBindsIt->second; + if (const auto bindIt = modIdToBinds.find(acVKModBind.ID); bindIt != modIdToBinds.cend()) + return IsBound(bindIt->second); + } + return false; } -bool VKBindings::IsBound(const std::string& aID) const +bool VKBindings::IsFirstKeyUsed(const uint64_t acVKCodeBind) const { - return m_idToBind.count(aID); + const auto bind = m_binds.lower_bound(acVKCodeBind & 0xFFFF000000000000ull); + return bind != m_binds.end() ? FirstKeyMatches(acVKCodeBind, bind->first) : false; } -std::string VKBindings::GetBindString(uint64_t aVKCodeBind) +std::string VKBindings::GetBindString(const uint64_t acVKCodeBind) { - std::string bindStr { }; - if (aVKCodeBind == 0) - bindStr = "NOT BOUND"; - else + if (acVKCodeBind == 0) + return "Unbound"; + + std::string bindStr; + const auto bindDec{DecodeVKCodeBind(acVKCodeBind)}; + for (const auto vkCode : bindDec) { - auto bindDec { DecodeVKCodeBind(aVKCodeBind) }; - for (auto vkCode : bindDec) - { - if (vkCode == 0) - break; + if (vkCode == 0) + break; - const char* specialName { GetSpecialKeyName(vkCode) }; - if (specialName) - bindStr += specialName; + const char* specialName{GetSpecialKeyName(vkCode)}; + if (specialName) + bindStr += specialName; + else + { + const char vkChar{static_cast(MapVirtualKey(vkCode, MAPVK_VK_TO_CHAR))}; + if (vkChar != 0) + bindStr += vkChar; else - { - char vkChar { static_cast(MapVirtualKey(vkCode, MAPVK_VK_TO_CHAR)) }; - if (vkChar != 0) - bindStr += vkChar; - else - bindStr += "UNKNOWN"; - } - bindStr += " + "; + bindStr += "Unknown"; } - bindStr.erase(bindStr.rfind(" + ")); + bindStr += " + "; } - return bindStr; + return bindStr.erase(bindStr.rfind(" + ")); } -std::string VKBindings::GetBindString(const std::string aID) const +std::string VKBindings::GetBindString(const VKModBind& acVKModBind) const { - return GetBindString(GetBindCodeForID(aID)); + return GetBindString(GetBindCodeForModBind(acVKModBind)); } - -uint64_t VKBindings::GetBindCodeForID(const std::string& aID) const + +uint64_t VKBindings::GetBindCodeForModBind(const VKModBind& acVKModBind, const bool acIncludeDead) const { - assert(!aID.empty()); // we never really want aID to be empty here... but leave some fallback for release! - if (aID.empty()) + assert(!acVKModBind.ModName.empty()); // we never really want acModName to be empty here... but leave some fallback for release! + assert(!acVKModBind.ID.empty()); // we never really want acID to be empty here... but leave some fallback for release! + if (acVKModBind.ModName.empty() || acVKModBind.ID.empty()) + return 0; + + const auto modBinds = m_modIdToBinds.find(acVKModBind.ModName); + if (modBinds == m_modIdToBinds.cend()) return 0; - const auto idToBind = m_idToBind.find(aID); - if (idToBind == m_idToBind.cend()) + const auto& idToBinds = modBinds.value(); + const auto idToBind = idToBinds.find(acVKModBind.ID); + if (idToBind == idToBinds.cend()) return 0; - return idToBind->second; + return acIncludeDead || IsBound(idToBind->second) ? idToBind->second : 0; +} + +const VKModBind* VKBindings::GetModBindForBindCode(const uint64_t acVKCodeBind) const +{ + assert(acVKCodeBind != 0); // we never really want aVKCodeBind == 0 here... but leave some fallback for release! + if (acVKCodeBind == 0) + return nullptr; + + const auto bind = m_binds.find(acVKCodeBind); + if (bind == m_binds.cend()) + return nullptr; + + return &bind->second; } -std::string VKBindings::GetIDForBindCode(uint64_t aVKCodeBind) const +const VKModBind* VKBindings::GetModBindStartingWithBindCode(const uint64_t acVKCodeBind) const { - assert(aVKCodeBind != 0); // we never really want aVKCodeBind == 0 here... but leave some fallback for release! - if (aVKCodeBind == 0) - return { }; + assert(acVKCodeBind != 0); // we never really want aVKCodeBind == 0 here... but leave some fallback for release! + if (acVKCodeBind == 0) + return nullptr; - const auto bind = m_binds.find(aVKCodeBind); + const auto bind = m_binds.lower_bound(acVKCodeBind & 0xFFFF000000000000ull); if (bind == m_binds.cend()) - return { }; + return nullptr; - return bind->second.ID; + if (!FirstKeyMatches(bind->first, acVKCodeBind)) + return nullptr; + + return &bind->second; } -VKCodeBindDecoded VKBindings::DecodeVKCodeBind(uint64_t aVKCodeBind) +VKCodeBindDecoded VKBindings::DecodeVKCodeBind(const uint64_t acVKCodeBind) { - if (aVKCodeBind == 0) - return { }; + if (acVKCodeBind == 0) + return {}; - VKCodeBindDecoded res{ }; - *reinterpret_cast(res.data()) = _byteswap_uint64(aVKCodeBind); + VKCodeBindDecoded res{}; + const auto vkCodeBind = _byteswap_uint64(acVKCodeBind); + std::memcpy(res.data(), &vkCodeBind, sizeof(uint64_t)); for (auto& key : res) key = _byteswap_ushort(key); return res; @@ -363,18 +411,19 @@ uint64_t VKBindings::EncodeVKCodeBind(VKCodeBindDecoded aVKCodeBindDecoded) { for (auto& key : aVKCodeBindDecoded) key = _byteswap_ushort(key); - return _byteswap_uint64(*reinterpret_cast(aVKCodeBindDecoded.data())); + uint64_t vkCodeBindEncoded = 0; + std::memcpy(&vkCodeBindEncoded, aVKCodeBindDecoded.data(), sizeof(uint64_t)); + return _byteswap_uint64(vkCodeBindEncoded); } -bool VKBindings::StartRecordingBind(const VKBind& aBind) +bool VKBindings::StartRecordingBind(const VKModBind& acVKModBind) { if (m_isBindRecording) return false; - m_recording.fill(0); - m_recordingLength = 0; - m_recordingBind = aBind; - m_recordingResult = 0; + ClearRecording(true); + + m_recordingModBind = acVKModBind; m_isBindRecording = true; return true; @@ -384,10 +433,8 @@ bool VKBindings::StopRecordingBind() { if (!m_isBindRecording) return false; - - m_isBindRecording = false; - m_recordingLength = 0; - m_recording.fill(0); + + ClearRecording(false); return true; } @@ -402,128 +449,128 @@ uint64_t VKBindings::GetLastRecordingResult() const return m_recordingResult; } -const char* VKBindings::GetSpecialKeyName(USHORT aVKCode) +const char* VKBindings::GetSpecialKeyName(const USHORT acVKCode) { - switch (aVKCode) + switch (acVKCode) { - case VK_LBUTTON: - return "Mouse LB"; - case VK_RBUTTON: - return "Mouse RB"; - case VK_MBUTTON: - return "Mouse MB"; - case VK_XBUTTON1: - return "Mouse X1"; - case VK_XBUTTON2: - return "Mouse X2"; - case VK_BACK: - return "Backspace"; - case VK_TAB: - return "Tab"; - case VK_CLEAR: - return "Clear"; - case VK_RETURN: - return "Enter"; - case VK_SHIFT: - return "Shift"; - case VK_CONTROL: - return "Ctrl"; - case VK_MENU: - return "Alt"; - case VK_PAUSE: - return "Pause"; - case VK_CAPITAL: - return "Caps Lock"; - case VK_ESCAPE: - return "Esc"; - case VK_SPACE: - return "Space"; - case VK_PRIOR: - return "Page Up"; - case VK_NEXT: - return "Page Down"; - case VK_END: - return "End"; - case VK_HOME: - return "Home"; - case VK_LEFT: - return "Left Arrow"; - case VK_UP: - return "Up Arrow"; - case VK_RIGHT: - return "Right Arrow"; - case VK_DOWN: - return "Down Arrow"; - case VK_SELECT: - return "Select"; - case VK_PRINT: - return "Print"; - case VK_EXECUTE: - return "Execute"; - case VK_INSERT: - return "Insert"; - case VK_DELETE: - return "Delete"; - case VK_HELP: - return "Help"; - case VK_NUMPAD0: - return "Numpad 0"; - case VK_NUMPAD1: - return "Numpad 1"; - case VK_NUMPAD2: - return "Numpad 2"; - case VK_NUMPAD3: - return "Numpad 3"; - case VK_NUMPAD4: - return "Numpad 4"; - case VK_NUMPAD5: - return "Numpad 5"; - case VK_NUMPAD6: - return "Numpad 6"; - case VK_NUMPAD7: - return "Numpad 7"; - case VK_NUMPAD8: - return "Numpad 8"; - case VK_NUMPAD9: - return "Numpad 9"; - case VK_F1: - return "F1"; - case VK_F2: - return "F2"; - case VK_F3: - return "F3"; - case VK_F4: - return "F4"; - case VK_F5: - return "F5"; - case VK_F6: - return "F6"; - case VK_F7: - return "F7"; - case VK_F8: - return "F8"; - case VK_F9: - return "F9"; - case VK_F10: - return "F10"; - case VK_F11: - return "F11"; - case VK_F12: - return "F12"; - case VK_NUMLOCK: - return "Num Lock"; - case VK_SCROLL: - return "Scroll Lock"; - case VKBC_MWHEELUP: - return "Mouse Wheel Up"; - case VKBC_MWHEELDOWN: - return "Mouse Wheel Down"; - case VKBC_MWHEELLEFT: - return "Mouse Wheel Left"; - case VKBC_MWHEELRIGHT: - return "Mouse Wheel Right"; - default: - return nullptr; + case VK_LBUTTON: + return "Mouse LB"; + case VK_RBUTTON: + return "Mouse RB"; + case VK_MBUTTON: + return "Mouse MB"; + case VK_XBUTTON1: + return "Mouse X1"; + case VK_XBUTTON2: + return "Mouse X2"; + case VK_BACK: + return "Backspace"; + case VK_TAB: + return "Tab"; + case VK_CLEAR: + return "Clear"; + case VK_RETURN: + return "Enter"; + case VK_SHIFT: + return "Shift"; + case VK_CONTROL: + return "Ctrl"; + case VK_MENU: + return "Alt"; + case VK_PAUSE: + return "Pause"; + case VK_CAPITAL: + return "Caps Lock"; + case VK_ESCAPE: + return "Esc"; + case VK_SPACE: + return "Space"; + case VK_PRIOR: + return "Page Up"; + case VK_NEXT: + return "Page Down"; + case VK_END: + return "End"; + case VK_HOME: + return "Home"; + case VK_LEFT: + return "Left Arrow"; + case VK_UP: + return "Up Arrow"; + case VK_RIGHT: + return "Right Arrow"; + case VK_DOWN: + return "Down Arrow"; + case VK_SELECT: + return "Select"; + case VK_PRINT: + return "Print"; + case VK_EXECUTE: + return "Execute"; + case VK_INSERT: + return "Insert"; + case VK_DELETE: + return "Delete"; + case VK_HELP: + return "Help"; + case VK_NUMPAD0: + return "Numpad 0"; + case VK_NUMPAD1: + return "Numpad 1"; + case VK_NUMPAD2: + return "Numpad 2"; + case VK_NUMPAD3: + return "Numpad 3"; + case VK_NUMPAD4: + return "Numpad 4"; + case VK_NUMPAD5: + return "Numpad 5"; + case VK_NUMPAD6: + return "Numpad 6"; + case VK_NUMPAD7: + return "Numpad 7"; + case VK_NUMPAD8: + return "Numpad 8"; + case VK_NUMPAD9: + return "Numpad 9"; + case VK_F1: + return "F1"; + case VK_F2: + return "F2"; + case VK_F3: + return "F3"; + case VK_F4: + return "F4"; + case VK_F5: + return "F5"; + case VK_F6: + return "F6"; + case VK_F7: + return "F7"; + case VK_F8: + return "F8"; + case VK_F9: + return "F9"; + case VK_F10: + return "F10"; + case VK_F11: + return "F11"; + case VK_F12: + return "F12"; + case VK_NUMLOCK: + return "Num Lock"; + case VK_SCROLL: + return "Scroll Lock"; + case VKBC_MWHEELUP: + return "Mouse Wheel Up"; + case VKBC_MWHEELDOWN: + return "Mouse Wheel Down"; + case VKBC_MWHEELLEFT: + return "Mouse Wheel Left"; + case VKBC_MWHEELRIGHT: + return "Mouse Wheel Right"; + default: + return nullptr; } } @@ -534,195 +581,133 @@ LRESULT VKBindings::OnWndProc(HWND, UINT auMsg, WPARAM, LPARAM alParam) switch (auMsg) { - case WM_INPUT: - return HandleRAWInput(reinterpret_cast(alParam)); + case WM_INPUT: + return HandleRAWInput(reinterpret_cast(alParam)); + case WM_KILLFOCUS: + // reset key states on focus loss, as otherwise, we end up with broken recording state + m_keyStates.reset(); + ClearRecording(false); } - return 0; -} - -void VKBindings::ConnectUpdate(D3D12& aD3D12) -{ - m_connectUpdate = aD3D12.OnUpdate.Connect([this](){ this->Update(); }); -} - -void VKBindings::DisconnectUpdate(D3D12& aD3D12) -{ - aD3D12.OnUpdate.Disconnect(m_connectUpdate); - m_connectUpdate = static_cast(-1); -} - -bool VKBindings::IsLastRecordingKey(USHORT aVKCode) -{ - if (m_recordingLength == 0) - return false; - return (m_recording[m_recordingLength-1] == aVKCode); + return 0; } -LRESULT VKBindings::RecordKeyDown(USHORT aVKCode) -{ - if (m_keyStates[aVKCode]) +LRESULT VKBindings::RecordKeyDown(const USHORT acVKCode) +{ + if (m_keyStates[acVKCode]) return 0; // ignore repeats - + // mark key down - m_keyStates[aVKCode] = true; + m_keyStates[acVKCode] = true; - if (m_recordingLength >= m_recording.size()) + if (m_recordingLength != 0) { - assert(m_recordingLength == m_recording.size()); - for (size_t i = 1; i < m_recordingLength; ++i) - m_recording[i-1] = m_recording[i]; - --m_recordingLength; - } - - m_recording[m_recordingLength++] = aVKCode; - - const VKBind* bind { VerifyRecording() }; - - if (m_isBindRecording) - return 0; // don't do anything else when bind is being recorded + if (m_recordingLength >= m_recording.size()) + { + ClearRecording(false); + m_recordingResult = 0; - if (bind && (bind != VKBRecord_OK)) - { - if (m_cpOverlay && m_cpOverlay->IsEnabled()) - return 0; // we dont want to handle bindings if overlay is open and we are not in binding state! + return 0; + } - if (bind->IsValid()) // prevention for freshly loaded bind from file without rebinding + for (size_t i = 0; i < m_recordingLength; ++i) { - if (!bind->ID.compare(0, 4, "cet.")) - bind->Call(true); // we need to execute this immediately, otherwise cursor will not show on overlay toggle - else - { - auto delayedCall { bind->DelayedCall(true) }; - if (delayedCall) - m_queuedCallbacks.Add(delayedCall); - - // reset recording for single input - if (bind->IsInput()) - { - m_recording.fill(0); - m_recordingLength = 0; - } - } + assert(m_recording[i] != acVKCode); + if (m_recording[i] == acVKCode) + return 0; } } + else if (m_keyStates.count() != 1) + return 0; + + m_recording[m_recordingLength++] = acVKCode; + m_recordingWasKeyPressed = true; + + ExecuteRecording(true); return 0; } -LRESULT VKBindings::RecordKeyUp(USHORT aVKCode) +LRESULT VKBindings::RecordKeyUp(const USHORT acVKCode) { - if (!m_keyStates[aVKCode]) - return 0; // ignore up event when we did not register down event + // ignore up event when we did not register down event + if (!m_keyStates[acVKCode]) + return 0; // mark key up - m_keyStates[aVKCode] = false; + m_keyStates[acVKCode] = false; - // handle single inputs first - if (!m_recordingLength) - { - m_recording[m_recordingLength++] = aVKCode; - m_recordingResult = EncodeVKCodeBind(m_recording); - const auto* cpBind = VerifyRecording(); - if (cpBind && (cpBind != VKBRecord_OK)) - { - if (cpBind->IsInput()) - { - if (m_cpOverlay && !m_cpOverlay->IsEnabled()) // we dont want to handle bindings if overlay is open - { - if (cpBind->IsValid()) // prevention for freshly loaded bind from file without rebinding - m_queuedCallbacks.Add(cpBind->DelayedCall(false)); - } - else - { - // remark key as down! otherwise, bad things may happen... - m_keyStates[aVKCode] = true; - } - } - } - m_recordingResult = 0; - m_recording[0] = 0; - m_recordingLength = 0; // explicitly reset to 0, as VerifyRecording can make it 0 which would underflow if we decreased it... - return 0; - } - - // handle hotkeys for (size_t i = 0; i < m_recordingLength; ++i) { - if (m_recording[i] == aVKCode) - { - m_recordingResult = EncodeVKCodeBind(m_recording); - m_recordingLength = i; - - for (; i < m_recording.size(); ++i) - m_recording[i] = 0; - - while (!VerifyRecording()) {} // fix recording for future use, so user can use HKs like Ctrl+C, Ctrl+V without the need of pressing again Ctrl for example - - if (m_isBindRecording) - { - if (!Bind(m_recordingResult, m_recordingBind)) - m_recordingResult = 0; - - m_isBindRecording = false; - - return 0; - } - - const auto bind = m_binds.find(m_recordingResult); - if (bind != m_binds.end()) - { - if (m_cpOverlay && m_cpOverlay->IsEnabled() && (bind->second.ID != m_cpOverlay->GetBind().ID)) - return 0; // we dont want to handle bindings if toolbar is open and we are not in binding state! + if (m_recording[i] != acVKCode) + continue; + + if (m_recordingWasKeyPressed) + ExecuteRecording(false); + + // fix recording for future use, so user can use HKs like Ctrl+C, Ctrl+V without the need of pressing + // again Ctrl for example + m_recordingLength = i; + for (; i < m_recording.size(); ++i) + m_recording[i] = 0; + + m_recordingWasKeyPressed = false; - if (bind->second.IsValid()) // prevention for freshly loaded bind from file without rebinding - { - if (!bind->second.ID.compare(0, 4, "cet.")) - bind->second.Call(false); // we need to execute this immediately, otherwise cursor will not show on overlay toggle - else - m_queuedCallbacks.Add(bind->second.DelayedCall(false)); - } - } - return 0; - } + return 0; } - // if we got here, aVKCode is not recorded key or there is no bind, so we ignore this event + return 0; } -const VKBind* VKBindings::VerifyRecording() +void VKBindings::ExecuteRecording(const bool acLastKeyDown) { - if ((m_isBindRecording) || (m_recordingLength == 0)) - return VKBRecord_OK; // always valid for bind recordings or when empty + m_recordingResult = EncodeVKCodeBind(m_recording); + + if (acLastKeyDown && (m_isBindRecording || m_recordingLength != 1)) + return; - const auto possibleBind = m_binds.lower_bound(EncodeVKCodeBind(m_recording)); - if (possibleBind == m_binds.end()) + if (!acLastKeyDown && m_isBindRecording) { - m_recording[--m_recordingLength] = 0; - return nullptr; // seems like there is no possible HK here + m_isBindRecording = false; + return; } - VKCodeBindDecoded pbCodeDec = DecodeVKCodeBind(possibleBind->first); - for (size_t i = 0; i < m_recordingLength; ++i) + if (const auto bindIt = m_binds.find(m_recordingResult); bindIt != m_binds.cend()) { - if (pbCodeDec[i] != m_recording[i]) + if (bindIt->first != m_recordingResult) + return; + + const auto& modBind = bindIt->second; + if (const auto vkBind = m_cpVm->GetBind(modBind)) { - m_recording[--m_recordingLength] = 0; - return nullptr; // seems like there is no possible HK here + // we dont want to handle bindings when vm is not initialized or when overlay is open and we are not in binding state! + // only exception allowed is any CET bind + const auto& overlayToggleModBind = Bindings::GetOverlayToggleModBind(); + const auto cetModBind = modBind.ModName == overlayToggleModBind.ModName; + if (!cetModBind && (!m_cpVm->IsInitialized() || CET::Get().GetOverlay().IsEnabled())) + return; + + // check first if this is a hotkey, as those should only execute when last key was up + if (acLastKeyDown && vkBind->IsHotkey()) + return; + + // execute CET binds immediately, otherwise cursor will not show on overlay toggle + if (cetModBind) + vkBind->Call(acLastKeyDown); + else + m_queuedCallbacks.Add(vkBind->DelayedCall(acLastKeyDown)); } } - // valid recording - return ((m_recordingLength == 4) || !pbCodeDec[m_recordingLength]) ? (&possibleBind->second) : (VKBRecord_OK); // ptr to VKBind if equal, OK if possible match exists } -LRESULT VKBindings::HandleRAWInput(HRAWINPUT ahRAWInput) +LRESULT VKBindings::HandleRAWInput(const HRAWINPUT achRAWInput) { - UINT dwSize { 0 }; - GetRawInputData(ahRAWInput, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER)); + UINT dwSize{0}; + GetRawInputData(achRAWInput, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER)); const auto lpb = std::make_unique(dwSize); - if (GetRawInputData(ahRAWInput, RID_INPUT, lpb.get(), &dwSize, sizeof(RAWINPUTHEADER)) != dwSize ) - Log::Warn("VKBindings::HandleRAWInput() - GetRawInputData() does not return correct size !"); + if (GetRawInputData(achRAWInput, RID_INPUT, lpb.get(), &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) + Log::Warn("VKBindings::HandleRAWInput() - GetRawInputData() does not return correct size!"); const auto* raw = reinterpret_cast(lpb.get()); if (raw->header.dwType == RIM_TYPEKEYBOARD) @@ -730,56 +715,80 @@ LRESULT VKBindings::HandleRAWInput(HRAWINPUT ahRAWInput) const auto& kb = raw->data.keyboard; switch (kb.Message) { - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - return RecordKeyDown(kb.VKey); - case WM_KEYUP: - case WM_SYSKEYUP: - return RecordKeyUp(kb.VKey); + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + return RecordKeyDown(kb.VKey); + case WM_KEYUP: + case WM_SYSKEYUP: + return RecordKeyUp(kb.VKey); } } - else if (raw->header.dwType == RIM_TYPEMOUSE) + else if (raw->header.dwType == RIM_TYPEMOUSE) { - if (m_cpOverlay && m_isBindRecording && (m_recordingBind.ID == m_cpOverlay->GetBind().ID)) + if (m_isBindRecording && m_recordingModBind == Bindings::GetOverlayToggleModBind()) return 0; // ignore mouse keys for toolbar key binding! const auto& m = raw->data.mouse; switch (m.usButtonFlags) { - case RI_MOUSE_LEFT_BUTTON_DOWN: - return RecordKeyDown(VK_LBUTTON); - case RI_MOUSE_LEFT_BUTTON_UP: - return RecordKeyUp(VK_LBUTTON); - case RI_MOUSE_RIGHT_BUTTON_DOWN: - return RecordKeyDown(VK_RBUTTON); - case RI_MOUSE_RIGHT_BUTTON_UP: - return RecordKeyUp(VK_RBUTTON); - case RI_MOUSE_MIDDLE_BUTTON_DOWN: - return RecordKeyDown(VK_MBUTTON); - case RI_MOUSE_MIDDLE_BUTTON_UP: - return RecordKeyUp(VK_MBUTTON); - case RI_MOUSE_BUTTON_4_DOWN: - return RecordKeyDown(VK_XBUTTON1); - case RI_MOUSE_BUTTON_4_UP: - return RecordKeyUp(VK_XBUTTON1); - case RI_MOUSE_BUTTON_5_DOWN: - return RecordKeyDown(VK_XBUTTON2); - case RI_MOUSE_BUTTON_5_UP: - return RecordKeyUp(VK_XBUTTON2); - case RI_MOUSE_WHEEL: - { - const USHORT key { static_cast(RI_MOUSE_WHEEL | ((m.usButtonData & 0x8000) ? 0 : 1)) }; - RecordKeyDown(key); - return RecordKeyUp(key); - } - case RI_MOUSE_HWHEEL: - { - const USHORT key{static_cast(RI_MOUSE_HWHEEL | ((m.usButtonData & 0x8000) ? 0 : 1))}; - RecordKeyDown(key); - return RecordKeyUp(key); - } + case RI_MOUSE_LEFT_BUTTON_DOWN: + return RecordKeyDown(VK_LBUTTON); + case RI_MOUSE_LEFT_BUTTON_UP: + return RecordKeyUp(VK_LBUTTON); + case RI_MOUSE_RIGHT_BUTTON_DOWN: + return RecordKeyDown(VK_RBUTTON); + case RI_MOUSE_RIGHT_BUTTON_UP: + return RecordKeyUp(VK_RBUTTON); + case RI_MOUSE_MIDDLE_BUTTON_DOWN: + return RecordKeyDown(VK_MBUTTON); + case RI_MOUSE_MIDDLE_BUTTON_UP: + return RecordKeyUp(VK_MBUTTON); + case RI_MOUSE_BUTTON_4_DOWN: + return RecordKeyDown(VK_XBUTTON1); + case RI_MOUSE_BUTTON_4_UP: + return RecordKeyUp(VK_XBUTTON1); + case RI_MOUSE_BUTTON_5_DOWN: + return RecordKeyDown(VK_XBUTTON2); + case RI_MOUSE_BUTTON_5_UP: + return RecordKeyUp(VK_XBUTTON2); + case RI_MOUSE_WHEEL: + { + const USHORT key{static_cast(RI_MOUSE_WHEEL | ((m.usButtonData & 0x8000) ? 0 : 1))}; + RecordKeyDown(key); + return RecordKeyUp(key); + } + case RI_MOUSE_HWHEEL: + { + const USHORT key{static_cast(RI_MOUSE_HWHEEL | ((m.usButtonData & 0x8000) ? 0 : 1))}; + RecordKeyDown(key); + return RecordKeyUp(key); + } } } - return 0; + return 0; +} + +void VKBindings::ClearRecording(const bool acClearBind) +{ + m_recordingWasKeyPressed = false; + m_recording.fill(0); + m_recordingLength = 0; + m_isBindRecording = false; + if (acClearBind) + { + m_recordingResult = 0; + m_recordingModBind.ID.clear(); + m_recordingModBind.ModName.clear(); + } +} + +void VKBindings::SetVM(const LuaVM* acpVm) +{ + // this should happen only once + assert(m_cpVm == nullptr); + // vm pointer shall be always valid when this is called! + assert(acpVm != nullptr); + + m_cpVm = acpVm; } diff --git a/src/VKBindings.h b/src/VKBindings.h index c14c48b6..6b20d2ea 100644 --- a/src/VKBindings.h +++ b/src/VKBindings.h @@ -5,67 +5,32 @@ using TVKBindInputCallback = void(bool); using VKCodeBindDecoded = std::array; -struct VKBind +struct VKModBind { - std::string ID { }; - std::string Description { }; - std::variant, std::function> Handler{}; + std::string ModName; + std::string ID; - [[nodiscard]] std::function DelayedCall(bool isDown) const - { - if (!isDown) // hotkeys only on key up - { - const auto* fn = std::get_if>(&Handler); - if (fn) - return *fn; - } - - { - const auto* fn = std::get_if>(&Handler); - if (fn) - return std::bind(*fn, isDown); - } - - assert(isDown); // nullptr should ever return only for key down events, in case binding is a hotkey - return nullptr; - } - - void Call(bool isDown) const - { - auto fn { DelayedCall(isDown) }; - if (fn) - fn(); - } - - [[nodiscard]] bool IsValid() const - { - return (Handler.index() != std::variant_npos); - } - - [[nodiscard]] bool IsHotkey() const - { - return std::holds_alternative>(Handler); - } - - [[nodiscard]] bool IsInput() const - { - return std::holds_alternative>(Handler); - } + [[nodiscard]] auto operator<=>(const VKModBind&) const = default; }; -struct VKBindInfo +struct VKBind { - VKBind Bind { }; - uint64_t CodeBind { 0 }; - uint64_t SavedCodeBind{ 0 }; - bool IsBinding{ false }; + std::string ID{}; + std::string DisplayName{}; + std::variant> Description{}; + std::variant, std::function> Handler{}; - void Fill(uint64_t aVKCodeBind, const VKBind& aVKBind); + [[nodiscard]] std::function DelayedCall(const bool acIsDown) const; + void Call(const bool acIsDown) const; - uint64_t Apply(); -}; + [[nodiscard]] bool IsHotkey() const; + [[nodiscard]] bool IsInput() const; -static const VKBind* VKBRecord_OK { reinterpret_cast(true) }; + [[nodiscard]] bool HasSimpleDescription() const; + [[nodiscard]] bool HasComplexDescription() const; + + [[nodiscard]] bool operator==(const std::string& acpId) const; +}; constexpr USHORT VKBC_MWHEELUP { RI_MOUSE_WHEEL | 1 }; constexpr USHORT VKBC_MWHEELDOWN { RI_MOUSE_WHEEL | 0 }; @@ -75,6 +40,7 @@ constexpr USHORT VKBC_MWHEELLEFT { RI_MOUSE_HWHEEL | 0 }; struct Options; struct Overlay; struct D3D12; +struct LuaVM; struct VKBindings { VKBindings(Paths& aPaths, const Options& acOptions); @@ -82,68 +48,68 @@ struct VKBindings [[nodiscard]] bool IsInitialized() const noexcept; - TiltedPhoques::Vector InitializeMods(TiltedPhoques::Vector aVKBindInfos); + void InitializeMods(const TiltedPhoques::Map>>& acVKBinds); - static VKCodeBindDecoded DecodeVKCodeBind(uint64_t aVKCodeBind); - static uint64_t EncodeVKCodeBind(VKCodeBindDecoded aVKCodeBindDecoded); - static const char* GetSpecialKeyName(USHORT aVKCode); - - bool Load(const Overlay& acOverlay); + [[nodiscard]] static VKCodeBindDecoded DecodeVKCodeBind(const uint64_t acVKCodeBind); + [[nodiscard]] static uint64_t EncodeVKCodeBind(VKCodeBindDecoded aVKCodeBindDecoded); + [[nodiscard]] static const char* GetSpecialKeyName(const USHORT acVKCode); + + void Load(); void Save(); void Update(); - void Clear(); - bool Bind(uint64_t aVKCodeBind, const VKBind& aBind); - bool UnBind(uint64_t aVKCodeBind); - bool UnBind(const std::string& aID); - bool IsBound(uint64_t aVKCodeBind) const; - bool IsBound(const std::string& aID) const; + bool Bind(const uint64_t acVKCodeBind, const VKModBind& acVKModBind); + bool UnBind(const uint64_t acVKCodeBind); + bool UnBind(const VKModBind& acVKModBind); + [[nodiscard]] bool IsBound(const uint64_t acVKCodeBind) const; + [[nodiscard]] bool IsBound(const VKModBind& acVKModBind) const; + [[nodiscard]] bool IsFirstKeyUsed(const uint64_t acVKCodeBind) const; - static std::string GetBindString(uint64_t aVKCodeBind); - std::string GetBindString(const std::string aID) const; + [[nodiscard]] static std::string GetBindString(const uint64_t acVKCodeBind); + [[nodiscard]] std::string GetBindString(const VKModBind& acVKModBind) const; - uint64_t GetBindCodeForID(const std::string& aID) const; - std::string GetIDForBindCode(uint64_t aVKCodeBind) const; - - bool StartRecordingBind(const VKBind& aBind); + [[nodiscard]] uint64_t GetBindCodeForModBind(const VKModBind& acVKModBind, const bool acIncludeDead = false) const; + [[nodiscard]] const VKModBind* GetModBindForBindCode(const uint64_t acVKCodeBind) const; + [[nodiscard]] const VKModBind* GetModBindStartingWithBindCode(const uint64_t acVKCodeBind) const; + + bool StartRecordingBind(const VKModBind& acVKModBind); bool StopRecordingBind(); - bool IsRecordingBind() const; - uint64_t GetLastRecordingResult() const; + [[nodiscard]] bool IsRecordingBind() const; + [[nodiscard]] uint64_t GetLastRecordingResult() const; LRESULT OnWndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam); - void ConnectUpdate(D3D12& aD3D12); - void DisconnectUpdate(D3D12& aD3D12); + void SetVM(const LuaVM* acpVm); private: - bool IsLastRecordingKey(USHORT aVKCode); - LRESULT RecordKeyDown(USHORT aVKCode); - LRESULT RecordKeyUp(USHORT aVKCode); + [[nodiscard]] LRESULT HandleRAWInput(HRAWINPUT achRAWInput); - const VKBind* VerifyRecording(); + LRESULT RecordKeyDown(const USHORT acVKCode); + LRESULT RecordKeyUp(const USHORT acVKCode); - LRESULT HandleRAWInput(HRAWINPUT ahRAWInput); + void ExecuteRecording(const bool acLastKeyDown); + void ClearRecording(const bool acClearBind); + + std::map m_binds{ }; // this map needs to be ordered! + TiltedPhoques::Map> m_modIdToBinds{ }; + TiltedPhoques::TaskQueue m_queuedCallbacks{ }; std::bitset<1 << 16> m_keyStates{ }; - std::map m_binds{ }; // this map needs to be ordered! - TiltedPhoques::Map m_idToBind{ }; - - TiltedPhoques::TaskQueue m_queuedCallbacks{ }; - VKCodeBindDecoded m_recording{ }; - size_t m_recordingLength{ 0 }; - VKBind m_recordingBind { }; uint64_t m_recordingResult{ 0 }; + size_t m_recordingLength{ 0 }; + bool m_recordingWasKeyPressed{ false }; + + VKModBind m_recordingModBind{ }; bool m_isBindRecording{ false }; + bool m_initialized{ false }; + const LuaVM* m_cpVm{ nullptr }; Paths& m_paths; const Options& m_cOptions; - const Overlay* m_cpOverlay{ nullptr }; - - size_t m_connectUpdate{ static_cast(-1) }; }; diff --git a/src/common/CETTasks.cpp b/src/common/CETTasks.cpp index 1843db0c..3f4f9b86 100644 --- a/src/common/CETTasks.cpp +++ b/src/common/CETTasks.cpp @@ -2,7 +2,7 @@ #include "CETTasks.h" -CETTasks::CETTasks(Options& aOptions) +CETTasks::CETTasks() : m_running(true) { } diff --git a/src/common/CETTasks.h b/src/common/CETTasks.h index f939a7bd..9481c37c 100644 --- a/src/common/CETTasks.h +++ b/src/common/CETTasks.h @@ -1,9 +1,8 @@ #pragma once -struct Options; struct CETTasks { - CETTasks(Options& aOptions); + CETTasks(); ~CETTasks(); private: diff --git a/src/common/D3D12Downlevel.h b/src/common/D3D12Downlevel.h index 7b08b44c..1b3bda94 100644 --- a/src/common/D3D12Downlevel.h +++ b/src/common/D3D12Downlevel.h @@ -30,7 +30,7 @@ #endif /* __RPCNDR_H_VERSION__ */ #ifndef COM_NO_WINDOWS_H -#include "windows.h" +#include "Windows.h" #include "ole2.h" #endif /*COM_NO_WINDOWS_H*/ @@ -41,7 +41,7 @@ #pragma once #endif -/* Forward Declarations */ +/* Forward Declarations */ #ifndef __ID3D12CommandQueueDownlevel_FWD_DEFINED__ #define __ID3D12CommandQueueDownlevel_FWD_DEFINED__ @@ -65,20 +65,20 @@ typedef interface ID3D12DeviceDownlevel ID3D12DeviceDownlevel; #ifdef __cplusplus extern "C"{ -#endif +#endif /* interface __MIDL_itf_d3d12downlevel_0000_0000 */ -/* [local] */ +/* [local] */ #include #pragma region Desktop Family #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) -typedef +typedef enum D3D12_DOWNLEVEL_PRESENT_FLAGS { D3D12_DOWNLEVEL_PRESENT_FLAG_NONE = 0, - D3D12_DOWNLEVEL_PRESENT_FLAG_WAIT_FOR_VBLANK = ( D3D12_DOWNLEVEL_PRESENT_FLAG_NONE + 1 ) + D3D12_DOWNLEVEL_PRESENT_FLAG_WAIT_FOR_VBLANK = D3D12_DOWNLEVEL_PRESENT_FLAG_NONE + 1 } D3D12_DOWNLEVEL_PRESENT_FLAGS; DEFINE_ENUM_FLAG_OPERATORS( D3D12_DOWNLEVEL_PRESENT_FLAGS ); @@ -91,50 +91,49 @@ extern RPC_IF_HANDLE __MIDL_itf_d3d12downlevel_0000_0000_v0_0_s_ifspec; #define __ID3D12CommandQueueDownlevel_INTERFACE_DEFINED__ /* interface ID3D12CommandQueueDownlevel */ -/* [unique][local][object][uuid] */ +/* [unique][local][object][uuid] */ EXTERN_C const IID IID_ID3D12CommandQueueDownlevel; #if defined(__cplusplus) && !defined(CINTERFACE) - + MIDL_INTERFACE("38a8c5ef-7ccb-4e81-914f-a6e9d072c494") - ID3D12CommandQueueDownlevel : public IUnknown + ID3D12CommandQueueDownlevel : IUnknown { - public: - virtual HRESULT STDMETHODCALLTYPE Present( + virtual HRESULT STDMETHODCALLTYPE Present( _In_ ID3D12GraphicsCommandList *pOpenCommandList, _In_ ID3D12Resource *pSourceTex2D, _In_ HWND hWindow, D3D12_DOWNLEVEL_PRESENT_FLAGS Flags) = 0; - + }; - - + + #else /* C style interface */ typedef struct ID3D12CommandQueueDownlevelVtbl { BEGIN_INTERFACE - - HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( ID3D12CommandQueueDownlevel * This, REFIID riid, _COM_Outptr_ void **ppvObject); - - ULONG ( STDMETHODCALLTYPE *AddRef )( + + ULONG ( STDMETHODCALLTYPE *AddRef )( ID3D12CommandQueueDownlevel * This); - - ULONG ( STDMETHODCALLTYPE *Release )( + + ULONG ( STDMETHODCALLTYPE *Release )( ID3D12CommandQueueDownlevel * This); - - HRESULT ( STDMETHODCALLTYPE *Present )( + + HRESULT ( STDMETHODCALLTYPE *Present )( ID3D12CommandQueueDownlevel * This, _In_ ID3D12GraphicsCommandList *pOpenCommandList, _In_ ID3D12Resource *pSourceTex2D, _In_ HWND hWindow, D3D12_DOWNLEVEL_PRESENT_FLAGS Flags); - + END_INTERFACE } ID3D12CommandQueueDownlevelVtbl; @@ -143,23 +142,23 @@ EXTERN_C const IID IID_ID3D12CommandQueueDownlevel; CONST_VTBL struct ID3D12CommandQueueDownlevelVtbl *lpVtbl; }; - + #ifdef COBJMACROS #define ID3D12CommandQueueDownlevel_QueryInterface(This,riid,ppvObject) \ - ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define ID3D12CommandQueueDownlevel_AddRef(This) \ - ( (This)->lpVtbl -> AddRef(This) ) + ( (This)->lpVtbl -> AddRef(This) ) #define ID3D12CommandQueueDownlevel_Release(This) \ - ( (This)->lpVtbl -> Release(This) ) + ( (This)->lpVtbl -> Release(This) ) #define ID3D12CommandQueueDownlevel_Present(This,pOpenCommandList,pSourceTex2D,hWindow,Flags) \ - ( (This)->lpVtbl -> Present(This,pOpenCommandList,pSourceTex2D,hWindow,Flags) ) + ( (This)->lpVtbl -> Present(This,pOpenCommandList,pSourceTex2D,hWindow,Flags) ) #endif /* COBJMACROS */ @@ -176,48 +175,47 @@ EXTERN_C const IID IID_ID3D12CommandQueueDownlevel; #define __ID3D12DeviceDownlevel_INTERFACE_DEFINED__ /* interface ID3D12DeviceDownlevel */ -/* [unique][local][object][uuid] */ +/* [unique][local][object][uuid] */ EXTERN_C const IID IID_ID3D12DeviceDownlevel; #if defined(__cplusplus) && !defined(CINTERFACE) - + MIDL_INTERFACE("74eaee3f-2f4b-476d-82ba-2b85cb49e310") - ID3D12DeviceDownlevel : public IUnknown + ID3D12DeviceDownlevel : IUnknown { - public: - virtual HRESULT STDMETHODCALLTYPE QueryVideoMemoryInfo( + virtual HRESULT STDMETHODCALLTYPE QueryVideoMemoryInfo( UINT NodeIndex, DXGI_MEMORY_SEGMENT_GROUP MemorySegmentGroup, _Out_ DXGI_QUERY_VIDEO_MEMORY_INFO *pVideoMemoryInfo) = 0; - + }; - - + + #else /* C style interface */ typedef struct ID3D12DeviceDownlevelVtbl { BEGIN_INTERFACE - - HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( ID3D12DeviceDownlevel * This, REFIID riid, _COM_Outptr_ void **ppvObject); - - ULONG ( STDMETHODCALLTYPE *AddRef )( + + ULONG ( STDMETHODCALLTYPE *AddRef )( ID3D12DeviceDownlevel * This); - - ULONG ( STDMETHODCALLTYPE *Release )( + + ULONG ( STDMETHODCALLTYPE *Release )( ID3D12DeviceDownlevel * This); - - HRESULT ( STDMETHODCALLTYPE *QueryVideoMemoryInfo )( + + HRESULT ( STDMETHODCALLTYPE *QueryVideoMemoryInfo )( ID3D12DeviceDownlevel * This, UINT NodeIndex, DXGI_MEMORY_SEGMENT_GROUP MemorySegmentGroup, _Out_ DXGI_QUERY_VIDEO_MEMORY_INFO *pVideoMemoryInfo); - + END_INTERFACE } ID3D12DeviceDownlevelVtbl; @@ -226,23 +224,23 @@ EXTERN_C const IID IID_ID3D12DeviceDownlevel; CONST_VTBL struct ID3D12DeviceDownlevelVtbl *lpVtbl; }; - + #ifdef COBJMACROS #define ID3D12DeviceDownlevel_QueryInterface(This,riid,ppvObject) \ - ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define ID3D12DeviceDownlevel_AddRef(This) \ - ( (This)->lpVtbl -> AddRef(This) ) + ( (This)->lpVtbl -> AddRef(This) ) #define ID3D12DeviceDownlevel_Release(This) \ - ( (This)->lpVtbl -> Release(This) ) + ( (This)->lpVtbl -> Release(This) ) #define ID3D12DeviceDownlevel_QueryVideoMemoryInfo(This,NodeIndex,MemorySegmentGroup,pVideoMemoryInfo) \ - ( (This)->lpVtbl -> QueryVideoMemoryInfo(This,NodeIndex,MemorySegmentGroup,pVideoMemoryInfo) ) + ( (This)->lpVtbl -> QueryVideoMemoryInfo(This,NodeIndex,MemorySegmentGroup,pVideoMemoryInfo) ) #endif /* COBJMACROS */ @@ -256,7 +254,7 @@ EXTERN_C const IID IID_ID3D12DeviceDownlevel; /* interface __MIDL_itf_d3d12downlevel_0000_0002 */ -/* [local] */ +/* [local] */ #endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */ #pragma endregion diff --git a/src/common/Logging.h b/src/common/Logging.h index 7266df7f..78f8b680 100644 --- a/src/common/Logging.h +++ b/src/common/Logging.h @@ -25,36 +25,36 @@ struct format_with_location template void Warn(format_with_location fmt, Args&&... args) { - spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::warn, fmt.value, std::forward(args)...); + spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::warn, fmt::runtime(fmt.value), std::forward(args)...); } template void Info(format_with_location fmt, Args&&... args) { - spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::info, fmt.value, std::forward(args)...); + spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::info, fmt::runtime(fmt.value), std::forward(args)...); } template void Debug(format_with_location fmt, Args&&... args) { - spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::debug, fmt.value, std::forward(args)...); + spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::debug, fmt::runtime(fmt.value), std::forward(args)...); } template void Error(format_with_location fmt, Args&&... args) { - spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::err, fmt.value, std::forward(args)...); + spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::err, fmt::runtime(fmt.value), std::forward(args)...); } template void Critical(format_with_location fmt, Args&&... args) { - spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::critical, fmt.value, std::forward(args)...); + spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::critical, fmt::runtime(fmt.value), std::forward(args)...); } template void Trace(format_with_location fmt, Args&&... args) { - spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::trace, fmt.value, std::forward(args)...); + spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::trace, fmt::runtime(fmt.value), std::forward(args)...); } } // namespace logging \ No newline at end of file diff --git a/src/common/ScopeGuard.h b/src/common/ScopeGuard.h index c777c825..006020ea 100644 --- a/src/common/ScopeGuard.h +++ b/src/common/ScopeGuard.h @@ -15,7 +15,7 @@ class ScopeGuard throw; } - ScopeGuard(ScopeGuard&& other) + ScopeGuard(ScopeGuard&& other) noexcept : f(std::move(other.f)) { other.f = nullptr; diff --git a/src/d3d12/D3D12.cpp b/src/d3d12/D3D12.cpp index e75140cd..3eee8b61 100644 --- a/src/d3d12/D3D12.cpp +++ b/src/d3d12/D3D12.cpp @@ -7,32 +7,34 @@ #include #include -void D3D12::SetTrapInputInImGui(bool aEnabled) +void D3D12::SetTrapInputInImGui(const bool acEnabled) { int showCursorState; - if (aEnabled) + if (acEnabled) do { showCursorState = ShowCursor(TRUE); } while (showCursorState < 0); else do { showCursorState = ShowCursor(FALSE); } while (showCursorState >= 0); - m_trapInputInImGui = aEnabled; + m_trapInputInImGui = acEnabled; } -void D3D12::DelayedSetTrapInputInImGui(bool aEnabled) +void D3D12::DelayedSetTrapInputInImGui(const bool acEnabled) { - m_delayedTrapInputState = aEnabled; + m_delayedTrapInputState = acEnabled; m_delayedTrapInput = true; } -LRESULT D3D12::OnWndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam) +LRESULT D3D12::OnWndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam) const { auto& d3d12 = CET::Get().GetD3D12(); if (d3d12.IsInitialized()) { - const auto res = ImGui_ImplWin32_WndProcHandler(ahWnd, auMsg, awParam, alParam); - if (res) - return res; + { + std::lock_guard _(d3d12.m_imguiLock); + if (const auto res = ImGui_ImplWin32_WndProcHandler(ahWnd, auMsg, awParam, alParam)) + return res; + } if (d3d12.m_delayedTrapInput) { @@ -47,15 +49,12 @@ LRESULT D3D12::OnWndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam) (auMsg >= WM_KEYFIRST && auMsg <= WM_KEYLAST)) return 1; - // ignore specific messages - switch (auMsg) - { - case WM_INPUT: - return 1; - } + // ignore input messages + if (auMsg == WM_INPUT) + return 1; } } - + return 0; } @@ -65,31 +64,9 @@ D3D12::D3D12(Window& aWindow, Paths& aPaths, Options& aOptions) , m_options(aOptions) { HookGame(); - - std::thread t([this]() - { - if (kiero::init() != kiero::Status::Success) - Log::Error("Kiero failed!"); - else - { - std::string_view d3d12type = (kiero::isDownLevelDevice()) ? ("D3D12on7") : ("D3D12"); - Log::Info("Kiero initialized for {0}", d3d12type); - - Hook(); - } - }); - - t.detach(); } -D3D12::~D3D12() +D3D12::~D3D12() { - if (m_initialized) - { - ImGui_ImplDX12_Shutdown(); - ImGui_ImplWin32_Shutdown(); - ImGui::DestroyContext(); - } - - kiero::shutdown(); + assert(!m_initialized); } diff --git a/src/d3d12/D3D12.h b/src/d3d12/D3D12.h index e80ed7b1..1d3af568 100644 --- a/src/d3d12/D3D12.h +++ b/src/d3d12/D3D12.h @@ -3,89 +3,99 @@ #include "common/D3D12Downlevel.h" #include "window/Window.h" -using TResizeBuffersD3D12 = HRESULT(IDXGISwapChain*, UINT, UINT, UINT, DXGI_FORMAT, UINT); using TPresentD3D12Downlevel = HRESULT(ID3D12CommandQueueDownlevel*, ID3D12GraphicsCommandList*, ID3D12Resource*, HWND, D3D12_DOWNLEVEL_PRESENT_FLAGS); using TCreateCommittedResource = HRESULT(ID3D12Device*, const D3D12_HEAP_PROPERTIES*, D3D12_HEAP_FLAGS, const D3D12_RESOURCE_DESC*, D3D12_RESOURCE_STATES, const D3D12_CLEAR_VALUE*, const IID*, void**); using TExecuteCommandLists = void(ID3D12CommandQueue*, UINT, ID3D12CommandList* const*); using TCRenderNode_Present_InternalPresent = void*(int32_t*, uint8_t ,UINT); -using TCRenderGlobal_Resize = void*(uint32_t a1, uint32_t a2, uint32_t a3, uint8_t a4, int* a5); +using TCRenderGlobal_Resize = void*(uint32_t a1, uint32_t a2, uint32_t a3, uint8_t a4, int32_t* a5); +using TCRenderGlobal_Shutdown = void*(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4); struct D3D12 { - static const uint32_t g_numDownlevelBackbuffersRequired = 3; // Windows 7 only: number of buffers needed before we start rendering + inline static const uint32_t g_numDownlevelBackbuffersRequired = 3; // Windows 7 only: number of buffers needed before we start rendering D3D12(Window& aWindow, Paths& aPaths, Options& aOptions); ~D3D12(); - void SetTrapInputInImGui(bool aEnabled); - void DelayedSetTrapInputInImGui(bool aEnabled); + void SetTrapInputInImGui(const bool acEnabled); + void DelayedSetTrapInputInImGui(const bool acEnabled); [[nodiscard]] bool IsTrapInputInImGui() const noexcept { return m_trapInputInImGui; } [[nodiscard]] bool IsInitialized() const noexcept { return m_initialized; } [[nodiscard]] SIZE GetResolution() const noexcept { return m_outSize; } - LRESULT OnWndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam); + LRESULT OnWndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam) const; + + bool IsImGuiPresentDraw() const; + void PrepareUpdate(bool aPrepareMods = true); TiltedPhoques::Signal OnInitialized; - TiltedPhoques::Signal OnUpdate; ID3D12Device* GetDevice() const; std::tuple CreateTextureDescriptor(); protected: - + void Hook(); void HookGame(); struct FrameContext { - CComPtr CommandAllocator { nullptr }; - CComPtr BackBuffer { nullptr }; + Microsoft::WRL::ComPtr CommandAllocator; + Microsoft::WRL::ComPtr BackBuffer; D3D12_CPU_DESCRIPTOR_HANDLE MainRenderTargetDescriptor{ 0 }; }; - bool ResetState(bool aClearDownlevelBackbuffers = true); - bool Initialize(IDXGISwapChain* apSwapChain); + bool ResetState(const bool acClearDownlevelBackbuffers = true, const bool acDestroyContext = false); + bool Initialize(); bool InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resource* apSourceTex2D, HWND ahWindow); bool InitializeImGui(size_t aBuffersCounts); void Update(); - static HRESULT ResizeBuffers(IDXGISwapChain* apSwapChain, UINT aBufferCount, UINT aWidth, UINT aHeight, DXGI_FORMAT aNewFormat, UINT aSwapChainFlags); static HRESULT PresentDownlevel(ID3D12CommandQueueDownlevel* apCommandQueueDownlevel, ID3D12GraphicsCommandList* apOpenCommandList, ID3D12Resource* apSourceTex2D, HWND ahWindow, D3D12_DOWNLEVEL_PRESENT_FLAGS aFlags); static HRESULT CreateCommittedResource(ID3D12Device* apDevice, const D3D12_HEAP_PROPERTIES* acpHeapProperties, D3D12_HEAP_FLAGS aHeapFlags, const D3D12_RESOURCE_DESC* acpDesc, D3D12_RESOURCE_STATES aInitialResourceState, const D3D12_CLEAR_VALUE* acpOptimizedClearValue, const IID* acpRIID, void** appvResource); static void ExecuteCommandLists(ID3D12CommandQueue* apCommandQueue, UINT aNumCommandLists, ID3D12CommandList* const* apcpCommandLists); + static void* CRenderNode_Present_InternalPresent(int32_t* apSomeInt, uint8_t aSomeSync, UINT aSyncInterval); - static void* CRenderGlobal_Resize(uint32_t a1, uint32_t a2, uint32_t a3, uint8_t a4, int* a5); - + static void* CRenderGlobal_Resize(uint32_t a1, uint32_t a2, uint32_t a3, uint8_t a4, int32_t* a5); + static void* CRenderGlobal_Shutdown(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4); + private: - TResizeBuffersD3D12* m_realResizeBuffersD3D12{ nullptr }; TPresentD3D12Downlevel* m_realPresentD3D12Downlevel{ nullptr }; TCreateCommittedResource* m_realCreateCommittedResource{ nullptr }; TExecuteCommandLists* m_realExecuteCommandLists{ nullptr }; TCRenderNode_Present_InternalPresent* m_realInternalPresent{ nullptr }; - TCRenderGlobal_Resize* m_realInternalResize{nullptr}; - + TCRenderGlobal_Resize* m_realInternalResize{ nullptr }; + TCRenderGlobal_Shutdown* m_realInternalShutdown{ nullptr }; + bool m_initialized{ false }; - TiltedPhoques::Vector m_frameContexts{ }; - TiltedPhoques::Vector m_downlevelBackbuffers{ }; - IDXGISwapChain3* m_pdxgiSwapChain{ nullptr }; - CComPtr m_pd3d12Device{ nullptr }; - CComPtr m_pd3dRtvDescHeap{ nullptr }; - CComPtr m_pd3dSrvDescHeap{ nullptr }; - CComPtr m_pd3dCommandList{ nullptr }; - ID3D12CommandQueue* m_pCommandQueue{ nullptr }; + TiltedPhoques::Vector m_frameContexts; + TiltedPhoques::Vector> m_downlevelBackbuffers; uint32_t m_downlevelBufferIndex{ 0 }; - + + Microsoft::WRL::ComPtr m_pd3d12Device{ }; + Microsoft::WRL::ComPtr m_pd3dRtvDescHeap{ }; + Microsoft::WRL::ComPtr m_pd3dSrvDescHeap{ }; + Microsoft::WRL::ComPtr m_pd3dCommandList{ }; + + // borrowed resources from game, do not manipulate reference counts on these! + Microsoft::WRL::ComPtr m_pdxgiSwapChain{ nullptr }; + Microsoft::WRL::ComPtr m_pCommandQueue{ nullptr }; + SIZE m_outSize{ }; - + std::atomic_bool m_trapInputInImGui{ false }; + ImGuiStyle m_styleReference{ }; Paths& m_paths; Window& m_window; Options& m_options; - + + std::recursive_mutex m_imguiLock; + std::array m_imguiDrawDataBuffers; + std::atomic_bool m_imguiPresentDraw{ true }; std::atomic_bool m_delayedTrapInput{ false }; std::atomic_bool m_delayedTrapInputState{ false }; }; diff --git a/src/d3d12/D3D12_Functions.cpp b/src/d3d12/D3D12_Functions.cpp index 9157645c..a8f221b7 100644 --- a/src/d3d12/D3D12_Functions.cpp +++ b/src/d3d12/D3D12_Functions.cpp @@ -1,42 +1,62 @@ #include #include "D3D12.h" +#include "Options.h" +#include "Utils.h" -#include +#include #include #include +#include -#include -#include "Options.h" - -bool D3D12::ResetState(bool aClearDownlevelBackbuffers) +bool D3D12::ResetState(const bool acClearDownlevelBackbuffers, const bool acDestroyContext) { if (m_initialized) - { - m_initialized = false; + { + std::lock_guard _(m_imguiLock); + + for (auto& drawData : m_imguiDrawDataBuffers) + { + for (auto i = 0; i < drawData.CmdListsCount; ++i) + IM_DELETE(drawData.CmdLists[i]); + delete[] drawData.CmdLists; + drawData.CmdLists = nullptr; + drawData.Clear(); + } + ImGui_ImplDX12_Shutdown(); ImGui_ImplWin32_Shutdown(); + + if (acDestroyContext) + ImGui::DestroyContext(); } m_frameContexts.clear(); + m_outSize = { 0, 0 }; - if (aClearDownlevelBackbuffers) + if (acClearDownlevelBackbuffers) m_downlevelBackbuffers.clear(); - - m_pdxgiSwapChain = nullptr; - m_pd3d12Device = nullptr; - m_pd3dRtvDescHeap = nullptr; - m_pd3dSrvDescHeap = nullptr; - m_pd3dCommandList = nullptr; m_downlevelBufferIndex = 0; - m_outSize = { 0, 0 }; - // NOTE: not clearing m_hWnd, m_wndProc and m_pCommandQueue, as these should be persistent once set till the EOL of D3D12 + + m_pd3d12Device.Reset(); + m_pd3dRtvDescHeap.Reset(); + m_pd3dSrvDescHeap.Reset(); + m_pd3dCommandList.Reset(); + + m_pCommandQueue.Reset(); + m_pdxgiSwapChain.Reset(); + + m_initialized = false; + return false; } -bool D3D12::Initialize(IDXGISwapChain* apSwapChain) +bool D3D12::Initialize() { - if (!apSwapChain) + if (m_initialized) + return true; + + if (!m_pdxgiSwapChain) return false; const HWND hWnd = m_window.GetWindow(); @@ -46,36 +66,6 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) return false; } - if (m_initialized) - { - IDXGISwapChain3* pSwapChain3{ nullptr }; - if (FAILED(apSwapChain->QueryInterface(IID_PPV_ARGS(&pSwapChain3)))) - { - Log::Error("D3D12::Initialize() - unable to query pSwapChain interface for IDXGISwapChain3! (pSwapChain = {:X})", reinterpret_cast(apSwapChain)); - return false; - } - if (m_pdxgiSwapChain != pSwapChain3) - { - Log::Warn("D3D12::Initialize() - multiple swap chains detected! Currently hooked to {0:X}, this call was from {1:X}.", reinterpret_cast(*(&m_pdxgiSwapChain)), reinterpret_cast(apSwapChain)); - return false; - } - { - DXGI_SWAP_CHAIN_DESC sdesc; - m_pdxgiSwapChain->GetDesc(&sdesc); - - if (hWnd != sdesc.OutputWindow) - Log::Warn("D3D12::Initialize() - output window of current swap chain does not match hooked window! Currently hooked to {0} while swap chain output window is {1}.", reinterpret_cast(hWnd), reinterpret_cast(sdesc.OutputWindow)); - } - - return true; - } - - if (FAILED(apSwapChain->QueryInterface(IID_PPV_ARGS(&m_pdxgiSwapChain)))) - { - Log::Error("D3D12::Initialize() - unable to query pSwapChain interface for IDXGISwapChain3! (pSwapChain = {0})", reinterpret_cast(apSwapChain)); - return ResetState(); - } - if (FAILED(m_pdxgiSwapChain->GetDevice(IID_PPV_ARGS(&m_pd3d12Device)))) { Log::Error("D3D12::Initialize() - failed to get device!"); @@ -84,18 +74,26 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) DXGI_SWAP_CHAIN_DESC sdesc; m_pdxgiSwapChain->GetDesc(&sdesc); - + if (hWnd != sdesc.OutputWindow) - Log::Warn("D3D12::Initialize() - output window of current swap chain does not match hooked window! Currently hooked to {0} while swap chain output window is {1}.", reinterpret_cast(hWnd), reinterpret_cast(sdesc.OutputWindow)); + Log::Warn("D3D12::Initialize() - output window of current swap chain does not match hooked window! Currently hooked to {} while swap chain output window is {}.", reinterpret_cast(hWnd), reinterpret_cast(sdesc.OutputWindow)); m_outSize = { static_cast(sdesc.BufferDesc.Width), static_cast(sdesc.BufferDesc.Height) }; - const auto buffersCounts = sdesc.BufferCount; + const auto buffersCounts = std::min(sdesc.BufferCount, 3u); m_frameContexts.resize(buffersCounts); - for (UINT i = 0; i < buffersCounts; i++) - m_pdxgiSwapChain->GetBuffer(i, IID_PPV_ARGS(&m_frameContexts[i].BackBuffer)); - D3D12_DESCRIPTOR_HEAP_DESC rtvdesc = {}; + D3D12_DESCRIPTOR_HEAP_DESC srvdesc = {}; + srvdesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + srvdesc.NumDescriptors = buffersCounts; + srvdesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + if (FAILED(m_pd3d12Device->CreateDescriptorHeap(&srvdesc, IID_PPV_ARGS(&m_pd3dSrvDescHeap)))) + { + Log::Error("D3D12::Initialize() - failed to create SRV descriptor heap!"); + return ResetState(); + } + + D3D12_DESCRIPTOR_HEAP_DESC rtvdesc; rtvdesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; rtvdesc.NumDescriptors = buffersCounts; rtvdesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; @@ -108,22 +106,15 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) const SIZE_T rtvDescriptorSize = m_pd3d12Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = m_pd3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart(); - for (auto& context : m_frameContexts) + for (UINT i = 0; i < buffersCounts; i++) { + auto& context = m_frameContexts[i]; context.MainRenderTargetDescriptor = rtvHandle; + m_pdxgiSwapChain->GetBuffer(i, IID_PPV_ARGS(&context.BackBuffer)); + m_pd3d12Device->CreateRenderTargetView(context.BackBuffer.Get(), nullptr, context.MainRenderTargetDescriptor); rtvHandle.ptr += rtvDescriptorSize; } - D3D12_DESCRIPTOR_HEAP_DESC srvdesc = {}; - srvdesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - srvdesc.NumDescriptors = 200; - srvdesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; - if (FAILED(m_pd3d12Device->CreateDescriptorHeap(&srvdesc, IID_PPV_ARGS(&m_pd3dSrvDescHeap)))) - { - Log::Error("D3D12::Initialize() - failed to create SRV descriptor heap!"); - return ResetState(); - } - for (auto& context : m_frameContexts) if (FAILED(m_pd3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&context.CommandAllocator)))) { @@ -131,16 +122,13 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) return ResetState(); } - if (FAILED(m_pd3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_frameContexts[0].CommandAllocator, nullptr, IID_PPV_ARGS(&m_pd3dCommandList))) || + if (FAILED(m_pd3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_frameContexts[0].CommandAllocator.Get(), nullptr, IID_PPV_ARGS(&m_pd3dCommandList))) || FAILED(m_pd3dCommandList->Close())) { Log::Error("D3D12::Initialize() - failed to create command list!"); return ResetState(); } - for (auto& context : m_frameContexts) - m_pd3d12Device->CreateRenderTargetView(context.BackBuffer, nullptr, context.MainRenderTargetDescriptor); - if (!InitializeImGui(buffersCounts)) { Log::Error("D3D12::Initialize() - failed to initialize ImGui!"); @@ -160,7 +148,7 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour if (!apCommandQueue || !apSourceTex2D) return false; - HWND hWnd = m_window.GetWindow(); + const HWND hWnd = m_window.GetWindow(); if (!hWnd) { Log::Warn("D3D12::InitializeDownlevel() - window not yet hooked!"); @@ -170,13 +158,13 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour if (m_initialized) { if (hWnd != ahWindow) - Log::Warn("D3D12::InitializeDownlevel() - current output window does not match hooked window! Currently hooked to {0} while current output window is {1}.", reinterpret_cast(hWnd), reinterpret_cast(ahWindow)); + Log::Warn("D3D12::InitializeDownlevel() - current output window does not match hooked window! Currently hooked to {} while current output window is {}.", reinterpret_cast(hWnd), reinterpret_cast(ahWindow)); return true; } - auto cmdQueueDesc = apCommandQueue->GetDesc(); - if(cmdQueueDesc.Type != D3D12_COMMAND_LIST_TYPE_DIRECT) + const auto cmdQueueDesc = apCommandQueue->GetDesc(); + if(cmdQueueDesc.Type != D3D12_COMMAND_LIST_TYPE_DIRECT) { Log::Warn("D3D12::InitializeDownlevel() - ignoring command queue - invalid type of command list!"); return false; @@ -184,11 +172,11 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour m_pCommandQueue = apCommandQueue; - auto st2DDesc = apSourceTex2D->GetDesc(); + const auto st2DDesc = apSourceTex2D->GetDesc(); m_outSize = { static_cast(st2DDesc.Width), static_cast(st2DDesc.Height) }; - + if (hWnd != ahWindow) - Log::Warn("D3D12::InitializeDownlevel() - current output window does not match hooked window! Currently hooked to {0} while current output window is {1}.", reinterpret_cast(hWnd), reinterpret_cast(ahWindow)); + Log::Warn("D3D12::InitializeDownlevel() - current output window does not match hooked window! Currently hooked to {} while current output window is {}.", reinterpret_cast(hWnd), reinterpret_cast(ahWindow)); if (FAILED(apSourceTex2D->GetDevice(IID_PPV_ARGS(&m_pd3d12Device)))) { @@ -237,7 +225,7 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour Log::Error("D3D12::InitializeDownlevel() - failed to create SRV descriptor heap!"); return ResetState(); } - + for (auto& context : m_frameContexts) { if (FAILED(m_pd3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&context.CommandAllocator)))) @@ -247,7 +235,7 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour } } - if (FAILED(m_pd3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_frameContexts[0].CommandAllocator, nullptr, IID_PPV_ARGS(&m_pd3dCommandList)))) + if (FAILED(m_pd3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_frameContexts[0].CommandAllocator.Get(), nullptr, IID_PPV_ARGS(&m_pd3dCommandList)))) { Log::Error("D3D12::InitializeDownlevel() - failed to create command list!"); return ResetState(); @@ -263,7 +251,7 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour { auto& context = m_frameContexts[i]; context.BackBuffer = m_downlevelBackbuffers[i]; - m_pd3d12Device->CreateRenderTargetView(context.BackBuffer, nullptr, context.MainRenderTargetDescriptor); + m_pd3d12Device->CreateRenderTargetView(context.BackBuffer.Get(), nullptr, context.MainRenderTargetDescriptor); } if (!InitializeImGui(buffersCounts)) @@ -282,103 +270,167 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour bool D3D12::InitializeImGui(size_t aBuffersCounts) { + std::lock_guard _(m_imguiLock); + + // TODO - scale also by DPI + const auto [resx, resy] = GetResolution(); + const auto scaleFromReference = std::min(static_cast(resx) / 1920.0f, static_cast(resy) / 1080.0f); + if (ImGui::GetCurrentContext() == nullptr) { // do this once, do not repeat context creation! IMGUI_CHECKVERSION(); ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - ImGui::StyleColorsDark(); - - ImFontConfig config; - config.SizePixels = m_options.FontSize; - config.OversampleH = config.OversampleV = 1; - config.PixelSnapH = true; - io.Fonts->AddFontDefault(&config); - + + // TODO - make this configurable eventually and overridable by mods for themselves easily + // setup CET default style + ImGui::StyleColorsDark(&m_styleReference); + m_styleReference.WindowRounding = 6.0f; + m_styleReference.WindowTitleAlign.x = 0.5f; + m_styleReference.ChildRounding = 6.0f; + m_styleReference.PopupRounding = 6.0f; + m_styleReference.FrameRounding = 6.0f; + m_styleReference.ScrollbarRounding = 12.0f; + m_styleReference.GrabRounding = 12.0f; + m_styleReference.TabRounding = 6.0f; + m_styleReference.WindowMenuButtonPosition = ImGuiDir_None; + } + + ImGui::GetStyle() = m_styleReference; + ImGui::GetStyle().ScaleAllSizes(scaleFromReference); + + auto& io = ImGui::GetIO(); + io.Fonts->Clear(); + + ImFontConfig config; + config.SizePixels = std::floorf(m_options.FontSize * scaleFromReference); + config.OversampleH = config.OversampleV = 2; + config.PixelSnapH = true; + config.MergeMode = false; + + // add default font + const auto customFontPath = m_options.FontPath.empty() ? std::filesystem::path{} : GetAbsolutePath(UTF8ToUTF16(m_options.FontPath), m_paths.Fonts(), false); + auto cetFontPath = GetAbsolutePath(L"NotoSans-Regular.ttf", m_paths.Fonts(), false); + const auto* cpGlyphRanges = io.Fonts->GetGlyphRangesDefault(); + if (customFontPath.empty()) + { if (!m_options.FontPath.empty()) + Log::Warn("D3D12::InitializeImGui() - Custom font path is invalid! Using default CET font."); + + if (cetFontPath.empty()) + { + Log::Warn("D3D12::InitializeImGui() - Missing default fonts!"); + io.Fonts->AddFontDefault(&config); + } + else + io.Fonts->AddFontFromFileTTF(UTF16ToUTF8(cetFontPath.native()).c_str(), config.SizePixels, &config, cpGlyphRanges); + } + else + io.Fonts->AddFontFromFileTTF(UTF16ToUTF8(customFontPath.native()).c_str(), config.SizePixels, &config, cpGlyphRanges); + + if (m_options.FontGlyphRanges == "ChineseFull") + { + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSansTC-Regular.otf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesChineseFull(); + } + else if (m_options.FontGlyphRanges == "ChineseSimplifiedCommon") + { + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSansSC-Regular.otf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesChineseSimplifiedCommon(); + } + else if (m_options.FontGlyphRanges == "Japanese") + { + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSansJP-Regular.otf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesJapanese(); + } + else if (m_options.FontGlyphRanges == "Korean") + { + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSansKR-Regular.otf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesKorean(); + } + else if (m_options.FontGlyphRanges == "Cyrillic") + { + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSans-Regular.ttf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesCyrillic(); + } + else if (m_options.FontGlyphRanges == "Thai") + { + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSansThai-Regular.ttf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesThai(); + } + else if (m_options.FontGlyphRanges == "Vietnamese") + { + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSans-Regular.ttf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesVietnamese(); + } + else + { + switch (GetSystemDefaultLangID()) { - std::filesystem::path fontPath(m_options.FontPath); - if (!fontPath.is_absolute()) - { - fontPath = m_paths.CETRoot() / fontPath; - } - if (exists(fontPath)) - { - const ImWchar* cpGlyphRanges = io.Fonts->GetGlyphRangesDefault(); - if (m_options.FontGlyphRanges == "System") - { - int langID = GetSystemDefaultLangID(); - - switch (langID) - { - case MAKELANGID(LANG_BELARUSIAN, SUBLANG_DEFAULT): - case MAKELANGID(LANG_RUSSIAN, SUBLANG_DEFAULT): - cpGlyphRanges = io.Fonts->GetGlyphRangesCyrillic(); - break; - - case MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT): - cpGlyphRanges = io.Fonts->GetGlyphRangesJapanese(); - break; - - case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL): - cpGlyphRanges = io.Fonts->GetGlyphRangesChineseFull(); - break; - - case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED): - cpGlyphRanges = io.Fonts->GetGlyphRangesChineseSimplifiedCommon(); - break; - - case MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT): - cpGlyphRanges = io.Fonts->GetGlyphRangesKorean(); - break; - - case MAKELANGID(LANG_THAI, SUBLANG_DEFAULT): - cpGlyphRanges = io.Fonts->GetGlyphRangesThai(); - break; - - case MAKELANGID(LANG_VIETNAMESE, SUBLANG_DEFAULT): - cpGlyphRanges = io.Fonts->GetGlyphRangesVietnamese(); - break; - - default: - cpGlyphRanges = io.Fonts->GetGlyphRangesDefault(); - break; - } - } - else if (m_options.FontGlyphRanges == "ChineseFull") - cpGlyphRanges = io.Fonts->GetGlyphRangesChineseFull(); - else if (m_options.FontGlyphRanges == "ChineseSimplifiedCommon") - cpGlyphRanges = io.Fonts->GetGlyphRangesChineseSimplifiedCommon(); - else if (m_options.FontGlyphRanges == "Japanese") - cpGlyphRanges = io.Fonts->GetGlyphRangesJapanese(); - else if (m_options.FontGlyphRanges == "Korean") - cpGlyphRanges = io.Fonts->GetGlyphRangesKorean(); - else if (m_options.FontGlyphRanges == "Cyrillic") - cpGlyphRanges = io.Fonts->GetGlyphRangesCyrillic(); - else if (m_options.FontGlyphRanges == "Thai") - cpGlyphRanges = io.Fonts->GetGlyphRangesThai(); - else if (m_options.FontGlyphRanges == "Vietnamese") - cpGlyphRanges = io.Fonts->GetGlyphRangesVietnamese(); - ImFont* pFont = - io.Fonts->AddFontFromFileTTF(fontPath.string().c_str(), m_options.FontSize, nullptr, cpGlyphRanges); - - if (pFont != nullptr) - { - io.FontDefault = pFont; - } - } + case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL): + cetFontPath = GetAbsolutePath(L"NotoSansTC-Regular.otf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesChineseFull(); + break; + + case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED): + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSansSC-Regular.otf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesChineseSimplifiedCommon(); + break; + + case MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT): + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSansJP-Regular.otf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesJapanese(); + break; + + case MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT): + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSansKR-Regular.otf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesKorean(); + break; + + case MAKELANGID(LANG_BELARUSIAN, SUBLANG_DEFAULT): + case MAKELANGID(LANG_RUSSIAN, SUBLANG_DEFAULT): + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSans-Regular.ttf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesCyrillic(); + break; + + case MAKELANGID(LANG_THAI, SUBLANG_DEFAULT): + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSansThai-Regular.ttf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesThai(); + break; + + case MAKELANGID(LANG_VIETNAMESE, SUBLANG_DEFAULT): + cetFontPath = GetAbsolutePath(m_paths.Fonts() / L"NotoSans-Regular.ttf", m_paths.Fonts(), false); + cpGlyphRanges = io.Fonts->GetGlyphRangesVietnamese(); + break; } } - - if (!ImGui_ImplWin32_Init(m_window.GetWindow())) + + // add extra glyphs from language font + config.MergeMode = true; + if (customFontPath.empty()) + { + if (!m_options.FontPath.empty()) + Log::Warn("D3D12::InitializeImGui() - Custom font path is invalid! Using default CET font."); + + if (cetFontPath.empty()) + { + Log::Warn("D3D12::InitializeImGui() - Missing fonts for extra language glyphs!"); + io.Fonts->AddFontDefault(&config); + } + else + io.Fonts->AddFontFromFileTTF(UTF16ToUTF8(cetFontPath.native()).c_str(), config.SizePixels, &config, cpGlyphRanges); + } + else + io.Fonts->AddFontFromFileTTF(UTF16ToUTF8(customFontPath.native()).c_str(), config.SizePixels, &config, cpGlyphRanges); + + if (!ImGui_ImplWin32_Init(m_window.GetWindow())) { Log::Error("D3D12::InitializeImGui() - ImGui_ImplWin32_Init call failed!"); return false; } - if (!ImGui_ImplDX12_Init(m_pd3d12Device, static_cast(aBuffersCounts), - DXGI_FORMAT_R8G8B8A8_UNORM, m_pd3dSrvDescHeap, + if (!ImGui_ImplDX12_Init(m_pd3d12Device.Get(), static_cast(aBuffersCounts), + DXGI_FORMAT_R8G8B8A8_UNORM, m_pd3dSrvDescHeap.Get(), m_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), m_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart())) { @@ -387,7 +439,7 @@ bool D3D12::InitializeImGui(size_t aBuffersCounts) return false; } - if (!ImGui_ImplDX12_CreateDeviceObjects(m_pCommandQueue)) + if (!ImGui_ImplDX12_CreateDeviceObjects(m_pCommandQueue.Get())) { Log::Error("D3D12::InitializeImGui() - ImGui_ImplDX12_CreateDeviceObjects call failed!"); ImGui_ImplDX12_Shutdown(); @@ -398,40 +450,91 @@ bool D3D12::InitializeImGui(size_t aBuffersCounts) return true; } -void D3D12::Update() +bool D3D12::IsImGuiPresentDraw() const { - ImGui_ImplDX12_NewFrame(m_pCommandQueue); + return m_imguiPresentDraw; +} + +void D3D12::PrepareUpdate(bool aPrepareMods) +{ + std::lock_guard _(m_imguiLock); + ImGui_ImplWin32_NewFrame(m_outSize); ImGui::NewFrame(); - OnUpdate.Emit(); + CET::Get().GetOverlay().Update(); - const auto bufferIndex = (m_pdxgiSwapChain != nullptr) ? (m_pdxgiSwapChain->GetCurrentBackBufferIndex()) : (m_downlevelBufferIndex); + if (aPrepareMods) + CET::Get().GetVM().Draw(); + + ImGui::Render(); + + auto& drawData = m_imguiDrawDataBuffers[2]; + + for (auto i = 0; i < drawData.CmdListsCount; ++i) + IM_DELETE(drawData.CmdLists[i]); + delete[] drawData.CmdLists; + drawData.CmdLists = nullptr; + drawData.Clear(); + + drawData = *ImGui::GetDrawData(); + + auto** copiedDrawLists = new ImDrawList*[drawData.CmdListsCount]; + for (auto i = 0; i < drawData.CmdListsCount; ++i) + copiedDrawLists[i] = drawData.CmdLists[i]->CloneOutput(); + drawData.CmdLists = copiedDrawLists; + + std::swap(m_imguiDrawDataBuffers[1], m_imguiDrawDataBuffers[2]); +} + +void D3D12::Update() +{ + if (m_imguiPresentDraw) + { + PrepareUpdate(false); + m_imguiPresentDraw = !CET::Get().GetVM().IsInitialized(); + } + + // swap staging ImGui buffer with render ImGui buffer + { + std::lock_guard _(m_imguiLock); + ImGui_ImplDX12_NewFrame(m_pCommandQueue.Get()); + if (m_imguiDrawDataBuffers[1].Valid) + { + std::swap(m_imguiDrawDataBuffers[0], m_imguiDrawDataBuffers[1]); + m_imguiDrawDataBuffers[1].Valid = false; + } + } + + assert(m_imguiDrawDataBuffers[0].Valid); + + const auto bufferIndex = m_pdxgiSwapChain != nullptr ? m_pdxgiSwapChain->GetCurrentBackBufferIndex() : m_downlevelBufferIndex; auto& frameContext = m_frameContexts[bufferIndex]; frameContext.CommandAllocator->Reset(); D3D12_RESOURCE_BARRIER barrier; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = frameContext.BackBuffer; + barrier.Transition.pResource = frameContext.BackBuffer.Get(); barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; - m_pd3dCommandList->Reset(frameContext.CommandAllocator, nullptr); + ID3D12DescriptorHeap* heaps[] = { m_pd3dSrvDescHeap.Get() }; + + m_pd3dCommandList->Reset(frameContext.CommandAllocator.Get(), nullptr); m_pd3dCommandList->ResourceBarrier(1, &barrier); + m_pd3dCommandList->SetDescriptorHeaps(1, heaps); m_pd3dCommandList->OMSetRenderTargets(1, &frameContext.MainRenderTargetDescriptor, FALSE, nullptr); - m_pd3dCommandList->SetDescriptorHeaps(1, &m_pd3dSrvDescHeap); - ImGui::Render(); - ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), m_pd3dCommandList); + ImGui_ImplDX12_RenderDrawData(&m_imguiDrawDataBuffers[0], m_pd3dCommandList.Get()); barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; - m_pd3dCommandList->ResourceBarrier(1, &barrier); m_pd3dCommandList->Close(); - m_pCommandQueue->ExecuteCommandLists(1, reinterpret_cast(&m_pd3dCommandList)); + ID3D12CommandList* commandLists[] = { m_pd3dCommandList.Get() }; + m_pCommandQueue->ExecuteCommandLists(1, commandLists); } diff --git a/src/d3d12/D3D12_Hooks.cpp b/src/d3d12/D3D12_Hooks.cpp index aa2eb9c1..1caa43c0 100644 --- a/src/d3d12/D3D12_Hooks.cpp +++ b/src/d3d12/D3D12_Hooks.cpp @@ -8,19 +8,7 @@ #include -HRESULT D3D12::ResizeBuffers(IDXGISwapChain* apSwapChain, UINT aBufferCount, UINT aWidth, UINT aHeight, DXGI_FORMAT aNewFormat, UINT aSwapChainFlags) -{ - auto& d3d12 = CET::Get().GetD3D12(); - - if (d3d12.m_initialized) - { - // NOTE: right now, done in case of any swap chain ResizeBuffers call, which may not be ideal. We have yet to encounter multiple swap chains in use though, so should be safe - Log::Info("D3D12::ResizeBuffers() called with initialized D3D12, triggering D3D12::ResetState."); - d3d12.ResetState(); - } - - return d3d12.m_realResizeBuffersD3D12(apSwapChain, aBufferCount, aWidth, aHeight, aNewFormat, aSwapChainFlags); -} +#include HRESULT D3D12::PresentDownlevel(ID3D12CommandQueueDownlevel* apCommandQueueDownlevel, ID3D12GraphicsCommandList* apOpenCommandList, ID3D12Resource* apSourceTex2D, HWND ahWindow, D3D12_DOWNLEVEL_PRESENT_FLAGS aFlags) { @@ -33,7 +21,7 @@ HRESULT D3D12::PresentDownlevel(ID3D12CommandQueueDownlevel* apCommandQueueDownl const auto cbegin = d3d12.m_downlevelBackbuffers.size() >= g_numDownlevelBackbuffersRequired ? d3d12.m_downlevelBackbuffers.cend() - g_numDownlevelBackbuffersRequired : d3d12.m_downlevelBackbuffers.cbegin(); - auto it = std::find(cbegin, d3d12.m_downlevelBackbuffers.cend(), apSourceTex2D); + auto it = std::find_if(cbegin, d3d12.m_downlevelBackbuffers.cend(), [apSourceTex2D](const auto& downlevelBackbuffer){ return downlevelBackbuffer.Get() == apSourceTex2D; }); if (it == d3d12.m_downlevelBackbuffers.cend()) { if (d3d12.m_initialized) @@ -55,7 +43,7 @@ HRESULT D3D12::PresentDownlevel(ID3D12CommandQueueDownlevel* apCommandQueueDownl // Determine the current buffer index d3d12.m_downlevelBufferIndex = static_cast(std::distance(d3d12.m_downlevelBackbuffers.cbegin() + skip, it)); - if (d3d12.InitializeDownlevel(d3d12.m_pCommandQueue, apSourceTex2D, ahWindow)) + if (d3d12.InitializeDownlevel(d3d12.m_pCommandQueue.Get(), apSourceTex2D, ahWindow)) d3d12.Update(); return d3d12.m_realPresentD3D12Downlevel(apCommandQueueDownlevel, apOpenCommandList, apSourceTex2D, ahWindow, aFlags); @@ -68,9 +56,9 @@ HRESULT D3D12::CreateCommittedResource(ID3D12Device* apDevice, const D3D12_HEAP_ // Check if this is a backbuffer resource being created bool isBackBuffer = false; - if (acpHeapProperties != NULL && acpHeapProperties->Type == D3D12_HEAP_TYPE_DEFAULT && aHeapFlags == D3D12_HEAP_FLAG_NONE && - acpDesc != NULL && acpDesc->Flags == D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET && aInitialResourceState == D3D12_RESOURCE_STATE_COMMON && - acpOptimizedClearValue == NULL && acpRIID != NULL && IsEqualGUID(*acpRIID, __uuidof(ID3D12Resource))) + if (acpHeapProperties != nullptr && acpHeapProperties->Type == D3D12_HEAP_TYPE_DEFAULT && aHeapFlags == D3D12_HEAP_FLAG_NONE && + acpDesc != nullptr && acpDesc->Flags == D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET && aInitialResourceState == D3D12_RESOURCE_STATE_COMMON && + acpOptimizedClearValue == nullptr && acpRIID != nullptr && IsEqualGUID(*acpRIID, __uuidof(ID3D12Resource))) { isBackBuffer = true; } @@ -81,7 +69,7 @@ HRESULT D3D12::CreateCommittedResource(ID3D12Device* apDevice, const D3D12_HEAP_ { // Store the returned resource d3d12.m_downlevelBackbuffers.emplace_back(static_cast(*appvResource)); - spdlog::debug("D3D12::CreateCommittedResourceD3D12() - found valid backbuffer target at {0}.", *appvResource); + spdlog::debug("D3D12::CreateCommittedResourceD3D12() - found valid backbuffer target at {}.", *appvResource); if (d3d12.m_initialized) { @@ -99,10 +87,10 @@ void D3D12::ExecuteCommandLists(ID3D12CommandQueue* apCommandQueue, UINT aNumCom auto& d3d12 = CET::Get().GetD3D12(); if (d3d12.m_pCommandQueue == nullptr) { - auto desc = apCommandQueue->GetDesc(); + const auto desc = apCommandQueue->GetDesc(); if (desc.Type == D3D12_COMMAND_LIST_TYPE_DIRECT) { - auto ret = (uintptr_t)_ReturnAddress() - (uintptr_t)GetModuleHandleA(nullptr); + auto ret = reinterpret_cast(_ReturnAddress()) - reinterpret_cast(GetModuleHandleA(nullptr)); d3d12.m_pCommandQueue = apCommandQueue; Log::Info("D3D12::ExecuteCommandListsD3D12() - found valid command queue. {:X}", ret); } @@ -116,51 +104,80 @@ void* ApplyHook(void** vtable, size_t index, void* target) { DWORD oldProtect; VirtualProtect(vtable + index, 8, PAGE_EXECUTE_READWRITE, &oldProtect); - auto ret = vtable[index]; + const auto ret = vtable[index]; vtable[index] = target; VirtualProtect(vtable + index, 8, oldProtect, nullptr); return ret; } -void* D3D12::CRenderNode_Present_InternalPresent(int32_t* apSomeInt, uint8_t aSomeSync, UINT aSyncInterval) +void* D3D12::CRenderNode_Present_InternalPresent(int32_t* apDeviceIndex, uint8_t aSomeSync, UINT aSyncInterval) { + static std::once_flag s_kieroOnce; + auto& d3d12 = CET::Get().GetD3D12(); - if (!kiero::isDownLevelDevice()) + const auto* pContext = RenderContext::GetInstance(); + auto* pSwapChain = pContext->devices[*apDeviceIndex - 1].pSwapChain; + if (d3d12.m_initialized) + d3d12.Update(); + else { - const auto idx = *apSomeInt - 1; - - const auto* pContext = RenderContext::GetInstance(); - if (pContext->unkED69C0 == nullptr) + // NOTE: checking against Windows 8 as Windows 10 requires specific compatibility manifest to be detected by these + // DX12 does not work on Windows 8 and 8.1 so we should be safe with this check + if (IsWindows8OrGreater()) { - auto* pDevice = pContext->devices[idx].pSwapChain; d3d12.m_pCommandQueue = pContext->pDirectQueue; - - if (d3d12.Initialize(pDevice)) - d3d12.Update(); + d3d12.m_pdxgiSwapChain = pSwapChain; + d3d12.Initialize(); + } + else + { + std::call_once(s_kieroOnce, [] { + if (kiero::init() != kiero::Status::Success) + Log::Error("Kiero failed!"); + else + { + std::string_view d3d12type = kiero::isDownLevelDevice() ? "D3D12on7" : "D3D12"; + Log::Info("Kiero initialized for {}", d3d12type); + + CET::Get().GetD3D12().Hook(); + } + }); } } - return d3d12.m_realInternalPresent(apSomeInt, aSomeSync, aSyncInterval); + return d3d12.m_realInternalPresent(apDeviceIndex, aSomeSync, aSyncInterval); } -void* D3D12::CRenderGlobal_Resize(uint32_t a1, uint32_t a2, uint32_t a3, uint8_t a4, int* a5) +void* D3D12::CRenderGlobal_Resize(uint32_t aWidth, uint32_t aHeight, uint32_t a3, uint8_t a4, int* apDeviceIndex) { auto& d3d12 = CET::Get().GetD3D12(); + // TODO - ideally find a way to not call this on each minimize/maximize/etc. which causes this to be called + // it can get called multiple times even when there was no resolution change or swapchain invalidation if (d3d12.m_initialized) { Log::Info("CRenderGlobal::Resize() called with initialized D3D12, triggering D3D12::ResetState."); d3d12.ResetState(); } - return d3d12.m_realInternalResize(a1, a2, a3, a4, a5); + return d3d12.m_realInternalResize(aWidth, aHeight, a3, a4, apDeviceIndex); +} + +// NOTE - this is called 32 times, as it seems to be called for each device object in RendererContext +void* D3D12::CRenderGlobal_Shutdown(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4) +{ + auto& d3d12 = CET::Get().GetD3D12(); + + d3d12.ResetState(true, true); + + return d3d12.m_realInternalShutdown(a1, a2, a3, a4); } ID3D12Device* D3D12::GetDevice() const { - return m_pd3d12Device; + return m_pd3d12Device.Get(); } std::tuple D3D12::CreateTextureDescriptor() @@ -174,9 +191,9 @@ std::tuple D3D12::Crea return {{}, {}}; D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = m_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(); - cpuHandle.ptr += (handle_increment * index); + cpuHandle.ptr += handle_increment * index; D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = m_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart(); - gpuHandle.ptr += (handle_increment * index); + gpuHandle.ptr += handle_increment * index; return {cpuHandle, gpuHandle}; } @@ -188,7 +205,7 @@ void D3D12::Hook() int d3d12FailedHooksCount = 0; int d3d12CompleteHooksCount = 0; - if (kiero::bind(175, reinterpret_cast(&m_realPresentD3D12Downlevel), &PresentDownlevel) != + if (kiero::bind(175, reinterpret_cast(&m_realPresentD3D12Downlevel), reinterpret_cast(&PresentDownlevel)) != kiero::Status::Success) { Log::Error("D3D12on7: Downlevel Present hook failed!"); @@ -200,7 +217,7 @@ void D3D12::Hook() ++d3d12CompleteHooksCount; } - if (kiero::bind(27, reinterpret_cast(&m_realCreateCommittedResource), &CreateCommittedResource) != + if (kiero::bind(27, reinterpret_cast(&m_realCreateCommittedResource), reinterpret_cast(&CreateCommittedResource)) != kiero::Status::Success) { Log::Error("D3D12on7: CreateCommittedResource Hook failed!"); @@ -212,7 +229,7 @@ void D3D12::Hook() ++d3d12CompleteHooksCount; } - if (kiero::bind(54, reinterpret_cast(&m_realExecuteCommandLists), &ExecuteCommandLists) != + if (kiero::bind(54, reinterpret_cast(&m_realExecuteCommandLists), reinterpret_cast(&ExecuteCommandLists)) != kiero::Status::Success) { Log::Error("D3D12on7: ExecuteCommandLists hook failed!"); @@ -225,10 +242,10 @@ void D3D12::Hook() } if (d3d12FailedHooksCount == 0) - Log::Info("D3D12on7: hook complete. ({1}/{2})", d3d12CompleteHooksCount, + Log::Info("D3D12on7: hook complete. ({}/{})", d3d12CompleteHooksCount, d3d12CompleteHooksCount + d3d12FailedHooksCount); else - Log::Error("D3D12on7: hook failed! ({1}/{2})", d3d12CompleteHooksCount, + Log::Error("D3D12on7: hook failed! ({}/{})", d3d12CompleteHooksCount, d3d12CompleteHooksCount + d3d12FailedHooksCount); } else @@ -239,19 +256,27 @@ void D3D12::HookGame() { const RED4ext::RelocPtr presentInternal(CyberEngineTweaks::Addresses::CRenderNode_Present_DoInternal); const RED4ext::RelocPtr resizeInternal(CyberEngineTweaks::Addresses::CRenderGlobal_Resize); + const RED4ext::RelocPtr shutdownInternal(CyberEngineTweaks::Addresses::CRenderGlobal_Shutdown); - if (MH_CreateHook(presentInternal.GetAddr(), &CRenderNode_Present_InternalPresent, + if (MH_CreateHook(presentInternal.GetAddr(), reinterpret_cast(&CRenderNode_Present_InternalPresent), reinterpret_cast(&m_realInternalPresent)) != MH_OK || MH_EnableHook(presentInternal.GetAddr()) != MH_OK) Log::Error("Could not hook CRenderNode_Present_InternalPresent function!"); else Log::Info("CRenderNode_Present_InternalPresent function hook complete!"); - if (MH_CreateHook(resizeInternal.GetAddr(), &CRenderGlobal_Resize, + if (MH_CreateHook(resizeInternal.GetAddr(), reinterpret_cast(&CRenderGlobal_Resize), reinterpret_cast(&m_realInternalResize)) != MH_OK || MH_EnableHook(resizeInternal.GetAddr()) != MH_OK) Log::Error("Could not hook CRenderGlobal_Resize function!"); else Log::Info("CRenderGlobal_Resize function hook complete!"); + + if (MH_CreateHook(shutdownInternal.GetAddr(), reinterpret_cast(&CRenderGlobal_Shutdown), + reinterpret_cast(&m_realInternalShutdown)) != MH_OK || + MH_EnableHook(shutdownInternal.GetAddr()) != MH_OK) + Log::Error("Could not hook CRenderGlobal_Shutdown function!"); + else + Log::Info("CRenderGlobal_Shutdown function hook complete!"); } diff --git a/src/dllmain.cpp b/src/dllmain.cpp index d55a357d..bfbcc99b 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -2,22 +2,21 @@ #include "CET.h" -#include "Image.h" #include "Options.h" -#ifndef NDEBUG +#ifdef CET_DEBUG #include "scripting/GameHooks.h" #endif -void EnableDebugPatch(const Image* apImage); -void StartScreenPatch(const Image* apImage); -void RemovePedsPatch(const Image* apImage); -void OptionsInitHook(const Image* apImage); -void DisableIntroMoviesPatch(const Image* apImage); -void DisableVignettePatch(const Image* apImage); -void DisableBoundaryTeleportPatch(const Image* apImage); -void SmtAmdPatch(const Image* apImage); -void MinimapFlickerPatch(const Image* apImage); +void EnableDebugPatch(); +void StartScreenPatch(); +void RemovePedsPatch(); +void OptionsInitHook(); +void DisableIntroMoviesPatch(); +void DisableVignettePatch(); +void DisableBoundaryTeleportPatch(); +void SmtAmdPatch(); +void MinimapFlickerPatch(); static HANDLE s_modInstanceMutex = nullptr; @@ -34,45 +33,45 @@ static void Initialize() const auto& options = CET::Get().GetOptions(); // single instance check - s_modInstanceMutex = CreateMutex(NULL, TRUE, _T("Cyber Engine Tweaks Module Instance")); + s_modInstanceMutex = CreateMutex(nullptr, TRUE, _T("Cyber Engine Tweaks Module Instance")); if (s_modInstanceMutex == nullptr) return; // initialize patches if (options.PatchEnableDebug) - EnableDebugPatch(&options.GameImage); + EnableDebugPatch(); if (options.PatchSkipStartMenu) - StartScreenPatch(&options.GameImage); + StartScreenPatch(); if (options.PatchRemovePedestrians) - RemovePedsPatch(&options.GameImage); + RemovePedsPatch(); if (options.PatchDisableIntroMovies) - DisableIntroMoviesPatch(&options.GameImage); + DisableIntroMoviesPatch(); if (options.PatchDisableVignette) - DisableVignettePatch(&options.GameImage); + DisableVignettePatch(); if (options.PatchDisableBoundaryTeleport) - DisableBoundaryTeleportPatch(&options.GameImage); + DisableBoundaryTeleportPatch(); if (options.PatchAmdSmt) - SmtAmdPatch(&options.GameImage); + SmtAmdPatch(); if (options.PatchMinimapFlicker) - MinimapFlickerPatch(&options.GameImage); + MinimapFlickerPatch(); - OptionsInitHook(&options.GameImage); + OptionsInitHook(); -#ifndef NDEBUG +#ifdef CET_DEBUG // We only need to hook the game thread right now to do RTTI Dump, which is Debug-only // if we need to queue tasks to the mainthread remove the debug check GameMainThread::Initialize(); #endif - MH_EnableHook(MH_ALL_HOOKS); + MH_EnableHook(nullptr); } catch(...) {} @@ -86,7 +85,7 @@ static void Shutdown() { inGameProcess = CET::Get().GetOptions().ExeValid; - MH_DisableHook(MH_ALL_HOOKS); + MH_DisableHook(nullptr); MH_Uninitialize(); CET::Shutdown(); @@ -102,11 +101,11 @@ static void Shutdown() } } -BOOL APIENTRY DllMain(HMODULE mod, DWORD ul_reason_for_call, LPVOID) +BOOL APIENTRY DllMain(HMODULE mod, DWORD ul_reason_for_call, LPVOID) { DisableThreadLibraryCalls(mod); - switch(ul_reason_for_call) + switch(ul_reason_for_call) { case DLL_PROCESS_ATTACH: Initialize(); diff --git a/src/imgui_impl/dx12.cpp b/src/imgui_impl/dx12.cpp index 1c8281ca..30dbd74a 100644 --- a/src/imgui_impl/dx12.cpp +++ b/src/imgui_impl/dx12.cpp @@ -462,8 +462,8 @@ bool ImGui_ImplDX12_CreateDeviceObjects(ID3D12CommandQueue* apCommandQueue) // If failed, we should be on Windows 10. if (d3d12_dll == NULL) - d3d12_dll = ::LoadLibrary(_T("d3d12.dll")); - + d3d12_dll = ::LoadLibrary(_T("d3d12.dll")); + if (d3d12_dll == NULL) return false; } diff --git a/src/imgui_impl/dx12.h b/src/imgui_impl/dx12.h index 9e39b775..8d1a3f42 100644 --- a/src/imgui_impl/dx12.h +++ b/src/imgui_impl/dx12.h @@ -16,7 +16,6 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API -enum DXGI_FORMAT; struct ID3D12Device; struct ID3D12DescriptorHeap; struct ID3D12GraphicsCommandList; diff --git a/src/imgui_impl/imgui_user_config.cpp b/src/imgui_impl/imgui_user_config.cpp index 3bb621d7..48444091 100644 --- a/src/imgui_impl/imgui_user_config.cpp +++ b/src/imgui_impl/imgui_user_config.cpp @@ -2,7 +2,7 @@ // NOTE: imgui_user_config.h is included by imgui.h which is included with precompiled header, so no need to include it here once more -// global definition "Enable ImGui Assertions" +// global definition "Enable ImGui Assertions Logging" bool g_ImGuiAssertionsEnabled{ false }; #ifdef NDEBUG @@ -10,10 +10,14 @@ bool g_ImGuiAssertionsEnabled{ false }; extern "C" _ACRTIMP void __cdecl _wassert(wchar_t const* _Message, wchar_t const* _File, unsigned _Line); #endif -// runtime assertions which can be enabled/disabled inside CET options +// runtime assertions which can be enabled/disabled inside CET options, always logged into main log file when they happen void ImGuiAssert(wchar_t const* acpMessage, wchar_t const* acpFile, unsigned aLine) { - // TODO - it looks like assertions dont get logged for some weird reason, even though there is flush_on set for errors (even higher for debug) - spdlog::error(L"ImGui assertion failed in file \"{ 0 }\" at line { 1 }! Expression ({ 2 }) evaluates to false!", acpFile, aLine, acpMessage); + // TODO - make this log to log of the one who caused assertion instead of default log! + spdlog::error(L"ImGui assertion failed in file \"{}\" at line {}! Expression ({}) evaluates to false!", acpFile, aLine, acpMessage); + +#ifdef CET_DEBUG + // we want to truly assert only in debug build, as it is causing confusion when users enable it... _wassert(acpMessage, acpFile, aLine); +#endif } diff --git a/src/imgui_impl/imgui_user_config.h b/src/imgui_impl/imgui_user_config.h index dbd89d92..699e276a 100644 --- a/src/imgui_impl/imgui_user_config.h +++ b/src/imgui_impl/imgui_user_config.h @@ -7,6 +7,6 @@ extern bool g_ImGuiAssertionsEnabled; void ImGuiAssert(wchar_t const* acpMessage, wchar_t const* acpFile, unsigned aLine); // custom assertion function macro for ImGui -#define IM_ASSERT(expression) (void)( \ - (g_ImGuiAssertionsEnabled && ((!!(expression)) || \ +#define IM_ASSERT(expression) (void)( \ + (g_ImGuiAssertionsEnabled && ((!!(expression)) || \ (ImGuiAssert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0)))) \ No newline at end of file diff --git a/src/imgui_impl/win32.cpp b/src/imgui_impl/win32.cpp index 83e05d3e..df55a8d7 100644 --- a/src/imgui_impl/win32.cpp +++ b/src/imgui_impl/win32.cpp @@ -16,6 +16,7 @@ #include "win32.h" #include "CET.h" +#include "Utils.h" // CHANGELOG // (minor and older changes stripped away, please see git history for details) @@ -68,7 +69,7 @@ bool ImGui_ImplWin32_Init(HWND ahWnd) io.ImeWindowHandle = ahWnd; // Setup ini path - g_LayoutPath = (CET::Get().GetPaths().CETRoot() / "layout.ini").string(); + g_LayoutPath = UTF16ToUTF8(GetAbsolutePath(L"layout.ini", CET::Get().GetPaths().CETRoot(), true).native()); io.IniFilename = g_LayoutPath.c_str(); // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. @@ -256,12 +257,12 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND ahWnd, UINT auMsg, WP case WM_KEYDOWN: case WM_SYSKEYDOWN: if (awParam < 256) - io.KeysDown[awParam] = 1; + io.KeysDown[awParam] = true; return 0; case WM_KEYUP: case WM_SYSKEYUP: if (awParam < 256) - io.KeysDown[awParam] = 0; + io.KeysDown[awParam] = false; return 0; case WM_CHAR: // You can also use ToAscii()+GetKeyboardState() to retrieve characters. @@ -272,6 +273,10 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND ahWnd, UINT auMsg, WP if (LOWORD(alParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor()) return 1; return 0; + case WM_KILLFOCUS: + std::fill_n(io.KeysDown, std::size(io.KeysDown), 0); + std::fill_n(io.MouseDown, std::size(io.MouseDown), 0); + return 0; } return 0; } diff --git a/src/kiero/kiero.cpp b/src/kiero/kiero.cpp index 340257df..bbf17646 100644 --- a/src/kiero/kiero.cpp +++ b/src/kiero/kiero.cpp @@ -1,7 +1,7 @@ #include #include "kiero.h" -#include +#include #include #include "common/D3D12Downlevel.h" @@ -9,7 +9,7 @@ #include static bool g_kieroInitialized = false; -static uint150_t* g_methodsTable = NULL; +static uint150_t* g_methodsTable = nullptr; static void** g_swapChainVtable = nullptr; static void** g_commandListVtable = nullptr; static void** g_commandQueueVtable = nullptr; @@ -20,20 +20,20 @@ kiero::Status::Enum kiero::init() { if (g_kieroInitialized) return Status::AlreadyInitializedError; - + WNDCLASSEX windowClass; windowClass.cbSize = sizeof(WNDCLASSEX); windowClass.style = CS_HREDRAW | CS_VREDRAW; windowClass.lpfnWndProc = DefWindowProc; windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0; - windowClass.hInstance = GetModuleHandle(NULL); - windowClass.hIcon = NULL; - windowClass.hCursor = NULL; - windowClass.hbrBackground = NULL; - windowClass.lpszMenuName = NULL; + windowClass.hInstance = GetModuleHandle(nullptr); + windowClass.hIcon = nullptr; + windowClass.hCursor = nullptr; + windowClass.hbrBackground = nullptr; + windowClass.lpszMenuName = nullptr; windowClass.lpszClassName = _T("Kiero"); - windowClass.hIconSm = NULL; + windowClass.hIconSm = nullptr; ::RegisterClassEx(&windowClass); @@ -41,14 +41,14 @@ kiero::Status::Enum kiero::init() HMODULE libDXGI; HMODULE libD3D12; - if ((libDXGI = ::GetModuleHandle(_T("dxgi.dll"))) == NULL) + if ((libDXGI = ::GetModuleHandle(_T("dxgi.dll"))) == nullptr) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::ModuleNotFoundError; } - if ((libD3D12 = ::GetModuleHandle(_T("d3d12.dll"))) == NULL) + if ((libD3D12 = ::GetModuleHandle(_T("d3d12.dll"))) == nullptr) { const TCHAR* localD3d12on7Paths[] = { @@ -59,58 +59,58 @@ kiero::Status::Enum kiero::init() for (uint32_t i = 0; i < std::size(localD3d12on7Paths); i++) { libD3D12 = LoadLibrary(localD3d12on7Paths[i]); - if (libD3D12 != NULL) + if (libD3D12 != nullptr) break; } - if (libD3D12 == NULL) + if (libD3D12 == nullptr) { - if ((libD3D12 = ::LoadLibrary(_T("d3d12.dll"))) == NULL) + if ((libD3D12 = ::LoadLibrary(_T("d3d12.dll"))) == nullptr) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::ModuleNotFoundError; } } } void* CreateDXGIFactory; - if ((CreateDXGIFactory = reinterpret_cast(GetProcAddress(libDXGI, "CreateDXGIFactory"))) == NULL) + if ((CreateDXGIFactory = reinterpret_cast(GetProcAddress(libDXGI, "CreateDXGIFactory"))) == nullptr) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::UnknownError; } - CComPtr factory; + Microsoft::WRL::ComPtr factory; if (reinterpret_cast(CreateDXGIFactory)(IID_PPV_ARGS(&factory)) < 0) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::UnknownError; } - CComPtr adapter; + Microsoft::WRL::ComPtr adapter; if (factory->EnumAdapters(0, &adapter) == DXGI_ERROR_NOT_FOUND) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::UnknownError; } void* D3D12CreateDevice; - if ((D3D12CreateDevice = reinterpret_cast(GetProcAddress(libD3D12, "D3D12CreateDevice"))) == NULL) + if ((D3D12CreateDevice = reinterpret_cast(GetProcAddress(libD3D12, "D3D12CreateDevice"))) == nullptr) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::UnknownError; } - CComPtr device; - if (reinterpret_cast(D3D12CreateDevice)(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device)) < 0) + Microsoft::WRL::ComPtr device; + if (reinterpret_cast(D3D12CreateDevice)(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device)) < 0) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::UnknownError; } @@ -120,34 +120,34 @@ kiero::Status::Enum kiero::init() queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.NodeMask = 0; - CComPtr commandQueue; + Microsoft::WRL::ComPtr commandQueue; if (device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)) < 0) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::UnknownError; } - CComPtr commandAllocator; + Microsoft::WRL::ComPtr commandAllocator; if (device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator)) < 0) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::UnknownError; } - CComPtr commandList; - if (device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator, NULL, IID_PPV_ARGS(&commandList)) < 0) + Microsoft::WRL::ComPtr commandList; + if (device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator.Get(), nullptr, IID_PPV_ARGS(&commandList)) < 0) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::UnknownError; } - CComPtr downlevelDevice; + Microsoft::WRL::ComPtr downlevelDevice; g_isDownLevelDevice = device->QueryInterface(IID_PPV_ARGS(&downlevelDevice)) >= 0; - CComPtr swapChain; - CComPtr commandQueueDownlevel; + Microsoft::WRL::ComPtr swapChain; + Microsoft::WRL::ComPtr commandQueueDownlevel; if (!g_isDownLevelDevice) { @@ -177,23 +177,23 @@ kiero::Status::Enum kiero::init() swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; - CComPtr swapChain1; - if (factory->CreateSwapChain(commandQueue, &swapChainDesc, &swapChain1) < 0) + Microsoft::WRL::ComPtr swapChain1; + if (factory->CreateSwapChain(commandQueue.Get(), &swapChainDesc, &swapChain1) < 0) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::UnknownError; } if (FAILED(swapChain1->QueryInterface(IID_PPV_ARGS(&swapChain)))) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::UnknownError; } - auto valueToFind = reinterpret_cast(static_cast(commandQueue)); - auto* swapChainPtr = static_cast(static_cast(swapChain)); + auto valueToFind = reinterpret_cast(static_cast(commandQueue.Get())); + auto* swapChainPtr = static_cast(static_cast(swapChain.Get())); auto* addr = std::find(swapChainPtr, swapChainPtr + 512, valueToFind); g_commandQueueOffset = reinterpret_cast(addr) - reinterpret_cast(swapChainPtr); @@ -202,36 +202,36 @@ kiero::Status::Enum kiero::init() { if (commandQueue->QueryInterface(IID_PPV_ARGS(&commandQueueDownlevel)) < 0) { - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); return Status::UnknownError; } - - auto* commandQueueDownlevelPtr = static_cast(static_cast(commandQueueDownlevel)); - auto* addr = std::find(commandQueueDownlevelPtr, commandQueueDownlevelPtr + 512, reinterpret_cast(static_cast(commandQueue))); + + auto* commandQueueDownlevelPtr = static_cast(static_cast(commandQueueDownlevel.Get())); + auto* addr = std::find(commandQueueDownlevelPtr, commandQueueDownlevelPtr + 512, reinterpret_cast(static_cast(commandQueue.Get()))); g_commandQueueOffset = reinterpret_cast(addr) - reinterpret_cast(commandQueueDownlevelPtr); } - g_methodsTable = (uint150_t*)::calloc(176, sizeof(uint150_t)); + g_methodsTable = static_cast(std::calloc(176, sizeof(uint150_t))); if (!g_isDownLevelDevice) - g_swapChainVtable = *(void***)swapChain.operator IDXGISwapChain3 *(); + g_swapChainVtable = *reinterpret_cast(swapChain.Get()); - g_commandListVtable = *(void***)commandList.operator ID3D12GraphicsCommandList *(); - g_commandQueueVtable = *(void***)commandQueue.operator ID3D12CommandQueue*(); + g_commandListVtable = *reinterpret_cast(commandList.Get()); + g_commandQueueVtable = *reinterpret_cast(commandQueue.Get()); - ::memcpy(g_methodsTable, *(uint150_t**)(void*)device, 44 * sizeof(uint150_t)); - ::memcpy(g_methodsTable + 44, *(uint150_t**)(void*)commandQueue, 19 * sizeof(uint150_t)); - ::memcpy(g_methodsTable + 44 + 19, *(uint150_t**)(void*)commandAllocator, 9 * sizeof(uint150_t)); - ::memcpy(g_methodsTable + 44 + 19 + 9, *(uint150_t**)(void*)commandList, 60 * sizeof(uint150_t)); + std::memcpy(g_methodsTable, *static_cast(static_cast(device.Get())), 44 * sizeof(uint150_t)); + std::memcpy(g_methodsTable + 44, *static_cast(static_cast(commandQueue.Get())), 19 * sizeof(uint150_t)); + std::memcpy(g_methodsTable + 44 + 19, *static_cast(static_cast(commandAllocator.Get())), 9 * sizeof(uint150_t)); + std::memcpy(g_methodsTable + 44 + 19 + 9, *static_cast(static_cast(commandList.Get())), 60 * sizeof(uint150_t)); if (!g_isDownLevelDevice) - ::memcpy(g_methodsTable + 44 + 19 + 9 + 60, *(uint150_t**)(void*)swapChain, 40 * sizeof(uint150_t)); + std::memcpy(g_methodsTable + 44 + 19 + 9 + 60, *static_cast(static_cast(swapChain.Get())), 40 * sizeof(uint150_t)); else - ::memcpy(g_methodsTable + 44 + 19 + 9 + 60 + 40, *(uint150_t**)(void*)commandQueueDownlevel, 4 * sizeof(uint150_t)); + std::memcpy(g_methodsTable + 44 + 19 + 9 + 60 + 40, *static_cast(static_cast(commandQueueDownlevel.Get())), 4 * sizeof(uint150_t)); - ::DestroyWindow(window); - ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + DestroyWindow(window); + UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); g_kieroInitialized = true; return Status::Success; @@ -241,10 +241,10 @@ void kiero::shutdown() { if (g_kieroInitialized) { - MH_DisableHook(MH_ALL_HOOKS); + MH_DisableHook(nullptr); - ::free(g_methodsTable); - g_methodsTable = NULL; + std::free(g_methodsTable); + g_methodsTable = nullptr; g_kieroInitialized = false; } } @@ -257,7 +257,7 @@ kiero::Status::Enum kiero::bind(uint16_t _index, void** _original, void* _functi if (g_kieroInitialized) { - void* target = (void*)g_methodsTable[_index]; + auto* target = reinterpret_cast(g_methodsTable[_index]); if (MH_CreateHook(target, _function, _original) != MH_OK || MH_EnableHook(target) != MH_OK) return Status::UnknownError; @@ -271,11 +271,9 @@ void kiero::unbind(uint16_t _index) { if (g_kieroInitialized) { -#if KIERO_USE_MINHOOK - void* target = (void*)g_methodsTable[_index]; + auto* target = (void*)g_methodsTable[_index]; MH_DisableHook(target); MH_RemoveHook(target); -#endif } } diff --git a/src/kiero/kiero.h b/src/kiero/kiero.h index a7dfbbb5..011ef76d 100644 --- a/src/kiero/kiero.h +++ b/src/kiero/kiero.h @@ -1,10 +1,10 @@ #pragma once -#include +#include #define KIERO_VERSION "1.2.10-cet_1.0" -#if defined(_M_X64) +#if defined(_M_X64) #define KIERO_ARCH_X86 0 #define KIERO_ARCH_X64 1 #else diff --git a/src/lsqlite3/lsqlite3.cpp b/src/lsqlite3/lsqlite3.cpp index ef0aa26b..5a3d9d8f 100644 --- a/src/lsqlite3/lsqlite3.cpp +++ b/src/lsqlite3/lsqlite3.cpp @@ -26,9 +26,9 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ************************************************************************/ -#include -#include -#include +#include +#include +#include #define LUA_LIB #include "lua.h" @@ -129,7 +129,7 @@ static const char *sqlite_bu_meta = ":sqlite3:bu"; static const char *sqlite_ctx_meta = ":sqlite3:ctx"; static int sqlite_ctx_meta_ref; -/* Lua 5.3 introduced an integer type, but depending on the implementation, it could be 32 +/* Lua 5.3 introduced an integer type, but depending on the implementation, it could be 32 ** or 64 bits (or something else?). This helper macro tries to do "the right thing." */ @@ -165,17 +165,17 @@ static void vm_push_column(lua_State *L, sqlite3_stmt *vm, int idx) { switch (sqlite3_column_type(vm, idx)) { case SQLITE_INTEGER: PUSH_INT64(L, sqlite3_column_int64(vm, idx) - , lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx) - , sqlite3_column_bytes(vm, idx))); + , lua_pushlstring(L, reinterpret_cast(sqlite3_column_text(vm, idx)) + , sqlite3_column_bytes(vm, idx))); break; case SQLITE_FLOAT: lua_pushnumber(L, sqlite3_column_double(vm, idx)); break; case SQLITE_TEXT: - lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx), sqlite3_column_bytes(vm, idx)); + lua_pushlstring(L, reinterpret_cast(sqlite3_column_text(vm, idx)), sqlite3_column_bytes(vm, idx)); break; case SQLITE_BLOB: - lua_pushlstring(L, (const char*)sqlite3_column_blob(vm, idx), sqlite3_column_bytes(vm, idx)); + lua_pushlstring(L, static_cast(sqlite3_column_blob(vm, idx)), sqlite3_column_bytes(vm, idx)); break; case SQLITE_NULL: lua_pushnil(L); @@ -200,7 +200,7 @@ struct sdb_vm { /* called with db,sql text on the lua stack */ static sdb_vm *newvm(lua_State *L, sdb *db) { - sdb_vm *svm = (sdb_vm*)lua_newuserdata(L, sizeof(sdb_vm)); /* db sql svm_ud -- */ + auto *svm = static_cast(lua_newuserdata(L, sizeof(sdb_vm))); /* db sql svm_ud -- */ luaL_getmetatable(L, sqlite_vm_meta); lua_setmetatable(L, -2); /* set metatable */ @@ -208,7 +208,7 @@ static sdb_vm *newvm(lua_State *L, sdb *db) { svm->db = db; svm->columns = 0; svm->has_values = 0; - svm->vm = NULL; + svm->vm = nullptr; svm->temp = 0; /* add an entry on the database table: svm -> db to keep db live while svm is live */ @@ -238,7 +238,7 @@ static int cleanupvm(lua_State *L, sdb_vm *svm) { if (!svm->vm) return 0; lua_pushinteger(L, sqlite3_finalize(svm->vm)); - svm->vm = NULL; + svm->vm = nullptr; return 1; } @@ -247,37 +247,37 @@ static int stepvm(lua_State *L, sdb_vm *svm) { } static sdb_vm *lsqlite_getvm(lua_State *L, int index) { - sdb_vm *svm = (sdb_vm*)luaL_checkudata(L, index, sqlite_vm_meta); - if (svm == NULL) luaL_argerror(L, index, "bad sqlite virtual machine"); + auto *svm = static_cast(luaL_checkudata(L, index, sqlite_vm_meta)); + if (svm == nullptr) luaL_argerror(L, index, "bad sqlite virtual machine"); return svm; } static sdb_vm *lsqlite_checkvm(lua_State *L, int index) { sdb_vm *svm = lsqlite_getvm(L, index); - if (svm->vm == NULL) luaL_argerror(L, index, "attempt to use closed sqlite virtual machine"); + if (svm->vm == nullptr) luaL_argerror(L, index, "attempt to use closed sqlite virtual machine"); return svm; } static int dbvm_isopen(lua_State *L) { - sdb_vm *svm = lsqlite_getvm(L, 1); - lua_pushboolean(L, svm->vm != NULL ? 1 : 0); + const sdb_vm *svm = lsqlite_getvm(L, 1); + lua_pushboolean(L, svm->vm != nullptr ? 1 : 0); return 1; } static int dbvm_tostring(lua_State *L) { char buff[39]; sdb_vm *svm = lsqlite_getvm(L, 1); - if (svm->vm == NULL) + if (svm->vm == nullptr) strcpy(buff, "closed"); else - sprintf(buff, "%p", svm); + sprintf(buff, "%p", reinterpret_cast(svm)); lua_pushfstring(L, "sqlite virtual machine (%s)", buff); return 1; } static int dbvm_gc(lua_State *L) { sdb_vm *svm = lsqlite_getvm(L, 1); - if (svm->vm != NULL) /* ignore closed vms */ + if (svm->vm != nullptr) /* ignore closed vms */ cleanupvm(L, svm); return 0; } @@ -300,7 +300,7 @@ static int dbvm_finalize(lua_State *L) { } static int dbvm_reset(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); + const sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_reset(svm->vm); lua_pushinteger(L, sqlite3_errcode(svm->db->db)); return 1; @@ -325,9 +325,9 @@ static void dbvm_check_bind_index(lua_State *L, sdb_vm *svm, int index) { } static int dbvm_last_insert_rowid(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); + const sdb_vm *svm = lsqlite_checkvm(L, 1); /* conversion warning: int64 -> luaNumber */ - sqlite_int64 rowid = sqlite3_last_insert_rowid(svm->db->db); + const sqlite_int64 rowid = sqlite3_last_insert_rowid(svm->db->db); PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid)); return 1; } @@ -338,7 +338,7 @@ static int dbvm_last_insert_rowid(lua_State *L) { ** ======================================================= */ static int dbvm_columns(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); + const sdb_vm *svm = lsqlite_checkvm(L, 1); lua_pushinteger(L, sqlite3_column_count(svm->vm)); return 1; } @@ -351,7 +351,7 @@ static int dbvm_columns(lua_State *L) { static int dbvm_get_value(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = luaL_checkint(L, 2); + const int index = luaL_checkint(L, 2); dbvm_check_contents(L, svm); dbvm_check_index(L, svm, index); vm_push_column(L, svm->vm, index); @@ -360,7 +360,7 @@ static int dbvm_get_value(lua_State *L) { static int dbvm_get_name(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = luaL_checknumber(L, 2); + const int index = static_cast(luaL_checknumber(L, 2)); dbvm_check_index(L, svm, index); lua_pushstring(L, sqlite3_column_name(svm->vm, index)); return 1; @@ -368,7 +368,7 @@ static int dbvm_get_name(lua_State *L) { static int dbvm_get_type(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = luaL_checknumber(L, 2); + const int index = static_cast(luaL_checknumber(L, 2)); dbvm_check_index(L, svm, index); lua_pushstring(L, sqlite3_column_decltype(svm->vm, index)); return 1; @@ -377,7 +377,7 @@ static int dbvm_get_type(lua_State *L) { static int dbvm_get_values(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_stmt *vm = svm->vm; - int columns = svm->columns; + const int columns = svm->columns; int n; dbvm_check_contents(L, svm); @@ -390,9 +390,9 @@ static int dbvm_get_values(lua_State *L) { } static int dbvm_get_names(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); + const sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_stmt *vm = svm->vm; - int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ + const int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ int n; lua_createtable(L, columns, 0); @@ -404,9 +404,9 @@ static int dbvm_get_names(lua_State *L) { } static int dbvm_get_types(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); + const sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_stmt *vm = svm->vm; - int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ + const int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ int n; lua_createtable(L, columns, 0); @@ -420,7 +420,7 @@ static int dbvm_get_types(lua_State *L) { static int dbvm_get_uvalues(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_stmt *vm = svm->vm; - int columns = svm->columns; + const int columns = svm->columns; int n; dbvm_check_contents(L, svm); @@ -431,9 +431,9 @@ static int dbvm_get_uvalues(lua_State *L) { } static int dbvm_get_unames(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); + const sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_stmt *vm = svm->vm; - int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ + const int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ int n; lua_checkstack(L, columns); @@ -443,9 +443,9 @@ static int dbvm_get_unames(lua_State *L) { } static int dbvm_get_utypes(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); + const sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_stmt *vm = svm->vm; - int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ + const int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ int n; lua_checkstack(L, columns); @@ -457,7 +457,7 @@ static int dbvm_get_utypes(lua_State *L) { static int dbvm_get_named_values(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_stmt *vm = svm->vm; - int columns = svm->columns; + const int columns = svm->columns; int n; dbvm_check_contents(L, svm); @@ -471,9 +471,9 @@ static int dbvm_get_named_values(lua_State *L) { } static int dbvm_get_named_types(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); + const sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_stmt *vm = svm->vm; - int columns = sqlite3_column_count(vm); + const int columns = sqlite3_column_count(vm); int n; lua_createtable(L, 0, columns); @@ -494,7 +494,7 @@ static int dbvm_get_named_types(lua_State *L) { static int dbvm_bind_index(lua_State *L, sqlite3_stmt *vm, int index, int lindex) { switch (lua_type(L, lindex)) { case LUA_TSTRING: - return sqlite3_bind_text(vm, index, lua_tostring(L, lindex), lua_strlen(L, lindex), SQLITE_TRANSIENT); + return sqlite3_bind_text(vm, index, lua_tostring(L, lindex), static_cast(lua_strlen(L, lindex)), SQLITE_TRANSIENT); case LUA_TNUMBER: #if LUA_VERSION_NUM > 502 if (lua_isinteger(L, lindex)) @@ -514,14 +514,14 @@ static int dbvm_bind_index(lua_State *L, sqlite3_stmt *vm, int index, int lindex static int dbvm_bind_parameter_count(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); + const sdb_vm *svm = lsqlite_checkvm(L, 1); lua_pushinteger(L, sqlite3_bind_parameter_count(svm->vm)); return 1; } static int dbvm_bind_parameter_name(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = luaL_checknumber(L, 2); + const int index = static_cast(luaL_checknumber(L, 2)); dbvm_check_bind_index(L, svm, index); lua_pushstring(L, sqlite3_bind_parameter_name(svm->vm, index)); return 1; @@ -530,7 +530,7 @@ static int dbvm_bind_parameter_name(lua_State *L) { static int dbvm_bind(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_stmt *vm = svm->vm; - int index = luaL_checkint(L, 2); + const int index = luaL_checkint(L, 2); int result; dbvm_check_bind_index(L, svm, index); @@ -541,19 +541,19 @@ static int dbvm_bind(lua_State *L) { } static int dbvm_bind_blob(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = luaL_checkint(L, 2); + const sdb_vm *svm = lsqlite_checkvm(L, 1); + const int index = luaL_checkint(L, 2); const char *value = luaL_checkstring(L, 3); - int len = lua_strlen(L, 3); + const int len = static_cast(lua_strlen(L, 3)); lua_pushinteger(L, sqlite3_bind_blob(svm->vm, index, value, len, SQLITE_TRANSIENT)); return 1; } static int dbvm_bind_values(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); + const sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_stmt *vm = svm->vm; - int top = lua_gettop(L); + const int top = lua_gettop(L); int result, n; if (top - 1 != sqlite3_bind_parameter_count(vm)) @@ -575,9 +575,9 @@ static int dbvm_bind_values(lua_State *L) { } static int dbvm_bind_names(lua_State *L) { - sdb_vm *svm = lsqlite_checkvm(L, 1); + const sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_stmt *vm = svm->vm; - int count = sqlite3_bind_parameter_count(vm); + const int count = sqlite3_bind_parameter_count(vm); const char *name; int result, n; luaL_checktype(L, 2, LUA_TTABLE); @@ -621,17 +621,17 @@ static int dbvm_bind_names(lua_State *L) { ** Creates a new 'table' and leaves it in the stack */ static sdb *newdb (lua_State *L) { - sdb *db = (sdb*)lua_newuserdata(L, sizeof(sdb)); + auto *db = static_cast(lua_newuserdata(L, sizeof(sdb))); db->L = L; - db->db = NULL; /* database handle is currently `closed' */ - db->func = NULL; + db->db = nullptr; /* database handle is currently `closed' */ + db->func = nullptr; db->busy_cb = db->busy_udata = db->progress_cb = db->progress_udata = db->trace_cb = - db->trace_udata = + db->trace_udata = #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK db->update_hook_cb = db->update_hook_udata = @@ -667,7 +667,7 @@ static int cleanupdb(lua_State *L, sdb *db) { top = lua_gettop(L); lua_pushnil(L); while (lua_next(L, -2)) { - sdb_vm *svm = static_cast(lua_touserdata(L, -2)); /* key: vm; val: sql text */ + auto *svm = static_cast(lua_touserdata(L, -2)); /* key: vm; val: sql text */ cleanupvm(L, svm); lua_settop(L, top); @@ -699,7 +699,7 @@ static int cleanupdb(lua_State *L, sdb *db) { /* close database */ result = sqlite3_close(db->db); - db->db = NULL; + db->db = nullptr; /* free associated memory with created functions */ func = db->func; @@ -711,19 +711,19 @@ static int cleanupdb(lua_State *L, sdb *db) { free(func); func = func_next; } - db->func = NULL; + db->func = nullptr; return result; } static sdb *lsqlite_getdb(lua_State *L, int index) { - sdb *db = (sdb*)luaL_checkudata(L, index, sqlite_meta); - if (db == NULL) luaL_typerror(L, index, "sqlite database"); + auto *db = static_cast(luaL_checkudata(L, index, sqlite_meta)); + if (db == nullptr) luaL_typerror(L, index, "sqlite database"); return db; } static sdb *lsqlite_checkdb(lua_State *L, int index) { - sdb *db = lsqlite_getdb(L, index); - if (db->db == NULL) luaL_argerror(L, index, "attempt to use closed sqlite database"); + auto *db = lsqlite_getdb(L, index); + if (db->db == nullptr) luaL_argerror(L, index, "attempt to use closed sqlite database"); return db; } @@ -739,47 +739,47 @@ typedef struct { } lcontext; static lcontext *lsqlite_make_context(lua_State *L) { - lcontext *ctx = (lcontext*)lua_newuserdata(L, sizeof(lcontext)); + auto *ctx = static_cast(lua_newuserdata(L, sizeof(lcontext))); lua_rawgeti(L, LUA_REGISTRYINDEX, sqlite_ctx_meta_ref); lua_setmetatable(L, -2); - ctx->ctx = NULL; + ctx->ctx = nullptr; ctx->ud = LUA_NOREF; return ctx; } static lcontext *lsqlite_getcontext(lua_State *L, int index) { - lcontext *ctx = (lcontext*)luaL_checkudata(L, index, sqlite_ctx_meta); - if (ctx == NULL) luaL_typerror(L, index, "sqlite context"); + auto *ctx = static_cast(luaL_checkudata(L, index, sqlite_ctx_meta)); + if (ctx == nullptr) luaL_typerror(L, index, "sqlite context"); return ctx; } static lcontext *lsqlite_checkcontext(lua_State *L, int index) { lcontext *ctx = lsqlite_getcontext(L, index); - if (ctx->ctx == NULL) luaL_argerror(L, index, "invalid sqlite context"); + if (ctx->ctx == nullptr) luaL_argerror(L, index, "invalid sqlite context"); return ctx; } static int lcontext_tostring(lua_State *L) { char buff[39]; - lcontext *ctx = lsqlite_getcontext(L, 1); - if (ctx->ctx == NULL) + const lcontext *ctx = lsqlite_getcontext(L, 1); + if (ctx->ctx == nullptr) strcpy(buff, "closed"); else - sprintf(buff, "%p", ctx->ctx); + sprintf(buff, "%p", reinterpret_cast(ctx->ctx)); lua_pushfstring(L, "sqlite function context (%s)", buff); return 1; } static void lcontext_check_aggregate(lua_State *L, lcontext *ctx) { - sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx); + const auto *func = static_cast(sqlite3_user_data(ctx->ctx)); if (!func->aggregate) { luaL_error(L, "attempt to call aggregate method from scalar function"); } } static int lcontext_user_data(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx); + const lcontext *ctx = lsqlite_checkcontext(L, 1); + const auto *func = static_cast(sqlite3_user_data(ctx->ctx)); lua_rawgeti(L, LUA_REGISTRYINDEX, func->udata); return 1; } @@ -824,7 +824,7 @@ static int lcontext_result(lua_State *L) { sqlite3_result_double(ctx->ctx, luaL_checknumber(L, 2)); break; case LUA_TSTRING: - sqlite3_result_text(ctx->ctx, luaL_checkstring(L, 2), lua_strlen(L, 2), SQLITE_TRANSIENT); + sqlite3_result_text(ctx->ctx, luaL_checkstring(L, 2), static_cast(lua_strlen(L, 2)), SQLITE_TRANSIENT); break; case LUA_TNIL: case LUA_TNONE: @@ -839,45 +839,45 @@ static int lcontext_result(lua_State *L) { } static int lcontext_result_blob(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); + const lcontext *ctx = lsqlite_checkcontext(L, 1); const char *blob = luaL_checkstring(L, 2); - int size = lua_strlen(L, 2); - sqlite3_result_blob(ctx->ctx, (const void*)blob, size, SQLITE_TRANSIENT); + const int size = static_cast(lua_strlen(L, 2)); + sqlite3_result_blob(ctx->ctx, blob, size, SQLITE_TRANSIENT); return 0; } static int lcontext_result_double(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - double d = luaL_checknumber(L, 2); + const lcontext *ctx = lsqlite_checkcontext(L, 1); + const double d = luaL_checknumber(L, 2); sqlite3_result_double(ctx->ctx, d); return 0; } static int lcontext_result_error(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); + const lcontext *ctx = lsqlite_checkcontext(L, 1); const char *err = luaL_checkstring(L, 2); - int size = lua_strlen(L, 2); + const int size = static_cast(lua_strlen(L, 2)); sqlite3_result_error(ctx->ctx, err, size); return 0; } static int lcontext_result_int(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); - int i = luaL_checkint(L, 2); + const lcontext *ctx = lsqlite_checkcontext(L, 1); + const int i = luaL_checkint(L, 2); sqlite3_result_int(ctx->ctx, i); return 0; } static int lcontext_result_null(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); + const lcontext *ctx = lsqlite_checkcontext(L, 1); sqlite3_result_null(ctx->ctx); return 0; } static int lcontext_result_text(lua_State *L) { - lcontext *ctx = lsqlite_checkcontext(L, 1); + const lcontext *ctx = lsqlite_checkcontext(L, 1); const char *text = luaL_checkstring(L, 2); - int size = lua_strlen(L, 2); + const int size = static_cast(lua_strlen(L, 2)); sqlite3_result_text(ctx->ctx, text, size, SQLITE_TRANSIENT); return 0; } @@ -889,51 +889,51 @@ static int lcontext_result_text(lua_State *L) { */ static int db_isopen(lua_State *L) { - sdb *db = lsqlite_getdb(L, 1); - lua_pushboolean(L, db->db != NULL ? 1 : 0); + const sdb *db = lsqlite_getdb(L, 1); + lua_pushboolean(L, db->db != nullptr ? 1 : 0); return 1; } static int db_last_insert_rowid(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); + const sdb *db = lsqlite_checkdb(L, 1); /* conversion warning: int64 -> luaNumber */ - sqlite_int64 rowid = sqlite3_last_insert_rowid(db->db); + const sqlite_int64 rowid = sqlite3_last_insert_rowid(db->db); PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid)); return 1; } static int db_changes(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); + const sdb *db = lsqlite_checkdb(L, 1); lua_pushinteger(L, sqlite3_changes(db->db)); return 1; } static int db_total_changes(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); + const sdb *db = lsqlite_checkdb(L, 1); lua_pushinteger(L, sqlite3_total_changes(db->db)); return 1; } static int db_errcode(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); + const sdb *db = lsqlite_checkdb(L, 1); lua_pushinteger(L, sqlite3_errcode(db->db)); return 1; } static int db_errmsg(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); + const sdb *db = lsqlite_checkdb(L, 1); lua_pushstring(L, sqlite3_errmsg(db->db)); return 1; } static int db_interrupt(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); + const sdb *db = lsqlite_checkdb(L, 1); sqlite3_interrupt(db->db); return 0; } static int db_db_filename(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); + const sdb *db = lsqlite_checkdb(L, 1); const char *db_name = luaL_checkstring(L, 2); // sqlite3_db_filename may return NULL, in that case Lua pushes nil... lua_pushstring(L, sqlite3_db_filename(db->db, db_name)); @@ -947,7 +947,7 @@ static int db_db_filename(lua_State *L) { static void db_push_value(lua_State *L, sqlite3_value *value) { switch (sqlite3_value_type(value)) { case SQLITE_TEXT: - lua_pushlstring(L, (const char*)sqlite3_value_text(value), sqlite3_value_bytes(value)); + lua_pushlstring(L, reinterpret_cast(sqlite3_value_text(value)), sqlite3_value_bytes(value)); break; case SQLITE_INTEGER: @@ -961,7 +961,7 @@ static void db_push_value(lua_State *L, sqlite3_value *value) { break; case SQLITE_BLOB: - lua_pushlstring(L, (const char*)sqlite3_value_blob(value), sqlite3_value_bytes(value)); + lua_pushlstring(L, static_cast(sqlite3_value_blob(value)), sqlite3_value_bytes(value)); break; case SQLITE_NULL: @@ -983,12 +983,12 @@ static void db_push_value(lua_State *L, sqlite3_value *value) { /* scalar function to be called ** callback params: context, values... */ static void db_sql_normal_function(sqlite3_context *context, int argc, sqlite3_value **argv) { - sdb_func *func = (sdb_func*)sqlite3_user_data(context); + const auto *func = static_cast(sqlite3_user_data(context)); lua_State *L = func->db->L; int n; lcontext *ctx; - int top = lua_gettop(L); + const int top = lua_gettop(L); /* ensure there is enough space in the stack */ lua_checkstack(L, argc + 3); @@ -1027,12 +1027,12 @@ static void db_sql_normal_function(sqlite3_context *context, int argc, sqlite3_v if (lua_pcall(L, argc + 1, 0, 0)) { const char *errmsg = lua_tostring(L, -1); - int size = lua_strlen(L, -1); + const int size = static_cast(lua_strlen(L, -1)); sqlite3_result_error(context, errmsg, size); } /* invalidate context */ - ctx->ctx = NULL; + ctx->ctx = nullptr; if (!func->aggregate) { luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud); @@ -1042,11 +1042,11 @@ static void db_sql_normal_function(sqlite3_context *context, int argc, sqlite3_v } static void db_sql_finalize_function(sqlite3_context *context) { - sdb_func *func = (sdb_func*)sqlite3_user_data(context); + const auto *func = static_cast(sqlite3_user_data(context)); lua_State *L = func->db->L; void *p = sqlite3_aggregate_context(context, 1); /* minimal mem usage */ lcontext *ctx; - int top = lua_gettop(L); + const int top = lua_gettop(L); lua_rawgeti(L, LUA_REGISTRYINDEX, func->fn_finalize); /* function to call */ @@ -1073,7 +1073,7 @@ static void db_sql_finalize_function(sqlite3_context *context) { } /* invalidate context */ - ctx->ctx = NULL; + ctx->ctx = nullptr; /* cleanup context */ luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud); @@ -1113,17 +1113,17 @@ static int db_register_function(lua_State *L, int aggregate) { if (aggregate) luaL_checktype(L, 5, LUA_TFUNCTION); /* maybe an alternative way to allocate memory should be used/avoided */ - func = (sdb_func*)malloc(sizeof(sdb_func)); - if (func == NULL) { + func = static_cast(malloc(sizeof(sdb_func))); + if (func == nullptr) { luaL_error(L, "out of memory"); } result = sqlite3_create_function( db->db, name, args, SQLITE_UTF8, func, - aggregate ? NULL : db_sql_normal_function, - aggregate ? db_sql_normal_function : NULL, - aggregate ? db_sql_finalize_function : NULL - ); + aggregate ? nullptr : db_sql_normal_function, + aggregate ? db_sql_normal_function : nullptr, + aggregate ? db_sql_finalize_function : nullptr + ); if (result == SQLITE_OK) { /* safety measures for userdata field to be present in the stack */ @@ -1179,9 +1179,9 @@ static int collwrapper(scc *co,int l1,const void *p1, int res=0; lua_State *L=co->L; lua_rawgeti(L,LUA_REGISTRYINDEX,co->ref); - lua_pushlstring(L, (const char*)p1, l1); - lua_pushlstring(L, (const char*)p2, l2); - if (lua_pcall(L,2,1,0)==0) res=(int)lua_tonumber(L,-1); + lua_pushlstring(L, static_cast(p1), l1); + lua_pushlstring(L, static_cast(p2), l2); + if (lua_pcall(L,2,1,0)==0) res=static_cast(lua_tonumber(L, -1)); lua_pop(L,1); return res; } @@ -1194,16 +1194,16 @@ static void collfree(scc *co) { } static int db_create_collation(lua_State *L) { - sdb *db=lsqlite_checkdb(L,1); + const sdb *db=lsqlite_checkdb(L,1); const char *collname=luaL_checkstring(L,2); - scc *co=NULL; - int (*collfunc)(scc *,int,const void *,int,const void *)=NULL; + scc *co= nullptr; + int (*collfunc)(scc *,int,const void *,int,const void *)= nullptr; lua_settop(L,3); /* default args to nil, and exclude extras */ if (lua_isfunction(L,3)) collfunc=collwrapper; else if (!lua_isnil(L,3)) luaL_error(L,"create_collation: function or nil expected"); - if (collfunc != NULL) { - co=(scc *)malloc(sizeof(scc)); /* userdata is a no-no as it + if (collfunc != nullptr) { + co=static_cast(malloc(sizeof(scc))); /* userdata is a no-no as it will be garbage-collected */ if (co) { co->L=L; @@ -1213,22 +1213,22 @@ static int db_create_collation(lua_State *L) { else luaL_error(L,"create_collation: could not allocate callback"); } sqlite3_create_collation_v2(db->db, collname, SQLITE_UTF8, - (void *)co, - (int(*)(void*,int,const void*,int,const void*))collfunc, - (void(*)(void*))collfree); + co, + reinterpret_cast(collfunc), + reinterpret_cast(collfree)); return 0; } /* Thanks to Wolfgang Oertl... */ static int db_load_extension(lua_State *L) { - sdb *db=lsqlite_checkdb(L,1); + const sdb *db=lsqlite_checkdb(L,1); const char *extname=luaL_optstring(L,2,NULL); const char *entrypoint=luaL_optstring(L,3,NULL); int result; - char *errmsg = NULL; + char *errmsg = nullptr; - if (extname == NULL) { + if (extname == nullptr) { result = sqlite3_enable_load_extension(db->db,0); /* disable extension loading */ } else { @@ -1255,9 +1255,9 @@ static int db_load_extension(lua_State *L) { ** Params: userdata, sql */ static void db_trace_callback(void *user, const char *sql) { - sdb *db = (sdb*)user; + const auto *db = static_cast(user); lua_State *L = db->L; - int top = lua_gettop(L); + const int top = lua_gettop(L); /* setup lua callback call */ lua_rawgeti(L, LUA_REGISTRYINDEX, db->trace_cb); /* get callback */ @@ -1282,7 +1282,7 @@ static int db_trace(lua_State *L) { db->trace_udata = LUA_NOREF; /* clear trace handler */ - sqlite3_trace(db->db, NULL, NULL); + sqlite3_trace(db->db, nullptr, nullptr); } else { luaL_checktype(L, 2, LUA_TFUNCTION); @@ -1310,14 +1310,13 @@ static int db_trace(lua_State *L) { ** Params: database, callback function, userdata ** ** callback function: -** Params: userdata, {one of SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE}, +** Params: userdata, {one of SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE}, ** database name, table name (containing the affected row), rowid of the row */ static void db_update_hook_callback(void *user, int op, char const *dbname, char const *tblname, sqlite3_int64 rowid) { - sdb *db = (sdb*)user; + const auto *db = static_cast(user); lua_State *L = db->L; - int top = lua_gettop(L); - lua_Number n; + const int top = lua_gettop(L); /* setup lua callback call */ lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_cb); /* get callback */ @@ -1325,7 +1324,7 @@ static void db_update_hook_callback(void *user, int op, char const *dbname, char lua_pushinteger(L, op); lua_pushstring(L, dbname); /* update_hook database name */ lua_pushstring(L, tblname); /* update_hook database name */ - + PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid)); /* call lua function */ @@ -1346,7 +1345,7 @@ static int db_update_hook(lua_State *L) { db->update_hook_udata = LUA_NOREF; /* clear update_hook handler */ - sqlite3_update_hook(db->db, NULL, NULL); + sqlite3_update_hook(db->db, nullptr, nullptr); } else { luaL_checktype(L, 2, LUA_TFUNCTION); @@ -1374,12 +1373,12 @@ static int db_update_hook(lua_State *L) { ** callback function: ** Params: userdata ** Returned value: Return false or nil to continue the COMMIT operation normally. -** return true (non false, non nil), then the COMMIT is converted into a ROLLBACK. +** return true (non false, non nil), then the COMMIT is converted into a ROLLBACK. */ static int db_commit_hook_callback(void *user) { - sdb *db = (sdb*)user; + const auto *db = static_cast(user); lua_State *L = db->L; - int top = lua_gettop(L); + const int top = lua_gettop(L); int rollback = 0; /* setup lua callback call */ @@ -1405,7 +1404,7 @@ static int db_commit_hook(lua_State *L) { db->commit_hook_udata = LUA_NOREF; /* clear commit_hook handler */ - sqlite3_commit_hook(db->db, NULL, NULL); + sqlite3_commit_hook(db->db, nullptr, nullptr); } else { luaL_checktype(L, 2, LUA_TFUNCTION); @@ -1434,9 +1433,9 @@ static int db_commit_hook(lua_State *L) { ** Params: userdata */ static void db_rollback_hook_callback(void *user) { - sdb *db = (sdb*)user; + const auto *db = static_cast(user); lua_State *L = db->L; - int top = lua_gettop(L); + const int top = lua_gettop(L); /* setup lua callback call */ lua_rawgeti(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); /* get callback */ @@ -1460,7 +1459,7 @@ static int db_rollback_hook(lua_State *L) { db->rollback_hook_udata = LUA_NOREF; /* clear rollback_hook handler */ - sqlite3_rollback_hook(db->db, NULL, NULL); + sqlite3_rollback_hook(db->db, nullptr, nullptr); } else { luaL_checktype(L, 2, LUA_TFUNCTION); @@ -1495,9 +1494,9 @@ static int db_rollback_hook(lua_State *L) { */ static int db_progress_callback(void *user) { int result = 1; /* abort by default */ - sdb *db = (sdb*)user; + const auto *db = static_cast(user); lua_State *L = db->L; - int top = lua_gettop(L); + const int top = lua_gettop(L); lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_cb); lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_udata); @@ -1521,10 +1520,10 @@ static int db_progress_handler(lua_State *L) { db->progress_udata = LUA_NOREF; /* clear busy handler */ - sqlite3_progress_handler(db->db, 0, NULL, NULL); + sqlite3_progress_handler(db->db, 0, nullptr, nullptr); } else { - int nop = luaL_checkint(L, 2); /* number of opcodes */ + const int nop = luaL_checkint(L, 2); /* number of opcodes */ luaL_checktype(L, 3, LUA_TFUNCTION); /* make sure we have an userdata field (even if nil) */ @@ -1581,22 +1580,21 @@ static int cleanupbu(lua_State *L, sdb_bu *sbu) { lua_rawset(L, LUA_REGISTRYINDEX); lua_pushinteger(L, sqlite3_backup_finish(sbu->bu)); - sbu->bu = NULL; + sbu->bu = nullptr; return 1; } static int lsqlite_backup_init(lua_State *L) { - - sdb *target_db = lsqlite_checkdb(L, 1); + const sdb *target_db = lsqlite_checkdb(L, 1); const char *target_nm = luaL_checkstring(L, 2); - sdb *source_db = lsqlite_checkdb(L, 3); + const sdb *source_db = lsqlite_checkdb(L, 3); const char *source_nm = luaL_checkstring(L, 4); sqlite3_backup *bu = sqlite3_backup_init(target_db->db, target_nm, source_db->db, source_nm); - if (NULL != bu) { - sdb_bu *sbu = (sdb_bu*)lua_newuserdata(L, sizeof(sdb_bu)); + if (nullptr != bu) { + auto *sbu = static_cast(lua_newuserdata(L, sizeof(sdb_bu))); luaL_getmetatable(L, sqlite_bu_meta); lua_setmetatable(L, -2); /* set metatable */ @@ -1616,26 +1614,25 @@ static int lsqlite_backup_init(lua_State *L) { return 1; } - else { - return 0; - } + + return 0; } static sdb_bu *lsqlite_getbu(lua_State *L, int index) { - sdb_bu *sbu = (sdb_bu*)luaL_checkudata(L, index, sqlite_bu_meta); - if (sbu == NULL) luaL_typerror(L, index, "sqlite database backup"); + auto *sbu = static_cast(luaL_checkudata(L, index, sqlite_bu_meta)); + if (sbu == nullptr) luaL_typerror(L, index, "sqlite database backup"); return sbu; } static sdb_bu *lsqlite_checkbu(lua_State *L, int index) { sdb_bu *sbu = lsqlite_getbu(L, index); - if (sbu->bu == NULL) luaL_argerror(L, index, "attempt to use closed sqlite database backup"); + if (sbu->bu == nullptr) luaL_argerror(L, index, "attempt to use closed sqlite database backup"); return sbu; } static int dbbu_gc(lua_State *L) { sdb_bu *sbu = lsqlite_getbu(L, 1); - if (sbu->bu != NULL) { + if (sbu->bu != nullptr) { cleanupbu(L, sbu); lua_pop(L, 1); } @@ -1644,20 +1641,20 @@ static int dbbu_gc(lua_State *L) { } static int dbbu_step(lua_State *L) { - sdb_bu *sbu = lsqlite_checkbu(L, 1); - int nPage = luaL_checkint(L, 2); + const sdb_bu *sbu = lsqlite_checkbu(L, 1); + const int nPage = luaL_checkint(L, 2); lua_pushinteger(L, sqlite3_backup_step(sbu->bu, nPage)); return 1; } static int dbbu_remaining(lua_State *L) { - sdb_bu *sbu = lsqlite_checkbu(L, 1); + const sdb_bu *sbu = lsqlite_checkbu(L, 1); lua_pushinteger(L, sqlite3_backup_remaining(sbu->bu)); return 1; } static int dbbu_pagecount(lua_State *L) { - sdb_bu *sbu = lsqlite_checkbu(L, 1); + const sdb_bu *sbu = lsqlite_checkbu(L, 1); lua_pushinteger(L, sqlite3_backup_pagecount(sbu->bu)); return 1; } @@ -1679,9 +1676,9 @@ static int dbbu_finish(lua_State *L) { */ static int db_busy_callback(void *user, int tries) { int retry = 0; /* abort by default */ - sdb *db = (sdb*)user; + const auto *db = static_cast(user); lua_State *L = db->L; - int top = lua_gettop(L); + const int top = lua_gettop(L); lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_cb); lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_udata); @@ -1706,7 +1703,7 @@ static int db_busy_handler(lua_State *L) { db->busy_udata = LUA_NOREF; /* clear busy handler */ - sqlite3_busy_handler(db->db, NULL, NULL); + sqlite3_busy_handler(db->db, nullptr, nullptr); } else { luaL_checktype(L, 2, LUA_TFUNCTION); @@ -1728,7 +1725,7 @@ static int db_busy_handler(lua_State *L) { static int db_busy_timeout(lua_State *L) { sdb *db = lsqlite_checkdb(L, 1); - int timeout = luaL_checkint(L, 2); + const int timeout = luaL_checkint(L, 2); sqlite3_busy_timeout(db->db, timeout); /* if there was a timeout callback registered, it is now @@ -1751,10 +1748,10 @@ static int db_busy_timeout(lua_State *L) { */ static int db_exec_callback(void* user, int columns, char **data, char **names) { int result = SQLITE_ABORT; /* abort by default */ - lua_State *L = (lua_State*)user; + auto *L = static_cast(user); int n; - int top = lua_gettop(L); + const int top = lua_gettop(L); lua_pushvalue(L, 3); /* function to call */ lua_pushvalue(L, 4); /* user data */ @@ -1789,7 +1786,7 @@ static int db_exec_callback(void* user, int columns, char **data, char **names) else #endif if (lua_isnumber(L, -1)) - result = lua_tonumber(L, -1); + result = static_cast(lua_tonumber(L, -1)); } lua_settop(L, top); @@ -1797,7 +1794,7 @@ static int db_exec_callback(void* user, int columns, char **data, char **names) } static int db_exec(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); + const sdb *db = lsqlite_checkdb(L, 1); const char *sql = luaL_checkstring(L, 2); int result; @@ -1813,11 +1810,11 @@ static int db_exec(lua_State *L) { lua_pushnil(L); /* column names not known at this point */ lua_newtable(L); /* column values table */ - result = sqlite3_exec(db->db, sql, db_exec_callback, L, NULL); + result = sqlite3_exec(db->db, sql, db_exec_callback, L, nullptr); } else { /* no callbacks */ - result = sqlite3_exec(db->db, sql, NULL, NULL, NULL); + result = sqlite3_exec(db->db, sql, nullptr, nullptr, nullptr); } lua_pushinteger(L, result); @@ -1831,7 +1828,7 @@ static int db_exec(lua_State *L) { static int db_prepare(lua_State *L) { sdb *db = lsqlite_checkdb(L, 1); const char *sql = luaL_checkstring(L, 2); - int sql_len = lua_strlen(L, 2); + const int sql_len = static_cast(lua_strlen(L, 2)); const char *sqltail; sdb_vm *svm; lua_settop(L,2); /* db,sql is on top of stack for call to newvm */ @@ -1881,18 +1878,17 @@ static int db_do_next_row(lua_State *L, int packed) { } return 1; } - else { - lua_checkstack(L, columns); - for (i = 0; i < columns; ++i) - vm_push_column(L, vm, i); - return svm->columns; - } + + lua_checkstack(L, columns); + for (i = 0; i < columns; ++i) + vm_push_column(L, vm, i); + return svm->columns; } if (svm->temp) { /* finalize and check for errors */ result = sqlite3_finalize(vm); - svm->vm = NULL; + svm->vm = nullptr; cleanupvm(L, svm); } else if (result == SQLITE_DONE) { @@ -1947,7 +1943,7 @@ static int db_do_rows(lua_State *L, int(*f)(lua_State *)) { svm = newvm(L, db); svm->temp = 1; - if (sqlite3_prepare_v2(db->db, sql, -1, &svm->vm, NULL) != SQLITE_OK) { + if (sqlite3_prepare_v2(db->db, sql, -1, &svm->vm, nullptr) != SQLITE_OK) { lua_pushstring(L, sqlite3_errmsg(svm->db->db)); if (cleanupvm(L, svm) == 1) lua_pop(L, 1); /* this should not happen since sqlite3_prepare_v2 will not set ->vm on error */ @@ -1974,8 +1970,8 @@ static int db_urows(lua_State *L) { static int db_tostring(lua_State *L) { char buff[32]; - sdb *db = lsqlite_getdb(L, 1); - if (db->db == NULL) + const sdb *db = lsqlite_getdb(L, 1); + if (db->db == nullptr) strcpy(buff, "closed"); else sprintf(buff, "%p", lua_touserdata(L, 1)); @@ -1992,7 +1988,7 @@ static int db_close(lua_State *L) { static int db_close_vm(lua_State *L) { sdb *db = lsqlite_checkdb(L, 1); /* cleanup temporary only tables? */ - int temp = lua_toboolean(L, 2); + const int temp = lua_toboolean(L, 2); /* free associated virtual machines */ lua_pushlightuserdata(L, db); @@ -2001,12 +1997,12 @@ static int db_close_vm(lua_State *L) { /* close all used handles */ lua_pushnil(L); while (lua_next(L, -2)) { - sdb_vm *svm = static_cast(lua_touserdata(L, -2)); /* key: vm; val: sql text */ + auto *svm = static_cast(lua_touserdata(L, -2)); /* key: vm; val: sql text */ if ((!temp || svm->temp) && svm->vm) { sqlite3_finalize(svm->vm); - svm->vm = NULL; + svm->vm = nullptr; } /* leave key in the stack */ @@ -2016,19 +2012,19 @@ static int db_close_vm(lua_State *L) { } /* From: Wolfgang Oertl -When using lsqlite3 in a multithreaded environment, each thread has a separate Lua +When using lsqlite3 in a multithreaded environment, each thread has a separate Lua environment, but full userdata structures can't be passed from one thread to another. This is possible with lightuserdata, however. See: lsqlite_open_ptr(). */ static int db_get_ptr(lua_State *L) { - sdb *db = lsqlite_checkdb(L, 1); + const sdb *db = lsqlite_checkdb(L, 1); lua_pushlightuserdata(L, db->db); return 1; } static int db_gc(lua_State *L) { sdb *db = lsqlite_getdb(L, 1); - if (db->db != NULL) /* ignore closed databases */ + if (db->db != nullptr) /* ignore closed databases */ cleanupdb(L, db); return 0; } @@ -2093,7 +2089,7 @@ static int lsqlite_do_open(lua_State *L, const char *filename, int flags) { static int lsqlite_open(lua_State *L) { const char *filename = luaL_checkstring(L, 1); - int flags = luaL_optinteger(L, 2, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + const int flags = static_cast(luaL_optinteger(L, 2, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)); return lsqlite_do_open(L, filename, flags); } @@ -2102,7 +2098,7 @@ static int lsqlite_open_memory(lua_State *L) { } /* From: Wolfgang Oertl -When using lsqlite3 in a multithreaded environment, each thread has a separate Lua +When using lsqlite3 in a multithreaded environment, each thread has a separate Lua environment, but full userdata structures can't be passed from one thread to another. This is possible with lightuserdata, however. See: db_get_ptr(). */ @@ -2116,7 +2112,7 @@ static int lsqlite_open_ptr(lua_State *L) { /* This is the only API function that runs sqlite3SafetyCheck regardless of * SQLITE_ENABLE_API_ARMOR and does almost nothing (without an SQL * statement) */ - rc = sqlite3_exec(db_ptr, NULL, NULL, NULL, NULL); + rc = sqlite3_exec(db_ptr, nullptr, nullptr, nullptr, nullptr); if (rc != SQLITE_OK) luaL_argerror(L, 1, "not a valid SQLite3 pointer"); @@ -2216,14 +2212,14 @@ static const struct { SC(OPEN_FULLMUTEX) SC(OPEN_SHAREDCACHE) SC(OPEN_PRIVATECACHE) - + /* terminator */ - { NULL, 0 } + {nullptr, 0 } }; /* ======================================================= */ -static const luaL_Reg dblib[] = { +static constexpr luaL_Reg dblib[] = { {"isopen", db_isopen }, {"last_insert_rowid", db_last_insert_rowid }, {"changes", db_changes }, @@ -2264,10 +2260,10 @@ static const luaL_Reg dblib[] = { {"__tostring", db_tostring }, {"__gc", db_gc }, - {NULL, NULL} + {nullptr, nullptr} }; -static const luaL_Reg vmlib[] = { +static constexpr luaL_Reg vmlib[] = { {"isopen", dbvm_isopen }, {"step", dbvm_step }, @@ -2312,10 +2308,10 @@ static const luaL_Reg vmlib[] = { {"__tostring", dbvm_tostring }, {"__gc", dbvm_gc }, - { NULL, NULL } + {nullptr, nullptr} }; -static const luaL_Reg ctxlib[] = { +static constexpr luaL_Reg ctxlib[] = { {"user_data", lcontext_user_data }, {"get_aggregate_data", lcontext_get_aggregate_context }, @@ -2332,10 +2328,10 @@ static const luaL_Reg ctxlib[] = { {"result_error", lcontext_result_error }, {"__tostring", lcontext_tostring }, - {NULL, NULL} + {nullptr, nullptr} }; -static const luaL_Reg dbbulib[] = { +static constexpr luaL_Reg dbbulib[] = { {"step", dbbu_step }, {"remaining", dbbu_remaining }, @@ -2344,10 +2340,10 @@ static const luaL_Reg dbbulib[] = { // {"__tostring", dbbu_tostring }, {"__gc", dbbu_gc }, - {NULL, NULL} + {nullptr, nullptr} }; -static const luaL_Reg sqlitelib[] = { +static constexpr luaL_Reg sqlitelib[] = { {"lversion", lsqlite_lversion }, {"version", lsqlite_version }, {"complete", lsqlite_complete }, @@ -2361,7 +2357,7 @@ static const luaL_Reg sqlitelib[] = { {"backup_init", lsqlite_backup_init }, {"__newindex", lsqlite_newindex }, - {NULL, NULL} + {nullptr, nullptr} }; static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) { @@ -2371,7 +2367,7 @@ static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) { lua_rawset(L, -3); /* metatable.__index = metatable */ /* register metatable functions */ - luaL_openlib(L, NULL, lib, 0); + luaL_openlib(L, nullptr, lib, 0); /* remove metatable from stack */ lua_pop(L, 1); diff --git a/src/overlay/Overlay.cpp b/src/overlay/Overlay.cpp index 3334edf0..ce2f9ef8 100644 --- a/src/overlay/Overlay.cpp +++ b/src/overlay/Overlay.cpp @@ -2,23 +2,19 @@ #include "Overlay.h" -#include "CET.h" -#include "widgets/HelperWidgets.h" - -#include +#include #include #include +#include void Overlay::PostInitialize() { if (!m_initialized) { - if (m_options.IsFirstLaunch) - { - m_showFirstTimeModal = true; + if (Bindings::IsFirstTimeSetup()) Toggle(); - } + m_initialized = true; } } @@ -49,129 +45,163 @@ bool Overlay::IsEnabled() const noexcept return m_initialized && m_enabled; } -VKBind Overlay::GetBind() const noexcept -{ - return m_VKBIOverlay.Bind; -} - void Overlay::Update() { if (!m_initialized) return; - + if (m_toggled) { + if (m_bindings.FirstTimeSetup()) + return; + + auto drawPopup = false; + WidgetResult disableResult; if (m_enabled) { - if (m_widgets[static_cast(m_activeWidgetID)]->OnDisable()) + if (m_toggled && !drawPopup && m_consoleEnabled) { - m_vm.OnOverlayClose(); - m_toggled = false; - m_enabled = false; + disableResult = m_console.OnDisable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::ENABLED) + drawPopup = true; + } + + if (m_toggled && !drawPopup && m_bindingsEnabled) + { + disableResult = m_bindings.OnDisable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::ENABLED) + drawPopup = true; + } + + if (m_toggled && !drawPopup && m_settingsEnabled) + { + disableResult = m_settings.OnDisable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::ENABLED) + drawPopup = true; + } + + if (m_toggled && !drawPopup && m_tweakDBEditorEnabled) + { + disableResult = m_tweakDBEditor.OnDisable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::ENABLED) + drawPopup = true; + } + + if (m_toggled && !drawPopup && m_gameLogEnabled) + { + disableResult = m_gameLog.OnDisable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::ENABLED) + drawPopup = true; + } + + if (m_toggled && !drawPopup && m_imguiDebugEnabled) + { + disableResult = m_imguiDebug.OnDisable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::ENABLED) + drawPopup = true; } } else { - if (m_widgets[static_cast(m_activeWidgetID)]->OnEnable()) + if (m_toggled && !drawPopup && m_consoleEnabled) { - m_vm.OnOverlayOpen(); - m_toggled = false; - m_enabled = true; + disableResult = m_console.OnEnable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::DISABLED) + drawPopup = true; + } + + if (m_toggled && !drawPopup && m_bindingsEnabled) + { + disableResult = m_bindings.OnEnable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::DISABLED) + drawPopup = true; + } + + if (m_toggled && !drawPopup && m_settingsEnabled) + { + disableResult = m_settings.OnEnable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::DISABLED) + drawPopup = true; + } + + if (m_toggled && !drawPopup && m_tweakDBEditorEnabled) + { + disableResult = m_tweakDBEditor.OnEnable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::DISABLED) + drawPopup = true; + } + + if (m_toggled && !drawPopup && m_gameLogEnabled) + { + disableResult = m_gameLog.OnEnable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::DISABLED) + drawPopup = true; + } + + if (m_toggled && !drawPopup && m_imguiDebugEnabled) + { + disableResult = m_imguiDebug.OnEnable(); + if (disableResult == WidgetResult::CANCEL) + m_toggled = false; + if (disableResult == WidgetResult::DISABLED) + drawPopup = true; } } - - if (!m_toggled) + + if (!drawPopup && m_toggled) { + if (m_enabled) + m_vm.OnOverlayClose(); + else + m_vm.OnOverlayOpen(); + m_enabled = !m_enabled; + auto& d3d12 = CET::Get().GetD3D12(); d3d12.DelayedSetTrapInputInImGui(m_enabled); ClipToCenter(RED4ext::CGameEngine::Get()->unkC0); + m_toggled = false; } } if (!m_enabled) return; - if (m_options.IsFirstLaunch) - { - if (m_showFirstTimeModal) - { - assert(!m_VKBIOverlay.CodeBind); - assert(!m_VKBIOverlay.SavedCodeBind); - assert(!m_VKBIOverlay.IsBinding); - - ImGui::OpenPopup("CET First Time Setup"); - m_showFirstTimeModal = false; - m_vm.BlockDraw(true); - } - - if (ImGui::BeginPopupModal("CET First Time Setup", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) - { - const auto shorterTextSz { ImGui::CalcTextSize("Combo can be composed from up to 4 keys.").x }; - const auto longerTextSz { ImGui::CalcTextSize("Please, bind some key combination for toggling overlay!").x }; - const auto diffTextSz { longerTextSz - shorterTextSz }; - - ImGui::TextUnformatted("Please, bind some key combination for toggling overlay!"); - ImGui::SetCursorPosX(diffTextSz / 2); - ImGui::TextUnformatted("Combo can be composed from up to 4 keys."); - ImGui::Separator(); - - // TODO - do not hardcode offset! this somewhat works temporarily... - HelperWidgets::BindWidget(m_VKBIOverlay, false, diffTextSz * 0.75f); - if (m_VKBIOverlay.CodeBind) - { - m_VKBIOverlay.Apply(); - m_bindings.Save(); - m_options.IsFirstLaunch = false; - m_vm.BlockDraw(false); - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } - return; - } - - auto& d3d12 = CET::Get().GetD3D12(); - const SIZE resolution = d3d12.GetResolution(); - - ImGui::SetNextWindowPos(ImVec2(resolution.cx * 0.2f, resolution.cy * 0.2f), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(resolution.cx * 0.6f, resolution.cy * 0.6f), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSizeConstraints(ImVec2(420, 315), ImVec2(FLT_MAX, FLT_MAX)); + const auto [width, height] = CET::Get().GetD3D12().GetResolution(); + const auto heightLimit = 2 * ImGui::GetFrameHeight() + 2 * ImGui::GetStyle().WindowPadding.y; + ImGui::SetNextWindowPos({width * 0.25f, height * 0.05f}, ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSizeConstraints({width * 0.5f, heightLimit}, {FLT_MAX, heightLimit}); if (ImGui::Begin("Cyber Engine Tweaks")) - { - const ImVec2 cZeroVec = {0, 0}; - - SetActiveWidget(HelperWidgets::ToolbarWidget()); - - if (m_activeWidgetID == WidgetID::CONSOLE) - { - if (ImGui::BeginChild("Console", cZeroVec, true)) - m_console.Update(); - ImGui::EndChild(); - } - if (m_activeWidgetID == WidgetID::BINDINGS) - { - if (ImGui::BeginChild("Bindings", cZeroVec, true)) - m_bindings.Update(); - ImGui::EndChild(); - } - if (m_activeWidgetID == WidgetID::TWEAKDB) - { - if (ImGui::BeginChild("TweakDB Editor", cZeroVec, true)) - m_tweakDBEditor.Update(); - ImGui::EndChild(); - } - if (m_activeWidgetID == WidgetID::SETTINGS) - { - if (ImGui::BeginChild("Settings", cZeroVec, true)) - m_settings.Update(); - ImGui::EndChild(); - } - } + DrawToolbar(); ImGui::End(); - if (m_options.DrawImGuiDiagnosticWindow) - ImGui::ShowMetricsWindow(&m_options.DrawImGuiDiagnosticWindow); + m_console.Draw(); + m_bindings.Draw(); + m_settings.Draw(); + m_tweakDBEditor.Draw(); + m_gameLog.Draw(); + m_imguiDebug.Draw(); } bool Overlay::IsInitialized() const noexcept @@ -179,20 +209,12 @@ bool Overlay::IsInitialized() const noexcept return m_initialized; } -LRESULT Overlay::OnWndProc(HWND, UINT auMsg, WPARAM awParam, LPARAM) -{ - // TODO - is this useful now? - return 0; -} - BOOL Overlay::ClipToCenter(RED4ext::CGameEngine::UnkC0* apThis) { - HWND wnd = (HWND)apThis->hWnd; - HWND foreground = GetForegroundWindow(); - - auto& overlay = CET::Get().GetOverlay(); + const auto wnd = static_cast(apThis->hWnd); + const HWND foreground = GetForegroundWindow(); - if (wnd == foreground && apThis->unk164 && !apThis->unk154 && !overlay.IsEnabled()) + if (wnd == foreground && apThis->unk164 && !apThis->unk154 && !CET::Get().GetOverlay().IsEnabled()) { RECT rect; GetClientRect(wnd, &rect); @@ -218,13 +240,11 @@ BOOL Overlay::ClipToCenter(RED4ext::CGameEngine::UnkC0* apThis) void Overlay::Hook() { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CWinapi_ClipToCenter); - - uint8_t* pLocation = func.GetAddr(); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CWinapi_ClipToCenter); - if (pLocation) + if (auto* pLocation = func.GetAddr()) { - if (MH_CreateHook(pLocation, &ClipToCenter, reinterpret_cast(&m_realClipToCenter)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) + if (MH_CreateHook(pLocation, reinterpret_cast(&ClipToCenter), reinterpret_cast(&m_realClipToCenter)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) Log::Error("Could not hook mouse clip function!"); else Log::Info("Hook mouse clip function!"); @@ -232,46 +252,83 @@ void Overlay::Hook() } Overlay::Overlay(D3D12& aD3D12, VKBindings& aBindings, Options& aOptions, LuaVM& aVm) - : m_console(aVm) - , m_bindings(aBindings, *this, aVm) + : m_console(aD3D12, aVm) + , m_bindings(aBindings, aVm) , m_settings(aOptions, aVm) , m_tweakDBEditor(aVm) + , m_gameLog(aD3D12) , m_d3d12(aD3D12) , m_options(aOptions) , m_vm(aVm) { - m_widgets[static_cast(WidgetID::CONSOLE)] = &m_console; - m_widgets[static_cast(WidgetID::BINDINGS)] = &m_bindings; - m_widgets[static_cast(WidgetID::SETTINGS)] = &m_settings; - m_widgets[static_cast(WidgetID::TWEAKDB)] = &m_tweakDBEditor; - Hook(); - m_options.IsFirstLaunch = !aBindings.Load(*this); - - m_connectInitialized = aD3D12.OnInitialized.Connect([this]() { PostInitialize(); }); - m_connectUpdate = aD3D12.OnUpdate.Connect([this]() { Update(); }); + m_connectInitialized = aD3D12.OnInitialized.Connect([this]{ PostInitialize(); }); } Overlay::~Overlay() { m_d3d12.OnInitialized.Disconnect(m_connectInitialized); - m_d3d12.OnUpdate.Disconnect(m_connectUpdate); } -void Overlay::SetActiveWidget(WidgetID aNewActive) +void Overlay::DrawToolbar() { - if (aNewActive < WidgetID::COUNT) - m_nextActiveWidgetID = aNewActive; + const auto itemWidth = GetAlignedItemWidth(7); - if (m_activeWidgetID != m_nextActiveWidgetID) - { - assert(m_activeWidgetID < WidgetID::COUNT); - if (m_widgets[static_cast(m_activeWidgetID)]->OnDisable()) - { - assert(m_nextActiveWidgetID < WidgetID::COUNT); - if (m_widgets[static_cast(m_nextActiveWidgetID)]->OnEnable()) - m_activeWidgetID = m_nextActiveWidgetID; - } - } + ImGui::PushStyleColor(ImGuiCol_Button, m_consoleEnabled ? ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive) : ImGui::GetStyleColorVec4(ImGuiCol_Button)); + if (ImGui::Button("Console", ImVec2(itemWidth, 0))) + m_console.Toggle(); + if (!m_toggled) + m_consoleEnabled = m_console.IsEnabled(); + ImGui::PopStyleColor(); + + ImGui::SameLine(); + + ImGui::PushStyleColor(ImGuiCol_Button, m_bindingsEnabled ? ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive) : ImGui::GetStyleColorVec4(ImGuiCol_Button)); + if (ImGui::Button("Bindings", ImVec2(itemWidth, 0))) + m_bindings.Toggle(); + if (!m_toggled) + m_bindingsEnabled = m_bindings.IsEnabled(); + ImGui::PopStyleColor(); + + ImGui::SameLine(); + + ImGui::PushStyleColor(ImGuiCol_Button, m_settingsEnabled ? ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive) : ImGui::GetStyleColorVec4(ImGuiCol_Button)); + if (ImGui::Button("Settings", ImVec2(itemWidth, 0))) + m_settings.Toggle(); + if (!m_toggled) + m_settingsEnabled = m_settings.IsEnabled(); + ImGui::PopStyleColor(); + + ImGui::SameLine(); + + ImGui::PushStyleColor(ImGuiCol_Button, m_tweakDBEditorEnabled ? ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive) : ImGui::GetStyleColorVec4(ImGuiCol_Button)); + if (ImGui::Button("TweakDB Editor", ImVec2(itemWidth, 0))) + m_tweakDBEditor.Toggle(); + if (!m_toggled) + m_tweakDBEditorEnabled = m_tweakDBEditor.IsEnabled(); + ImGui::PopStyleColor(); + + ImGui::SameLine(); + + ImGui::PushStyleColor(ImGuiCol_Button, m_gameLogEnabled ? ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive) : ImGui::GetStyleColorVec4(ImGuiCol_Button)); + if (ImGui::Button("Game Log", ImVec2(itemWidth, 0))) + m_gameLog.Toggle(); + if (!m_toggled) + m_gameLogEnabled = m_gameLog.IsEnabled(); + ImGui::PopStyleColor(); + + ImGui::SameLine(); + + ImGui::PushStyleColor(ImGuiCol_Button, m_imguiDebugEnabled ? ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive) : ImGui::GetStyleColorVec4(ImGuiCol_Button)); + if (ImGui::Button("ImGui Debug", ImVec2(itemWidth, 0))) + m_imguiDebug.Toggle(); + if (!m_toggled) + m_imguiDebugEnabled = m_imguiDebug.IsEnabled(); + ImGui::PopStyleColor(); + + ImGui::SameLine(); + + if (ImGui::Button("Reload all mods", ImVec2(itemWidth, 0))) + m_vm.ReloadAllMods(); } diff --git a/src/overlay/Overlay.h b/src/overlay/Overlay.h index a3e086d5..16585c56 100644 --- a/src/overlay/Overlay.h +++ b/src/overlay/Overlay.h @@ -1,10 +1,11 @@ #pragma once -#include "widgets/Widget.h" #include "widgets/Console.h" #include "widgets/Bindings.h" #include "widgets/Settings.h" #include "widgets/TweakDBEditor.h" +#include "widgets/GameLog.h" +#include "widgets/ImGuiDebug.h" using TClipToCenter = HWND(RED4ext::CGameEngine::UnkC0*); @@ -12,55 +13,50 @@ struct D3D12; struct Options; struct Overlay -{ +{ Overlay(D3D12& aD3D12, VKBindings& aBindings, Options& aOptions, LuaVM& aVm); ~Overlay(); void PostInitialize(); - + [[nodiscard]] bool IsInitialized() const noexcept; - Console& GetConsole(); - Bindings& GetBindings(); - Settings& GetSettings(); - + [[nodiscard]] Console& GetConsole(); + [[nodiscard]] Bindings& GetBindings(); + [[nodiscard]] Settings& GetSettings(); + void Toggle(); [[nodiscard]] bool IsEnabled() const noexcept; - [[nodiscard]] VKBind GetBind() const noexcept; void Update(); - LRESULT OnWndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM alParam); - protected: - void Hook(); - + static BOOL ClipToCenter(RED4ext::CGameEngine::UnkC0* apThis); private: - - void SetActiveWidget(WidgetID aNewActive); - - VKBindInfo m_VKBIOverlay{ { "cet.overlay_key", "Overlay Key", [this](){ Toggle(); } }, 0, 0, false }; + void DrawToolbar(); Console m_console; + bool m_consoleEnabled = false; Bindings m_bindings; + bool m_bindingsEnabled = false; Settings m_settings; + bool m_settingsEnabled = false; TweakDBEditor m_tweakDBEditor; - std::array m_widgets{ }; + bool m_tweakDBEditorEnabled = false; + GameLog m_gameLog; + bool m_gameLogEnabled = false; + ImGuiDebug m_imguiDebug; + bool m_imguiDebugEnabled = false; TClipToCenter* m_realClipToCenter{ nullptr }; - WidgetID m_activeWidgetID{ WidgetID::CONSOLE }; - WidgetID m_nextActiveWidgetID{ WidgetID::CONSOLE }; - std::atomic_bool m_enabled{ false }; std::atomic_bool m_toggled{ false }; bool m_initialized{ false }; - std::atomic_bool m_showFirstTimeModal{ false }; - D3D12& m_d3d12; Options& m_options; LuaVM& m_vm; diff --git a/src/overlay/widgets/Bindings.cpp b/src/overlay/widgets/Bindings.cpp index 1140e5f6..207b97a3 100644 --- a/src/overlay/widgets/Bindings.cpp +++ b/src/overlay/widgets/Bindings.cpp @@ -1,205 +1,514 @@ #include #include "Bindings.h" -#include "overlay/Overlay.h" -#include +#include +#include -Bindings::Bindings(VKBindings& aBindings, Overlay& aOverlay, LuaVM& aVm) - : m_bindings(aBindings) - , m_overlay(aOverlay) +namespace +{ + VKBind s_overlayToggleBind{ "overlay_key", "Overlay Key", "Use this hotkey to toggle overlay on and off.", [] { + if (!CET::Get().GetBindings().IsRecordingBind()) + CET::Get().GetOverlay().Toggle(); + }}; + VKBindInfo s_overlayToggleBindInfo{s_overlayToggleBind, 0, 0, false}; + VKModBind s_overlayToggleModBind{"cet", s_overlayToggleBind.ID}; +} + +bool VKBindInfo::operator==(const std::string& id) const +{ + return Bind == id; +} + +Bindings::Bindings(VKBindings& aBindings, LuaVM& aVm) + : Widget("Bindings") + , m_bindings(aBindings) , m_vm(aVm) - , m_overlayKeyID(m_overlay.GetBind().ID) { } -bool Bindings::OnEnable() +WidgetResult Bindings::OnEnable() { if (!m_enabled) { m_bindings.StopRecordingBind(); - Load(); - m_enabled = true; + Initialize(); } - return m_enabled; + + return Widget::OnEnable(); +} + +WidgetResult Bindings::OnPopup() +{ + const auto ret = UnsavedChangesPopup( + "Bindings", + m_openChangesModal, + m_madeChanges, + [this]{ Save(); }, + [this]{ ResetChanges(); }); + m_madeChanges = ret == TChangedCBResult::CHANGED; + m_popupResult = ret; + + return m_madeChanges ? WidgetResult::ENABLED : WidgetResult::DISABLED; } -bool Bindings::OnDisable() +WidgetResult Bindings::OnDisable() { if (m_enabled) { - m_bindings.StopRecordingBind(); - m_vm.BlockDraw(m_madeChanges); - m_madeChanges = (HelperWidgets::UnsavedChangesPopup(m_openChangesModal, m_madeChanges, m_saveCB, m_loadCB) == 0); - m_vm.BlockDraw(m_madeChanges); - m_enabled = m_madeChanges; + if (m_popupResult == TChangedCBResult::CANCEL) + { + m_popupResult = TChangedCBResult::APPLY; + return WidgetResult::CANCEL; + } + + if (m_madeChanges) + { + m_drawPopup = true; + return WidgetResult::ENABLED; + } + + m_enabled = false; } + if (!m_enabled) - { - // reset changes substates - m_hotkeysChanged = false; - m_inputsChanged = false; - } - return !m_enabled; + m_bindings.StopRecordingBind(); + + return m_enabled ? WidgetResult::ENABLED : WidgetResult::DISABLED; } -void Bindings::Update() +void Bindings::OnUpdate() { - if (ImGui::Button("Load")) - Load(); - ImGui::SameLine(); - if (ImGui::Button("Save")) + const auto frameSize = ImVec2(ImGui::GetContentRegionAvail().x, -(ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.y + ImGui::GetStyle().FramePadding.y + 2.0f)); + if (ImGui::BeginChild(ImGui::GetID("Bindings"), frameSize)) + { + m_madeChanges = false; + for (auto modBindingsIt = m_vkBindInfos.begin(); modBindingsIt != m_vkBindInfos.end(); ++modBindingsIt) + UpdateAndDrawModBindings(modBindingsIt.key(), modBindingsIt.value().first, modBindingsIt.value().second); + } + ImGui::EndChild(); + + ImGui::Separator(); + + const auto itemWidth = GetAlignedItemWidth(2); + if (ImGui::Button("Save", ImVec2(itemWidth, 0))) Save(); ImGui::SameLine(); - if (ImGui::Button("Reset changes")) + if (ImGui::Button("Reset changes", ImVec2(itemWidth, 0))) ResetChanges(); +} - ImGui::Spacing(); - - if (!m_luaVMReady && m_vm.IsInitialized()) - Load(); - - if (ImGui::BeginTabBar("##BINDINGS", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton | ImGuiTabBarFlags_NoTooltip)) +void Bindings::Save() +{ + for (auto modBindingsIt = m_vkBindInfos.begin(); modBindingsIt != m_vkBindInfos.end(); ++modBindingsIt) { - if (ImGui::BeginTabItem("Hotkeys")) + for (auto& binding : modBindingsIt.value().first) { - if (ImGui::BeginChild("##BINDINGS_HOTKEYS")) - m_hotkeysChanged = DrawBindings(true); - ImGui::EndChild(); - ImGui::EndTabItem(); + if (binding.SavedCodeBind != binding.CodeBind) + binding.SavedCodeBind = binding.CodeBind; } + } - if (ImGui::BeginTabItem("Inputs")) + m_bindings.Save(); +} + +void Bindings::ResetChanges() +{ + const bool overlayToggleChanged = s_overlayToggleBindInfo.SavedCodeBind != s_overlayToggleBindInfo.CodeBind; + + if (overlayToggleChanged && s_overlayToggleBindInfo.CodeBind != 0) + m_bindings.UnBind(s_overlayToggleBindInfo.CodeBind); + + for (auto modBindingsIt = m_vkBindInfos.begin(); modBindingsIt != m_vkBindInfos.end(); ++modBindingsIt) + { + for (const auto& binding : modBindingsIt.value().first) { - if (ImGui::BeginChild("##BINDINGS_INPUTS")) - m_inputsChanged = DrawBindings(false); - ImGui::EndChild(); - ImGui::EndTabItem(); + if (binding.CodeBind != 0 && binding.SavedCodeBind != binding.CodeBind) + m_bindings.UnBind(binding.CodeBind); } + } - m_madeChanges = m_hotkeysChanged || m_inputsChanged; + if (overlayToggleChanged && s_overlayToggleBindInfo.SavedCodeBind != 0) + { + m_bindings.Bind(s_overlayToggleBindInfo.SavedCodeBind, s_overlayToggleModBind); + s_overlayToggleBindInfo.CodeBind = s_overlayToggleBindInfo.SavedCodeBind; + } - ImGui::EndTabBar(); - } + for (auto modBindingsIt = m_vkBindInfos.begin(); modBindingsIt != m_vkBindInfos.end(); ++modBindingsIt) + { + for (auto& binding : modBindingsIt.value().first) + { + if (binding.SavedCodeBind != 0 && binding.SavedCodeBind != binding.CodeBind) + m_bindings.Bind(binding.SavedCodeBind, {modBindingsIt.key(), binding.Bind.ID}); + + binding.CodeBind = binding.SavedCodeBind; + } + } } -void Bindings::Load() +bool Bindings::IsFirstTimeSetup() { - if (!m_vm.IsInitialized()) + if (s_overlayToggleBindInfo.SavedCodeBind == 0) { - m_luaVMReady = false; - return; + s_overlayToggleBindInfo.CodeBind = CET::Get().GetBindings().GetBindCodeForModBind(s_overlayToggleModBind); + s_overlayToggleBindInfo.SavedCodeBind = s_overlayToggleBindInfo.CodeBind; } - - m_bindings.Load(m_overlay); - m_vkBindInfos = m_bindings.InitializeMods(m_vm.GetBinds()); - m_luaVMReady = true; + return s_overlayToggleBindInfo.SavedCodeBind == 0; } -void Bindings::Save() +bool Bindings::FirstTimeSetup() { - m_bindings.Save(); + if (!IsFirstTimeSetup()) + return false; + + if (m_vkBindInfos.empty()) + Initialize(); + + ImGui::OpenPopup("CET First Time Setup"); - if (!m_vm.IsInitialized()) + if (ImGui::BeginPopupModal("CET First Time Setup", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - m_luaVMReady = false; - return; + const auto shorterTextSz { ImGui::CalcTextSize("Combo can be composed from up to 4 keys.").x }; + const auto longerTextSz { ImGui::CalcTextSize("Please, bind some key combination for toggling overlay!").x }; + const auto diffTextSz { longerTextSz - shorterTextSz }; + + ImGui::TextUnformatted("Please, bind some key combination for toggling overlay!"); + ImGui::SetCursorPosX(diffTextSz / 2); + ImGui::TextUnformatted("Combo can be composed from up to 4 keys."); + ImGui::Separator(); + + auto& [cetBinds, cetHotkeys] = m_vkBindInfos.at(s_overlayToggleModBind.ModName); + UpdateAndDrawModBindings(s_overlayToggleModBind.ModName, cetBinds, cetHotkeys, true); + + auto& cetOverlayToggle = cetBinds[0]; + if (cetOverlayToggle.CodeBind != 0) + { + cetOverlayToggle.SavedCodeBind = cetOverlayToggle.CodeBind; + m_bindings.Save(); + + m_vm.BlockDraw(false); + + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + + return false; + } + + ImGui::EndPopup(); } - - m_vkBindInfos = m_bindings.InitializeMods(m_vm.GetBinds()); - m_luaVMReady = true; + + return true; } -void Bindings::ResetChanges() +const VKModBind& Bindings::GetOverlayToggleModBind() noexcept +{ + return s_overlayToggleModBind; +} + +const VKBind& Bindings::GetOverlayToggleBind() noexcept { - for (auto& vkBindInfo : m_vkBindInfos) + return s_overlayToggleBind; +} + +void Bindings::Initialize() +{ + const auto& allModsBinds = m_vm.GetAllBinds(); + + if (!m_vkBindInfos.empty()) { - if (vkBindInfo.CodeBind == vkBindInfo.SavedCodeBind) - continue; + assert(m_vkBindInfos.at(s_overlayToggleModBind.ModName).first.size() == 1); + const auto& overlayToggleBindInfo = m_vkBindInfos.at(s_overlayToggleModBind.ModName).first[0]; - if (vkBindInfo.CodeBind) - m_bindings.UnBind(vkBindInfo.CodeBind); - if (vkBindInfo.SavedCodeBind) - m_bindings.Bind(vkBindInfo.SavedCodeBind, vkBindInfo.Bind); + s_overlayToggleBindInfo.CodeBind = overlayToggleBindInfo.CodeBind; + s_overlayToggleBindInfo.SavedCodeBind = overlayToggleBindInfo.SavedCodeBind; + + if (s_overlayToggleBindInfo.SavedCodeBind == 0) + { + assert(s_overlayToggleBindInfo.CodeBind); + s_overlayToggleBindInfo.SavedCodeBind = s_overlayToggleBindInfo.CodeBind; + } - vkBindInfo.CodeBind = vkBindInfo.SavedCodeBind; + m_vkBindInfos.clear(); } -} -bool Bindings::DrawBindings(bool aDrawHotkeys) -{ - bool madeChanges = false; + // emplace CET internal settings + { + auto& [vkBindInfos, hotkeyCount] = m_vkBindInfos[s_overlayToggleModBind.ModName]; + vkBindInfos.emplace_back(s_overlayToggleBindInfo); + hotkeyCount = 1; + } - if (m_luaVMReady) + // emplace mod bindings + for (const auto& modBindsIt : allModsBinds) { - const char* cpEmptyMessage + auto& [vkBindInfos, hotkeyCount] = m_vkBindInfos[modBindsIt.first]; + hotkeyCount = 0; + for (const auto& vkBind : modBindsIt.second.get()) { - (aDrawHotkeys) - ? ("This mod has no hotkeys, but it should have some inputs in other tab...") - : ("This mod has no inputs, but it should have some hotkeys in other tab...") - }; + auto& vkBindInfo = vkBindInfos.emplace_back(vkBind); + vkBindInfo.SavedCodeBind = m_bindings.GetBindCodeForModBind({modBindsIt.first, vkBind.ID}); + vkBindInfo.CodeBind = vkBindInfo.SavedCodeBind; - std::string activeModName; - std::string_view prevMod { "" }; - size_t modBindsForType { 0 }; - for (auto& vkBindInfo : m_vkBindInfos) + if (vkBind.IsHotkey()) + ++hotkeyCount; + } + } + + // we have only one binding! if there are more for some reason, something didnt work as expected... + assert(m_vkBindInfos[s_overlayToggleModBind.ModName].first.size() == 1); +} + +void Bindings::UpdateAndDrawBinding(const VKModBind& acModBind, VKBindInfo& aVKBindInfo) +{ + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + auto codeBind = m_bindings.GetLastRecordingResult(); + if (aVKBindInfo.IsBinding && aVKBindInfo.Bind.IsInput() && (codeBind & 0xFFFF000000000000ull) != codeBind) + { + m_bindings.StopRecordingBind(); + aVKBindInfo.IsBinding = false; + } + + const auto isRecording = m_bindings.IsRecordingBind(); + if (aVKBindInfo.IsBinding && !isRecording) + { + const auto previousCodeBind = aVKBindInfo.CodeBind; + + if (codeBind != 0 && codeBind != previousCodeBind) { - std::string_view curMod { vkBindInfo.Bind.ID }; - curMod = curMod.substr(0, curMod.find('.')); - if (prevMod != curMod) + if (m_bindings.IsFirstKeyUsed(codeBind)) { - if (curMod == "cet") - { - if (!aDrawHotkeys) - continue; // skip in this instance - activeModName = "Cyber Engine Tweaks"; - } - else - activeModName = curMod; - - // transform to nicer format till modinfo is in - bool capitalize = true; - std::ranges::transform(std::as_const(activeModName), activeModName.begin(), [&capitalize](char c) { - if (!std::isalnum(c)) + // note - creating copy so we are not destroying reference to modBind when we unbind + const auto checkModBind = [this, &aVKBindInfo, codeBind](const VKModBind modBind) { + const auto cetBind = modBind == s_overlayToggleModBind; + if (!cetBind || aVKBindInfo.Bind.IsHotkey()) { - capitalize = true; - return ' '; - } - if (capitalize) - { - capitalize = false; - return static_cast(std::toupper(static_cast(c))); - } - return c; - }); + const auto& modName = modBind.ModName; + auto& [bindInfos, _] = m_vkBindInfos[modName]; + const auto bindIt = std::find(bindInfos.begin(), bindInfos.end(), modBind.ID); + if (bindIt != bindInfos.cend()) + { + const auto bindItCodeBindIsSame = bindIt->CodeBind == codeBind; + if (!cetBind || !bindItCodeBindIsSame) + { + const auto bindItCodeBind = bindIt->CodeBind; + if ((!cetBind && bindItCodeBindIsSame) || aVKBindInfo.Bind.IsInput() || bindIt->Bind.IsInput()) + { + m_bindings.UnBind(modBind); + bindIt->CodeBind = 0; + } - // add vertical spacing when this is not first iteration and check if we drawn anything - if (!prevMod.empty()) - { - if (!modBindsForType) - { - // we did not draw anything, write appropriate message so it is not empty - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10.0f); - ImGui::TextUnformatted(cpEmptyMessage); + if (aVKBindInfo.Bind.IsInput() && bindIt->Bind.IsHotkey() && m_bindings.IsFirstKeyUsed(codeBind)) + { + bindIt->CodeBind = bindItCodeBind; + m_bindings.Bind(bindIt->CodeBind, modBind); + } + else + aVKBindInfo.CodeBind = codeBind; + } + } } - ImGui::Spacing(); - } + }; - ImGui::TextUnformatted(activeModName.c_str()); - ImGui::Spacing(); + if (const auto* directModBind = m_bindings.GetModBindForBindCode(codeBind)) + checkModBind(*directModBind); + else if (const auto* indirectModBind = m_bindings.GetModBindStartingWithBindCode(codeBind)) + checkModBind(*indirectModBind); + } + else + aVKBindInfo.CodeBind = codeBind; + } - prevMod = curMod; + if (previousCodeBind != aVKBindInfo.CodeBind) + m_bindings.Bind(aVKBindInfo.CodeBind, acModBind); + + aVKBindInfo.IsBinding = false; + } + + bool bound = aVKBindInfo.CodeBind != 0; + const bool unbindable = bound && acModBind != s_overlayToggleModBind; + const bool modified = aVKBindInfo.CodeBind != aVKBindInfo.SavedCodeBind; + + ImVec4 curTextColor { ImGui::GetStyleColorVec4(ImGuiCol_Text) }; + if (!bound) + curTextColor = ImVec4(1.0f, modified ? 0.5f : 0.0f, 0.0f, 1.0f); + else if (modified) + curTextColor = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); + + ImGui::PushStyleColor(ImGuiCol_Text, curTextColor); + + const auto& bind = aVKBindInfo.Bind; + + ImGui::PushID(&aVKBindInfo.Bind.ID); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + GetCenteredOffsetForText(bind.DisplayName.c_str())); + ImGui::TextUnformatted(bind.DisplayName.c_str()); + ImGui::PopID(); + + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + { + if (bind.HasComplexDescription()) + { + if (m_vm.IsInitialized()) + { + ImGui::BeginTooltip(); + std::get>(bind.Description)(); + ImGui::EndTooltip(); } + else + ImGui::SetTooltip("Currently unable to draw this tooltip. Wait for a bit please..."); + } + if (bind.HasSimpleDescription()) + { + const auto& description = std::get(bind.Description); + if (!description.empty()) + ImGui::SetTooltip("%s", description.c_str()); + } + } + + ImGui::TableNextColumn(); - if (aDrawHotkeys == vkBindInfo.Bind.IsHotkey()) + const auto currentBindState = aVKBindInfo.IsBinding ? m_bindings.GetLastRecordingResult() : aVKBindInfo.CodeBind; + ImGui::PushID(&aVKBindInfo.CodeBind); + if (ImGui::Button( + aVKBindInfo.IsBinding && currentBindState == 0 ? "Binding..." : VKBindings::GetBindString(currentBindState).c_str(), + ImVec2(unbindable ? -(ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.x) : -FLT_MIN, 0))) + { + if (!aVKBindInfo.IsBinding && !isRecording) + { + m_bindings.StartRecordingBind(acModBind); + aVKBindInfo.IsBinding = true; + } + } + ImGui::PopID(); + + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + { + if (bind.HasComplexDescription()) + { + if (m_vm.IsInitialized()) { - madeChanges |= HelperWidgets::BindWidget(vkBindInfo, (vkBindInfo.Bind.ID != m_overlayKeyID), 10.0f); - ++modBindsForType; - } + ImGui::BeginTooltip(); + std::get>(bind.Description)(); + ImGui::EndTooltip(); + } + else + ImGui::SetTooltip("Currently unable to draw this tooltip. Wait for a bit please..."); + } + if (bind.HasSimpleDescription()) + { + const auto& description = std::get(bind.Description); + if (!description.empty()) + ImGui::SetTooltip("%s", description.c_str()); } } - else - ImGui::TextUnformatted("LuaVM is not yet initialized!"); - return madeChanges; -}; + if (unbindable) + { + ImGui::SameLine(); + + ImGui::PushID(&aVKBindInfo.SavedCodeBind); + if (ImGui::Checkbox("##IsBound", &bound)) + { + if (aVKBindInfo.CodeBind != 0) + { + m_bindings.StopRecordingBind(); + aVKBindInfo.IsBinding = false; + + m_bindings.UnBind(acModBind); + aVKBindInfo.CodeBind = 0; + } + } + ImGui::PopID(); + + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + ImGui::SetTooltip("Uncheck this checkbox to unbind this binding."); + } + + ImGui::PopStyleColor(); + + m_madeChanges |= aVKBindInfo.IsBinding || aVKBindInfo.CodeBind != aVKBindInfo.SavedCodeBind; +} + +void Bindings::UpdateAndDrawModBindings(const std::string& acModName, TiltedPhoques::Vector& aVKBindInfos, size_t aHotkeyCount, bool aSimplified) +{ + if (aVKBindInfos.empty()) + return; + + // transform mod name to nicer format until modinfo is in + std::string activeModName = acModName == s_overlayToggleModBind.ModName ? "Cyber Engine Tweaks" : acModName; + bool capitalize = true; + std::ranges::transform(std::as_const(activeModName), activeModName.begin(), [&capitalize](char c) { + if (!std::isalnum(c)) + { + capitalize = true; + return ' '; + } + if (capitalize) + { + capitalize = false; + return static_cast(std::toupper(c)); + } + return c; + }); + + bool pushed = false; + auto headerOpen = aSimplified; + if (!headerOpen) + { + headerOpen = ImGui::CollapsingHeader(activeModName.c_str(), ImGuiTreeNodeFlags_DefaultOpen); + if (headerOpen) + { + ImGui::TreePush(); + pushed = true; + } + } + + if (!headerOpen) + return; + + if (aHotkeyCount > 0) + { + if (!aSimplified) + { + ImGui::TextUnformatted("Hotkeys"); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + ImGui::SetTooltip("Hotkeys react after assigned key combination has been pressed and subsequently released. You can bind up to 4 key combination to them."); + } + + if (ImGui::BeginTable(("##HOTKEYS_" + activeModName).c_str(), 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Borders, ImVec2(-ImGui::GetStyle().IndentSpacing, 0))) + { + for (auto& binding : aVKBindInfos) + { + if (binding.Bind.IsHotkey()) + UpdateAndDrawBinding({acModName, binding.Bind.ID}, binding); + } + + ImGui::EndTable(); + } + } + + if (aHotkeyCount < aVKBindInfos.size()) + { + if (!aSimplified) + { + ImGui::TextUnformatted("Inputs"); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + ImGui::SetTooltip("Inputs react when key is pressed and released. You can bind single key to them."); + } + + if (ImGui::BeginTable(("##INPUTS_" + activeModName).c_str(), 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Borders, ImVec2(-ImGui::GetStyle().IndentSpacing, 0))) + { + for (auto& binding : aVKBindInfos) + { + if (binding.Bind.IsInput()) + UpdateAndDrawBinding({acModName, binding.Bind.ID}, binding); + } + + ImGui::EndTable(); + } + } + + if (pushed) + ImGui::TreePop(); +} diff --git a/src/overlay/widgets/Bindings.h b/src/overlay/widgets/Bindings.h index 7d796fa1..57f09dfc 100644 --- a/src/overlay/widgets/Bindings.h +++ b/src/overlay/widgets/Bindings.h @@ -1,42 +1,49 @@ #pragma once #include "Widget.h" -#include "HelperWidgets.h" -struct Overlay; -struct LuaVM; +struct VKBindInfo +{ + const VKBind& Bind; + uint64_t CodeBind{ 0 }; + uint64_t SavedCodeBind{ 0 }; + bool IsBinding{ false }; + + bool operator==(const std::string& id) const; +}; +struct LuaVM; struct Bindings : Widget { - Bindings(VKBindings& aBindings, Overlay& aOverlay, LuaVM& aVm); + Bindings(VKBindings& aBindings, LuaVM& aVm); ~Bindings() override = default; - bool OnEnable() override; - bool OnDisable() override; - void Update() override; - - void Load(); + WidgetResult OnEnable() override; + WidgetResult OnDisable() override; + void Save(); void ResetChanges(); + [[nodiscard]] static bool IsFirstTimeSetup(); + [[nodiscard]] bool FirstTimeSetup(); + + [[nodiscard]] static const VKModBind& GetOverlayToggleModBind() noexcept; + [[nodiscard]] static const VKBind& GetOverlayToggleBind() noexcept; + +protected: + void OnUpdate() override; + WidgetResult OnPopup() override; + private: - bool DrawBindings(bool aDrawHotkeys); + void Initialize(); + void UpdateAndDrawBinding(const VKModBind& acModBind, VKBindInfo& aVKBindInfo); + void UpdateAndDrawModBindings(const std::string& acModName, TiltedPhoques::Vector& aVKBindInfos, size_t aHotkeyCount, bool aSimplified = false); - TiltedPhoques::Vector m_vkBindInfos{ }; + TiltedPhoques::Map, size_t>> m_vkBindInfos{ }; VKBindings& m_bindings; - Overlay& m_overlay; LuaVM& m_vm; - std::string m_overlayKeyID; - - HelperWidgets::TUCHPSave m_saveCB { [this](){ Save(); } }; - HelperWidgets::TUCHPLoad m_loadCB { [this](){ ResetChanges(); } }; - - bool m_luaVMReady{ false }; - bool m_enabled{ false }; + TChangedCBResult m_popupResult{ TChangedCBResult::APPLY }; bool m_madeChanges{ false }; bool m_openChangesModal{ true }; - - bool m_hotkeysChanged{ false }; - bool m_inputsChanged{ false }; }; diff --git a/src/overlay/widgets/Console.cpp b/src/overlay/widgets/Console.cpp index 35ae91d9..ecd57b9e 100644 --- a/src/overlay/widgets/Console.cpp +++ b/src/overlay/widgets/Console.cpp @@ -1,188 +1,110 @@ #include #include "Console.h" -#include "Utils.h" #include -Console::Console(LuaVM& aVm) - : m_vm(aVm) +Console::Console(D3D12& aD3D12, LuaVM& aVm) + : Widget("Console") + , m_vm(aVm) + , m_logWindow(aD3D12, "scripting") { - const auto consoleSink = CreateCustomSinkST([this](const std::string& msg) { Log(msg); }); - consoleSink->set_pattern("%v"); - - spdlog::get("scripting")->sinks().push_back(consoleSink); + m_command.resize(255); } -bool Console::OnEnable() +WidgetResult Console::OnDisable() { - m_focusConsoleInput = true; - return true; -} + m_command.clear(); + m_command.resize(255); -bool Console::OnDisable() -{ - return true; + return Widget::OnDisable(); } int Console::HandleConsoleHistory(ImGuiInputTextCallbackData* apData) { auto* pConsole = static_cast(apData->UserData); - std::string* pStr = nullptr; + const std::string* pStr = nullptr; - if (pConsole->m_newConsoleHistory) + if (pConsole->m_newHistory) { - pStr = &pConsole->m_consoleHistory[pConsole->m_consoleHistoryIndex]; + pStr = &pConsole->m_history[pConsole->m_historyIndex]; } - else if (apData->EventKey == ImGuiKey_UpArrow && pConsole->m_consoleHistoryIndex > 0) + else if (apData->EventKey == ImGuiKey_UpArrow && pConsole->m_historyIndex > 0) { - pConsole->m_consoleHistoryIndex--; - - pStr = &pConsole->m_consoleHistory[pConsole->m_consoleHistoryIndex]; + pStr = &pConsole->m_history[--pConsole->m_historyIndex]; } - else if (apData->EventKey == ImGuiKey_DownArrow && pConsole->m_consoleHistoryIndex + 1 < pConsole->m_consoleHistory.size()) + else if (apData->EventKey == ImGuiKey_DownArrow && pConsole->m_historyIndex + 1 < pConsole->m_history.size()) { - pConsole->m_consoleHistoryIndex++; - - pStr = &pConsole->m_consoleHistory[pConsole->m_consoleHistoryIndex]; + pStr = &pConsole->m_history[++pConsole->m_historyIndex]; } - pConsole->m_newConsoleHistory = false; + pConsole->m_newHistory = false; if (pStr) { - std::memcpy(apData->Buf, pStr->c_str(), pStr->length() + 1); - apData->BufDirty = true; - apData->BufTextLen = pStr->length(); - apData->CursorPos = apData->BufTextLen; + apData->DeleteChars(0, apData->BufTextLen); + apData->InsertChars(0, pStr->c_str()); + apData->SelectAll(); } return 0; } -void Console::Update() +int Console::HandleConsoleResize(ImGuiInputTextCallbackData* apData) { - ImGui::Checkbox("Clear Input", &m_inputClear); - ImGui::SameLine(); - if (ImGui::Button("Clear Output")) - { - std::lock_guard _{ m_outputLock }; - m_outputLines.clear(); - } - ImGui::SameLine(); - ImGui::Checkbox("Scroll Output", &m_outputShouldScroll); - ImGui::SameLine(); - ImGui::Checkbox("Disable Game Log", &m_disabledGameLog); - ImGui::SameLine(); - if (ImGui::Button("Reload All Mods")) - m_vm.ReloadAllMods(); - - auto& style = ImGui::GetStyle(); - auto inputLineHeight = ImGui::GetTextLineHeight() + style.ItemInnerSpacing.y * 2; - - if (ImGui::ListBoxHeader("##ConsoleHeader", ImVec2(-1, -(inputLineHeight + style.ItemSpacing.y)))) - { - std::lock_guard _{ m_outputLock }; - - ImGuiListClipper clipper; - clipper.Begin(m_outputLines.size()); - while (clipper.Step()) - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) - { - auto& item = m_outputLines[i]; - ImGui::PushID(i); - if (ImGui::Selectable(item.c_str())) - { - auto str = item; - if (item[0] == '>' && item[1] == ' ') - str = str.substr(2); - - std::strncpy(m_Command, str.c_str(), sizeof(m_Command) - 1); - m_focusConsoleInput = true; - } - ImGui::PopID(); - } - - if (m_outputScroll) - { - if (m_outputShouldScroll) - ImGui::SetScrollHereY(); - m_outputScroll = false; - } - - ImGui::ListBoxFooter(); - } - - if (m_focusConsoleInput) + auto* pConsole = static_cast(apData->UserData); + + if (apData->BufTextLen + 1 >= apData->BufSize) { - ImGui::SetKeyboardFocusHere(); - m_focusConsoleInput = false; + pConsole->m_command.resize((apData->BufSize * 3) / 2); + apData->Buf = pConsole->m_command.data(); } - ImGui::SetNextItemWidth(-FLT_MIN); - const auto execute = ImGui::InputText("##InputCommand", m_Command, std::size(m_Command), - ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackHistory, &HandleConsoleHistory, this); - ImGui::SetItemDefaultFocus(); - if (execute) - { - auto consoleLogger = spdlog::get("scripting"); - consoleLogger->info("> {}", m_Command); - - m_consoleHistoryIndex = m_consoleHistory.size(); - m_consoleHistory.push_back(m_Command); - m_newConsoleHistory = true; - - if (!m_vm.ExecuteLua(m_Command)) - consoleLogger->info("Command failed to execute!"); - - if (m_inputClear) - { - std::memset(m_Command, 0, sizeof(m_Command)); - } + pConsole->m_commandLength = apData->BufTextLen; - m_focusConsoleInput = true; - } + return 0; } -void Console::Log(const std::string& acpText) +int Console::HandleConsole(ImGuiInputTextCallbackData* apData) { - std::lock_guard _{ m_outputLock }; + if (apData->EventFlag & ImGuiInputTextFlags_CallbackHistory) + return HandleConsoleHistory(apData); - size_t first = 0; - size_t size = acpText.size(); - while (first < size) - { - // find_first_of \r or \n - size_t second = std::string::npos; - for (size_t i = first; i != size; ++i) - { - char ch = acpText[i]; - if (ch == '\r' || ch == '\n') - { - second = i; - break; - } - } - - if (second == std::string_view::npos) - { - m_outputLines.emplace_back(acpText.substr(first)); - break; - } - - if (first != second) - m_outputLines.emplace_back(acpText.substr(first, second-first)); - - first = second + 1; - char ch = acpText[first]; - while (ch == '\r' || ch == '\n') - ch = acpText[++first]; - } + if (apData->EventFlag & ImGuiInputTextFlags_CallbackResize) + return HandleConsoleResize(apData); - m_outputScroll = true; + return 0; } -bool Console::GameLogEnabled() const +void Console::OnUpdate() { - return !m_disabledGameLog; + const auto& style = ImGui::GetStyle(); + const auto inputLineHeight = ImGui::GetTextLineHeight() + style.ItemInnerSpacing.y * 2; + m_logWindow.Draw({-FLT_MIN, -(inputLineHeight + style.ItemSpacing.y)}); + + ImGui::SetNextItemWidth(-FLT_MIN); + constexpr auto flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackResize; + const auto execute = ImGui::InputText("##InputCommand", m_command.data(), m_command.capacity(), flags, &HandleConsole, this); + ImGui::SetItemDefaultFocus(); + if (execute) + { + m_command.resize(m_commandLength); + m_command.shrink_to_fit(); + + const auto consoleLogger = spdlog::get("scripting"); + consoleLogger->info("> {}", m_command); + + if (!m_vm.ExecuteLua(m_command)) + consoleLogger->info("Command failed to execute!"); + + m_historyIndex = m_history.size(); + auto& history = m_history.emplace_back(); + history.swap(m_command); + m_newHistory = true; + + m_command.resize(255); + m_commandLength = 0; + + ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget + } } diff --git a/src/overlay/widgets/Console.h b/src/overlay/widgets/Console.h index d7cb04ba..08b1ba5b 100644 --- a/src/overlay/widgets/Console.h +++ b/src/overlay/widgets/Console.h @@ -1,36 +1,32 @@ #pragma once #include "Widget.h" +#include "LogWindow.h" +struct D3D12; struct LuaVM; - struct Console : Widget { - Console(LuaVM& aVm); + Console(D3D12& aD3D12, LuaVM& aVm); ~Console() override = default; - - bool OnEnable() override; - bool OnDisable() override; - void Update() override; - - void Log(const std::string& acpText); - bool GameLogEnabled() const; -private: + WidgetResult OnDisable() override; + +protected: + void OnUpdate() override; +private: static int HandleConsoleHistory(ImGuiInputTextCallbackData* apData); + static int HandleConsoleResize(ImGuiInputTextCallbackData* apData); + static int HandleConsole(ImGuiInputTextCallbackData* apData); - std::recursive_mutex m_outputLock{ }; - TiltedPhoques::Vector m_outputLines{ }; - TiltedPhoques::Vector m_consoleHistory{ }; - int64_t m_consoleHistoryIndex{ 0 }; - bool m_newConsoleHistory{ true }; - bool m_outputShouldScroll{ true }; - bool m_outputScroll{ false }; - bool m_inputClear{ true }; - bool m_disabledGameLog{ true }; - bool m_focusConsoleInput{ false }; LuaVM& m_vm; + LogWindow m_logWindow; + + TiltedPhoques::Vector m_history; + size_t m_historyIndex{ 0 }; + bool m_newHistory{ true }; - char m_Command[0x10000]{ 0 }; -}; \ No newline at end of file + std::string m_command; + int m_commandLength{ 0 }; +}; diff --git a/src/overlay/widgets/GameLog.cpp b/src/overlay/widgets/GameLog.cpp new file mode 100644 index 00000000..fa852620 --- /dev/null +++ b/src/overlay/widgets/GameLog.cpp @@ -0,0 +1,14 @@ +#include + +#include "GameLog.h" + +GameLog::GameLog(D3D12& aD3D12) + : Widget("Game Log") + , m_logWindow(aD3D12, "gamelog") +{ +} + +void GameLog::OnUpdate() +{ + m_logWindow.Draw({-FLT_MIN, -FLT_MIN}); +} diff --git a/src/overlay/widgets/GameLog.h b/src/overlay/widgets/GameLog.h new file mode 100644 index 00000000..88a3e5c2 --- /dev/null +++ b/src/overlay/widgets/GameLog.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Widget.h" +#include "LogWindow.h" + +struct D3D12; +struct GameLog : Widget +{ + GameLog(D3D12& aD3D12); + ~GameLog() override = default; + +protected: + void OnUpdate() override; + +private: + LogWindow m_logWindow; +}; diff --git a/src/overlay/widgets/HelperWidgets.cpp b/src/overlay/widgets/HelperWidgets.cpp deleted file mode 100644 index 44db4bdd..00000000 --- a/src/overlay/widgets/HelperWidgets.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include - -#include "HelperWidgets.h" - -#include "CET.h" -#include "overlay/Overlay.h" - -namespace HelperWidgets -{ - - WidgetID ToolbarWidget() - { - WidgetID activeID = WidgetID::COUNT; - ImGui::SameLine(); - if (ImGui::Button("Console")) - activeID = WidgetID::CONSOLE; - ImGui::SameLine(); - if (ImGui::Button("Bindings")) - activeID = WidgetID::BINDINGS; - ImGui::SameLine(); - if (ImGui::Button("Settings")) - activeID = WidgetID::SETTINGS; - ImGui::SameLine(); - if (ImGui::Button("TweakDB Editor")) - activeID = WidgetID::TWEAKDB; - ImGui::Spacing(); - return activeID; - } - - bool BindWidget(VKBindInfo& aVKBindInfo, bool aUnbindable, float aOffsetX) - { - VKBindings& vkb { CET::Get().GetBindings() }; - - if (aVKBindInfo.IsBinding && !vkb.IsRecordingBind()) - { - aVKBindInfo.CodeBind = vkb.GetLastRecordingResult(); - aVKBindInfo.IsBinding = false; - } - - ImVec4 curTextColor { ImGui::GetStyleColorVec4(ImGuiCol_Text) }; - if (aVKBindInfo.CodeBind == 0) - curTextColor = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); - if (aVKBindInfo.CodeBind != aVKBindInfo.SavedCodeBind) - curTextColor = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); - - std::string label { aVKBindInfo.Bind.Description + ':' }; - - ImGui::AlignTextToFramePadding(); - - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + aOffsetX); - - ImGui::PushStyleColor(ImGuiCol_Text, curTextColor); - ImGui::PushID(&aVKBindInfo.Bind.Description); // ensure we have unique ID by using pointer to Description, is OK, pointer will not be used inside ImGui :P - ImGui::TextUnformatted(label.c_str()); - ImGui::PopID(); - ImGui::PopStyleColor(); - - std::string vkStr { (aVKBindInfo.IsBinding) ? ("BINDING...") : (VKBindings::GetBindString(aVKBindInfo.CodeBind)) }; - - ImGui::SameLine(); - ImGui::PushID(&aVKBindInfo.Bind.ID[0]); // same as PushID before, just make it pointer to ID and make sure we point to first char (so we can make one more unique ID from this pointer) - if (ImGui::Button(vkStr.c_str())) - { - if (!aVKBindInfo.IsBinding) - { - vkb.StartRecordingBind(aVKBindInfo.Bind); - aVKBindInfo.IsBinding = true; - } - } - ImGui::PopID(); - - if (aUnbindable && aVKBindInfo.CodeBind) - { - ImGui::PushID(&aVKBindInfo.Bind.ID[1]); // same as PushID before, just make pointer a bit bigger :) - ImGui::SameLine(); - if (ImGui::Button("UNBIND")) - { - if (aVKBindInfo.IsBinding) - { - vkb.StopRecordingBind(); - aVKBindInfo.IsBinding = false; - } - vkb.UnBind(aVKBindInfo.CodeBind); - aVKBindInfo.CodeBind = 0; - } - ImGui::PopID(); - } - - return (aVKBindInfo.CodeBind != aVKBindInfo.SavedCodeBind); - } - - bool BoolWidget(const std::string& aLabel, bool& aCurrent, bool aSaved, float aOffsetX) - { - ImVec4 curTextColor = ImGui::GetStyleColorVec4(ImGuiCol_Text); - if (aCurrent != aSaved) - curTextColor = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); - - ImGui::AlignTextToFramePadding(); - - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + aOffsetX); - - ImGui::PushStyleColor(ImGuiCol_Text, curTextColor); - ImGui::TextUnformatted(aLabel.c_str()); - ImGui::PopStyleColor(); - - ImGui::SameLine(); - - ImGui::Checkbox(("##" + aLabel).c_str(), &aCurrent); - - return (aCurrent != aSaved); - } - - int32_t UnsavedChangesPopup(bool& aFirstTime, bool aMadeChanges, TUCHPSave aSaveCB, TUCHPLoad aLoadCB) - { - if (aMadeChanges) - { - int32_t res = 0; - if (aFirstTime) - { - ImGui::OpenPopup("Unsaved changes"); - aFirstTime = false; - } - - if (ImGui::BeginPopupModal("Unsaved changes", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) - { - const auto shorterTextSz { ImGui::CalcTextSize("You have some unsaved changes.").x }; - const auto longerTextSz { ImGui::CalcTextSize("Do you wish to apply them or discard them?").x }; - const auto diffTextSz { longerTextSz - shorterTextSz }; - - ImGui::SetCursorPosX(diffTextSz / 2); - ImGui::TextUnformatted("You have some unsaved changes."); - ImGui::TextUnformatted("Do you wish to apply them or discard them?"); - ImGui::Separator(); - - const auto buttonWidth { (longerTextSz - ImGui::GetStyle().ItemSpacing.x) / 2 }; - - if (ImGui::Button("Apply", ImVec2(buttonWidth, 0))) - { - aSaveCB(); - res = 1; - aFirstTime = true; - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if (ImGui::Button("Discard", ImVec2(buttonWidth, 0))) - { - aLoadCB(); - res = -1; - aFirstTime = true; - ImGui::CloseCurrentPopup(); - } - ImGui::SetItemDefaultFocus(); - - ImGui::EndPopup(); - } - return res; - } - return 1; // no changes, same as if we were to Apply - } -} diff --git a/src/overlay/widgets/HelperWidgets.h b/src/overlay/widgets/HelperWidgets.h deleted file mode 100644 index a64b6595..00000000 --- a/src/overlay/widgets/HelperWidgets.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Widget.h" - -namespace HelperWidgets -{ -WidgetID ToolbarWidget(); -bool BindWidget(VKBindInfo& aVKBindInfo, bool aUnbindable, float aOffsetX = 0.0f); -bool BoolWidget(const std::string& aLabel, bool& aCurrent, bool aSaved, float aOffsetX = 0.0f); - -using TUCHPSave = std::function; -using TUCHPLoad = std::function; -int32_t UnsavedChangesPopup(bool& aFirstTime, bool aMadeChanges, TUCHPSave aSaveCB, TUCHPLoad aLoadCB); -} \ No newline at end of file diff --git a/src/overlay/widgets/ImGuiDebug.cpp b/src/overlay/widgets/ImGuiDebug.cpp new file mode 100644 index 00000000..5e98154a --- /dev/null +++ b/src/overlay/widgets/ImGuiDebug.cpp @@ -0,0 +1,31 @@ +#include + +#include "ImGuiDebug.h" + +ImGuiDebug::ImGuiDebug() + : Widget("ImGui Debug", true) +{ +} + +void ImGuiDebug::OnUpdate() +{ + // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc. + ImGui::ShowMetricsWindow(); + + // create Debug Log window. display a simplified log of important dear imgui events. + ImGui::ShowDebugLogWindow(); + + // create Stack Tool window. hover items with mouse to query information about the source of their unique ID. + ImGui::ShowStackToolWindow(); + + // create About window. display Dear ImGui version, credits and build/system information. + ImGui::ShowAboutWindow(); + + // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) + ImGui::Begin("Dear ImGui Style Editor"); + ImGui::ShowStyleEditor(nullptr); + ImGui::End(); + + // add basic help/info block (not a window): how to manipulate ImGui as a end-user (mouse/keyboard controls). + //ImGui::ShowUserGuide(); +} diff --git a/src/overlay/widgets/ImGuiDebug.h b/src/overlay/widgets/ImGuiDebug.h new file mode 100644 index 00000000..b4de08fb --- /dev/null +++ b/src/overlay/widgets/ImGuiDebug.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Widget.h" + +struct D3D12; +struct ImGuiDebug : Widget +{ + ImGuiDebug(); + ~ImGuiDebug() override = default; + +protected: + void OnUpdate() override; +}; diff --git a/src/overlay/widgets/LogWindow.cpp b/src/overlay/widgets/LogWindow.cpp new file mode 100644 index 00000000..696a4fdd --- /dev/null +++ b/src/overlay/widgets/LogWindow.cpp @@ -0,0 +1,169 @@ +#include + +#include "LogWindow.h" + +#include +#include + +LogWindow::LogWindow(D3D12& aD3D12, const std::string& acpLoggerName) + : m_d3d12(aD3D12) + , m_loggerName(acpLoggerName) +{ + auto logSink = CreateCustomSinkMT([this](const std::string& msg){ Log(msg); }); + logSink->set_pattern("%L;%v"); + spdlog::get(m_loggerName)->sinks().emplace_back(std::move(logSink)); +} + +void LogWindow::Draw(const ImVec2& size) +{ + const auto itemWidth = GetAlignedItemWidth(2); + + if (ImGui::Button("Clear output", ImVec2(itemWidth, 0))) + { + m_normalizedWidth = -1.0f; + std::lock_guard _{ m_lock }; + m_nextIndexToCheck = 0; + m_lines.clear(); + } + ImGui::SameLine(); + ImGui::Checkbox("Auto-scroll", &m_shouldScroll); + + const auto& style = ImGui::GetStyle(); + + const auto frameId = ImGui::GetID(("##" + m_loggerName).c_str()); + if (ImGui::BeginChildFrame(frameId, size, ImGuiWindowFlags_HorizontalScrollbar)) + { + std::lock_guard _{ m_lock }; + + if (!m_lines.empty() && (m_normalizedWidth < 0.0f || m_nextIndexToCheck < m_lines.size())) + { + for (size_t i = m_nextIndexToCheck; i < m_lines.size(); ++i) + { + auto& line = m_lines[i].second; + m_normalizedWidth = std::max(m_normalizedWidth, ImGui::CalcTextSize(line.c_str()).x); + } + m_nextIndexToCheck = m_lines.size(); + } + const auto listItemWidth = std::max(m_normalizedWidth + style.ItemInnerSpacing.x * 2, ImGui::GetContentRegionAvail().x); + + ImGuiListClipper clipper; + clipper.Begin(static_cast(m_lines.size())); + while (clipper.Step()) + { + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) + { + auto [level, item] = m_lines[i]; + + switch (level) + { + case spdlog::level::level_enum::trace: + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{0.0f, 0.0f, 1.0f, 1.0f}); + break; + + case spdlog::level::level_enum::debug: + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{0.0f, 1.0f, 0.0f, 1.0f}); + break; + + case spdlog::level::level_enum::warn: + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 1.0f, 0.0f, 1.0f}); + break; + + case spdlog::level::level_enum::err: + case spdlog::level::level_enum::critical: + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.0f, 0.0f, 1.0f}); + break; + + case spdlog::level::level_enum::info: + default: + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + + ImGui::SetNextItemWidth(listItemWidth); + ImGui::InputText(("##" + item).c_str(), item.data(), item.size(), ImGuiInputTextFlags_ReadOnly); + ImGui::PopStyleColor(); + } + } + + if (m_scroll) + { + if (m_shouldScroll) + { + ImGui::SetScrollHereY(); + ImGui::SetScrollX(0.0f); + } + m_scroll = false; + } + } + ImGui::EndChildFrame(); +} + +void LogWindow::Log(const std::string& acpText) +{ + assert(!acpText.empty()); + assert(acpText.size() >= 2); + assert(acpText[1] == ';'); + + spdlog::level::level_enum level = spdlog::level::level_enum::off; + switch(acpText[0]) + { + case 'T': // trace + level = spdlog::level::level_enum::trace; + break; + case 'D': // debug + level = spdlog::level::level_enum::debug; + break; + case 'I': // info + level = spdlog::level::level_enum::info; + break; + case 'W': // warning + level = spdlog::level::level_enum::warn; + break; + case 'E': // error + level = spdlog::level::level_enum::err; + break; + case 'C': // critical + level = spdlog::level::level_enum::critical; + break; + } + assert(level != spdlog::level::level_enum::off); + + size_t first = 2; + const size_t size = acpText.size(); + while (first < size) + { + // find_first_of \r or \n + size_t second = std::string::npos; + for (size_t i = first; i != size; ++i) + { + const auto ch = acpText[i]; + if (ch == '\r' || ch == '\n') + { + second = i; + break; + } + } + + if (second == std::string_view::npos) + { + auto text = acpText.substr(first); + std::lock_guard _{ m_lock }; + m_lines.emplace_back(level, std::move(text)); + break; + } + + if (first != second) + { + auto text = acpText.substr(first, second-first); + std::lock_guard _{ m_lock }; + m_lines.emplace_back(level, std::move(text)); + } + + first = second + 1; + char ch = acpText[first]; + while (ch == '\r' || ch == '\n') + ch = acpText[++first]; + } + + std::lock_guard _{ m_lock }; + m_scroll = true; +} diff --git a/src/overlay/widgets/LogWindow.h b/src/overlay/widgets/LogWindow.h new file mode 100644 index 00000000..5f745533 --- /dev/null +++ b/src/overlay/widgets/LogWindow.h @@ -0,0 +1,23 @@ +#pragma once + +struct D3D12; +struct LogWindow +{ + LogWindow(D3D12& aD3D12, const std::string& acpLoggerName); + + void Draw(const ImVec2& size); + +private: + void Log(const std::string& acpText); + + D3D12& m_d3d12; + + std::string m_loggerName; + float m_normalizedWidth{ -1.0f }; + bool m_shouldScroll{ true }; + + std::recursive_mutex m_lock; + TiltedPhoques::Vector> m_lines; + size_t m_nextIndexToCheck{ 0 }; + bool m_scroll{ false }; +}; diff --git a/src/overlay/widgets/Settings.cpp b/src/overlay/widgets/Settings.cpp index 444022f4..1d284642 100644 --- a/src/overlay/widgets/Settings.cpp +++ b/src/overlay/widgets/Settings.cpp @@ -1,94 +1,118 @@ #include #include "Settings.h" -#include + +#include + +#include Settings::Settings(Options& aOptions, LuaVM& aVm) - : m_options(aOptions) + : Widget("Settings") + , m_options(aOptions) , m_vm(aVm) { } -bool Settings::OnEnable() +WidgetResult Settings::OnEnable() { if (!m_enabled) { Load(); m_enabled = true; } - return m_enabled; + return m_enabled ? WidgetResult::ENABLED : WidgetResult::DISABLED; +} + +WidgetResult Settings::OnPopup() +{ + const auto ret = UnsavedChangesPopup( + "Settings", + m_openChangesModal, + m_madeChanges, + [this]{ Save(); }, + [this]{ Load(); }); + m_madeChanges = ret == TChangedCBResult::CHANGED; + m_popupResult = ret; + + return m_madeChanges ? WidgetResult::ENABLED : WidgetResult::DISABLED; } -bool Settings::OnDisable() +WidgetResult Settings::OnDisable() { if (m_enabled) { - m_vm.BlockDraw(m_madeChanges); - m_madeChanges = (HelperWidgets::UnsavedChangesPopup(m_openChangesModal, m_madeChanges, m_saveCB, m_loadCB) == 0); - m_vm.BlockDraw(m_madeChanges); - m_enabled = m_madeChanges; - } - if (!m_enabled) - { - // reset changes substates - m_patchesChanged = false; - m_devChanged = false; + if (m_popupResult == TChangedCBResult::CANCEL) + { + m_popupResult = TChangedCBResult::APPLY; + return WidgetResult::CANCEL; + } + + if (m_madeChanges) + { + m_drawPopup = true; + return WidgetResult::ENABLED; + } + + m_enabled = false; } - return !m_enabled; + + return m_enabled ? WidgetResult::ENABLED : WidgetResult::DISABLED; } -void Settings::Update() +void Settings::OnUpdate() { - if (ImGui::Button("Load")) - Load(); - ImGui::SameLine(); - if (ImGui::Button("Save")) - Save(); - ImGui::SameLine(); - if (ImGui::Button("Defaults")) - ResetToDefaults(); - - ImGui::Spacing(); - - if (ImGui::BeginTabBar("##SETTINGS", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton | ImGuiTabBarFlags_NoTooltip)) + const auto frameSize = ImVec2(ImGui::GetContentRegionAvail().x, -(ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.y + ImGui::GetStyle().FramePadding.y + 2.0f)); + if (ImGui::BeginChild(ImGui::GetID("Settings"), frameSize)) { - if (ImGui::BeginTabItem("Patches")) + m_madeChanges = false; + if (ImGui::CollapsingHeader("Patches", ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginChild("##SETTINGS_PATCHES")) + ImGui::TreePush(); + if (ImGui::BeginTable("##SETTINGS_PATCHES", 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Borders, ImVec2(-ImGui::GetStyle().IndentSpacing, 0))) { - m_patchesChanged = HelperWidgets::BoolWidget("AMD SMT Patch:", m_patchAmdSmt, m_options.PatchAmdSmt); - m_patchesChanged |= HelperWidgets::BoolWidget("Remove Pedestrians:", m_patchRemovePedestrians, m_options.PatchRemovePedestrians); - m_patchesChanged |= HelperWidgets::BoolWidget("Disable Async Compute:", m_patchAsyncCompute, m_options.PatchAsyncCompute); - m_patchesChanged |= HelperWidgets::BoolWidget("Disable Antialiasing:", m_patchAntialiasing, m_options.PatchAntialiasing); - m_patchesChanged |= HelperWidgets::BoolWidget("Skip Start Menu:", m_patchSkipStartMenu, m_options.PatchSkipStartMenu); - m_patchesChanged |= HelperWidgets::BoolWidget("Suppress Intro Movies:", m_patchDisableIntroMovies, m_options.PatchDisableIntroMovies); - m_patchesChanged |= HelperWidgets::BoolWidget("Disable Vignette:", m_patchDisableVignette, m_options.PatchDisableVignette); - m_patchesChanged |= HelperWidgets::BoolWidget("Disable Boundary Teleport:", m_patchDisableBoundaryTeleport, m_options.PatchDisableBoundaryTeleport); - m_patchesChanged |= HelperWidgets::BoolWidget("Disable V-Sync (Windows 7 only):", m_patchDisableWin7Vsync, m_options.PatchDisableWin7Vsync); - m_patchesChanged |= HelperWidgets::BoolWidget("Fix Minimap Flicker:", m_patchMinimapFlicker, m_options.PatchMinimapFlicker); + UpdateAndDrawSetting("AMD SMT Patch", "For AMD CPUs that did not get a performance boost after CDPR's patch (requires restart to take effect).", m_patchAmdSmt, m_options.PatchAmdSmt); + UpdateAndDrawSetting("Remove Pedestrians", "Removes most of the pedestrians and traffic (requires restart to take effect).", m_patchRemovePedestrians, m_options.PatchRemovePedestrians); + UpdateAndDrawSetting("Disable Async Compute", "Disables async compute, this can give a boost on older GPUs like Nvidia 10xx series for example (requires restart to take effect).", m_patchAsyncCompute, m_options.PatchAsyncCompute); + UpdateAndDrawSetting("Disable Anti-aliasing", "Completely disables anti-aliasing (requires restart to take effect).", m_patchAntialiasing, m_options.PatchAntialiasing); + UpdateAndDrawSetting("Skip Start Menu", "Skips the 'Breaching...' menu asking you to press space bar to continue (requires restart to take effect).", m_patchSkipStartMenu, m_options.PatchSkipStartMenu); + UpdateAndDrawSetting("Suppress Intro Movies", "Disables logos played at the beginning (requires restart to take effect).", m_patchDisableIntroMovies, m_options.PatchDisableIntroMovies); + UpdateAndDrawSetting("Disable Vignette", "Disables vignetting along screen borders (requires restart to take effect).", m_patchDisableVignette, m_options.PatchDisableVignette); + UpdateAndDrawSetting("Disable Boundary Teleport", "Allows players to access out-of-bounds locations (requires restart to take effect).", m_patchDisableBoundaryTeleport, m_options.PatchDisableBoundaryTeleport); + UpdateAndDrawSetting("Disable V-Sync (Windows 7 only)", " (requires restart to take effect).", m_patchDisableWin7Vsync, m_options.PatchDisableWin7Vsync); + UpdateAndDrawSetting("Fix Minimap Flicker", "Disables VSync on Windows 7 to bypass the 60 FPS limit (requires restart to take effect).", m_patchMinimapFlicker, m_options.PatchMinimapFlicker); + + ImGui::EndTable(); } - ImGui::EndChild(); - ImGui::EndTabItem(); + ImGui::TreePop(); } - - if (ImGui::BeginTabItem("Dev")) + if (ImGui::CollapsingHeader("CET Development Settings", ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginChild("##SETTINGS_DEV")) + ImGui::TreePush(); + if (ImGui::BeginTable("##SETTINGS_DEV", 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Borders, ImVec2(-ImGui::GetStyle().IndentSpacing, 0))) { - HelperWidgets::BoolWidget("Draw ImGui Diagnostic Window:", m_options.DrawImGuiDiagnosticWindow, m_options.DrawImGuiDiagnosticWindow); - m_devChanged = HelperWidgets::BoolWidget("Remove Dead Bindings:", m_removeDeadBindings, m_options.RemoveDeadBindings); - m_devChanged |= HelperWidgets::BoolWidget("Enable ImGui Assertions:", m_enableImGuiAssertions, m_options.EnableImGuiAssertions); - m_devChanged |= HelperWidgets::BoolWidget("Enable Debug Menu:", m_patchEnableDebug, m_options.PatchEnableDebug); - m_devChanged |= HelperWidgets::BoolWidget("Dump Game Options:", m_dumpGameOptions, m_options.DumpGameOptions); + UpdateAndDrawSetting("Remove Dead Bindings", "Removes all bindings which are no longer valid (disabling this could be useful when debugging mod issues).", m_removeDeadBindings, m_options.RemoveDeadBindings); + UpdateAndDrawSetting("Enable ImGui Assertions Logging", "Enables all ImGui assertions, assertions will get logged into log file of whoever triggered the assertion (useful when debugging ImGui issues, should also be used to check mods before shipping!).", m_enableImGuiAssertionsLogging, m_options.EnableImGuiAssertionsLogging); + UpdateAndDrawSetting("Enable Debug Build", "Sets internal flags to disguise as debug build (requires restart to take effect).", m_patchEnableDebug, m_options.PatchEnableDebug); + UpdateAndDrawSetting("Dump Game Options", "Dumps all game options into main log file (requires restart to take effect).", m_dumpGameOptions, m_options.DumpGameOptions); + + ImGui::EndTable(); } - ImGui::EndChild(); - ImGui::EndTabItem(); + ImGui::TreePop(); } + } + ImGui::EndChild(); - m_madeChanges = m_patchesChanged || m_devChanged; + ImGui::Separator(); - ImGui::EndTabBar(); - } + const auto itemWidth = GetAlignedItemWidth(3); + if (ImGui::Button("Load", ImVec2(itemWidth, 0))) + Load(); + ImGui::SameLine(); + if (ImGui::Button("Save", ImVec2(itemWidth, 0))) + Save(); + ImGui::SameLine(); + if (ImGui::Button("Defaults", ImVec2(itemWidth, 0))) + ResetToDefaults(); } void Settings::Load() @@ -107,7 +131,7 @@ void Settings::Load() m_patchMinimapFlicker = m_options.PatchMinimapFlicker; m_removeDeadBindings = m_options.RemoveDeadBindings; - m_enableImGuiAssertions = m_options.EnableImGuiAssertions; + m_enableImGuiAssertionsLogging = m_options.EnableImGuiAssertionsLogging; m_patchEnableDebug = m_options.PatchEnableDebug; m_dumpGameOptions = m_options.DumpGameOptions; } @@ -126,7 +150,7 @@ void Settings::Save() const m_options.PatchMinimapFlicker = m_patchMinimapFlicker; m_options.RemoveDeadBindings = m_removeDeadBindings; - m_options.EnableImGuiAssertions = m_enableImGuiAssertions; + m_options.EnableImGuiAssertionsLogging = m_enableImGuiAssertionsLogging; m_options.PatchEnableDebug = m_patchEnableDebug; m_options.DumpGameOptions = m_dumpGameOptions; @@ -138,3 +162,36 @@ void Settings::ResetToDefaults() m_options.ResetToDefaults(); Load(); } + +void Settings::UpdateAndDrawSetting(const std::string& acLabel, const std::string& acTooltip, bool& aCurrent, const bool& acSaved) +{ + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImVec4 curTextColor = ImGui::GetStyleColorVec4(ImGuiCol_Text); + if (aCurrent != acSaved) + curTextColor = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); + + ImGui::AlignTextToFramePadding(); + + ImGui::PushStyleColor(ImGuiCol_Text, curTextColor); + + ImGui::PushID(&acLabel); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + GetCenteredOffsetForText(acLabel.c_str())); + ImGui::TextUnformatted(acLabel.c_str()); + ImGui::PopID(); + + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !acTooltip.empty()) + ImGui::SetTooltip("%s", acTooltip.c_str()); + + ImGui::TableNextColumn(); + + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (ImGui::GetContentRegionAvail().x - ImGui::GetFrameHeight()) / 2); + ImGui::Checkbox(("##" + acLabel).c_str(), &aCurrent); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + ImGui::SetTooltip("%s", acTooltip.c_str()); + + ImGui::PopStyleColor(); + + m_madeChanges |= aCurrent != acSaved; +} diff --git a/src/overlay/widgets/Settings.h b/src/overlay/widgets/Settings.h index 4d3ea5e4..2bb1fea5 100644 --- a/src/overlay/widgets/Settings.h +++ b/src/overlay/widgets/Settings.h @@ -1,27 +1,28 @@ #pragma once #include "Widget.h" -#include "HelperWidgets.h" -struct VKBindings; -struct Overlay; struct Options; struct LuaVM; - struct Settings : Widget { Settings(Options& aOptions, LuaVM& aVm); ~Settings() override = default; - bool OnEnable() override; - bool OnDisable() override; - void Update() override; - + WidgetResult OnEnable() override; + WidgetResult OnDisable() override; + void Load(); void Save() const; - void ResetToDefaults(); + void ResetToDefaults(); + +protected: + void OnUpdate() override; + WidgetResult OnPopup() override; private: + void UpdateAndDrawSetting(const std::string& acLabel, const std::string& acTooltip, bool& aCurrent, const bool& acSaved); + bool m_patchEnableDebug{ false }; bool m_patchRemovePedestrians{ false }; bool m_patchAsyncCompute{ false }; @@ -35,18 +36,12 @@ struct Settings : Widget bool m_patchMinimapFlicker{ false }; bool m_dumpGameOptions{ false }; bool m_removeDeadBindings{ true }; - bool m_enableImGuiAssertions{ true }; + bool m_enableImGuiAssertionsLogging{ false }; Options& m_options; LuaVM& m_vm; - HelperWidgets::TUCHPSave m_saveCB { [this](){ Save(); } }; - HelperWidgets::TUCHPLoad m_loadCB { [this](){ Load(); } }; - - bool m_enabled{ false }; + TChangedCBResult m_popupResult{ TChangedCBResult::APPLY }; bool m_madeChanges{ false }; bool m_openChangesModal{ true }; - - bool m_patchesChanged{ false }; - bool m_devChanged{ false }; }; diff --git a/src/overlay/widgets/TweakDBEditor.cpp b/src/overlay/widgets/TweakDBEditor.cpp index 70efd043..909c1c43 100644 --- a/src/overlay/widgets/TweakDBEditor.cpp +++ b/src/overlay/widgets/TweakDBEditor.cpp @@ -1,17 +1,19 @@ #include +#include "TweakDBEditor.h" + +#include +#include + +#include +#include + #include #include #include #include #include -#include "HelperWidgets.h" -#include -#include - -#include "TweakDBEditor.h" - bool TweakDBEditor::s_recordsFilterIsRegex = false; bool TweakDBEditor::s_flatsFilterIsRegex = false; char TweakDBEditor::s_recordsFilterBuffer[256]{}; @@ -20,317 +22,6 @@ char TweakDBEditor::s_tweakdbidFilterBuffer[256]{}; float g_comboDropdownHeight = 300.0f; constexpr float c_searchDelay = 0.25f; -bool SortString(const std::string& acLeft, const std::string& acRight); - -struct CDPRTweakDBMetadata -{ - static constexpr char c_defaultFilename[] = "tweakdb.str"; - - static CDPRTweakDBMetadata* Get() - { - static CDPRTweakDBMetadata instance; - return &instance; - } - - bool Initialize() - { - Reset(); - - auto filepath = CET::Get().GetPaths().CETRoot() / c_defaultFilename; - if (!std::filesystem::exists(filepath)) - return false; - - try - { - std::ifstream file(filepath, std::ios::binary); - file.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit); - file.read(reinterpret_cast(&m_header), sizeof(Header)); - assert(m_header.m_version == 1); - ReadTDBIDNameArray(file, m_header.m_recordsCount, m_records); - ReadTDBIDNameArray(file, m_header.m_flatsCount, m_flats); - ReadTDBIDNameArray(file, m_header.m_queriesCount, m_queries); - m_isInitialized = true; - } - // this is easier for now - catch (std::exception&) - { - return false; - } - - // check if we have a tweakdb that was changed by REDmod - if (HasREDModTweakDB()) - { - try - { - auto moddedTweakDbFilePath = CET::Get().GetPaths().R6CacheModdedRoot() / c_defaultFilename; - std::ifstream file(moddedTweakDbFilePath, std::ios::binary); - file.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit); - file.read(reinterpret_cast(&m_header), sizeof(Header)); - assert(m_header.m_version == 1); - ReadTDBIDNameArray(file, m_header.m_recordsCount, m_records); - ReadTDBIDNameArray(file, m_header.m_flatsCount, m_flats); - ReadTDBIDNameArray(file, m_header.m_queriesCount, m_queries); - Log::Info("CDPRTweakDBMetadata::Initalize() - REDMod TweakDB initialization successful!"); - } - // this is easier for now - // do not modify the return state since this isn't the main TweakDB - catch (std::exception&) - { - Log::Warn("CDPRTweakDBMetadata::Initalize() - Failed to load REDMod TweakDB. Modded entries may not " - "be shown in the TweakDB Editor."); - } - } - - return true; - } - - bool IsInitialized() - { - return m_isInitialized; - } - - bool GetRecordName(RED4ext::TweakDBID aDBID, std::string& aName) - { - const auto it = m_records.find(aDBID.value & 0xFFFFFFFFFF); - if (it == m_records.end()) - return false; - - aName = it->second; - return true; - } - - bool GetFlatName(RED4ext::TweakDBID aDBID, std::string& aName) - { - const auto it = m_flats.find(aDBID.value & 0xFFFFFFFFFF); - if (it == m_flats.end()) - return false; - - aName = it->second; - return true; - } - - bool GetQueryName(RED4ext::TweakDBID aDBID, std::string& aName) - { - const auto it = m_queries.find(aDBID.value & 0xFFFFFFFFFF); - if (it == m_queries.end()) - return false; - - aName = it->second; - return true; - } - - bool HasREDModTweakDB() - { - auto filepath = CET::Get().GetPaths().R6CacheModdedRoot() / c_defaultFilename; - return std::filesystem::exists(filepath); - } - -protected: - int32_t ReadCompressedInt(std::ifstream& aFile) - { - int32_t uncompressed = 0; - - int8_t byte; - aFile.read(reinterpret_cast(&byte), 1); - uncompressed |= byte & 0x3F; - if (byte & 0x40) - { - aFile.read(reinterpret_cast(&byte), 1); - uncompressed |= (byte & 0x7F) << 6; - if (byte & 0x80) - { - aFile.read(reinterpret_cast(&byte), 1); - uncompressed |= (byte & 0x7F) << 13; - if (byte & 0x80) - { - aFile.read(reinterpret_cast(&byte), 1); - uncompressed |= (byte & 0x7F) << 20; - if (byte & 0x80) - { - aFile.read(reinterpret_cast(&byte), 1); - uncompressed |= byte << 27; - } - } - } - } - - return uncompressed; - } - - void ReadTDBIDNameArray(std::ifstream& aFile, uint32_t aCount, TiltedPhoques::Map& aOutMap) - { - for (int32_t i = 0; i != aCount; ++i) - { - int32_t length = ReadCompressedInt(aFile); - std::string str; - str.resize(length); - aFile.read(str.data(), length); - aOutMap.try_emplace(TweakDBID(str).value, std::move(str)); - } - } - - void Reset() - { - m_isInitialized = false; - m_records.clear(); - m_flats.clear(); - m_queries.clear(); - } - -private: - struct Header - { - uint32_t m_magic; // a hash of all types currently supported - uint32_t m_version; // 1 - uint32_t m_recordsCount; - uint32_t m_flatsCount; - uint32_t m_queriesCount; - }; - - bool m_isInitialized = false; - Header m_header; - TiltedPhoques::Map m_records; - TiltedPhoques::Map m_flats; - TiltedPhoques::Map m_queries; -}; - -using TOodleLZ_Decompress = size_t(*)(char *in, int insz, char *out, int outsz, int wantsFuzzSafety, int b, int c, void *d, void *e, void *f, void *g, void *workBuffer, size_t workBufferSize, int j); - -struct ResourcesList -{ - static constexpr char c_defaultFilename[] = "usedhashes.kark"; - - struct Resource - { - bool m_isFiltered; - std::string m_name; - uint64_t m_hash; - - Resource(std::string aName) noexcept - : m_isFiltered(false) - , m_name(std::move(aName)) - , m_hash(RED4ext::FNV1a(m_name.c_str())) - { - } - - Resource(Resource&&) noexcept = default; - Resource& operator=(Resource&&) noexcept = default; - }; - - static ResourcesList* Get() - { - static ResourcesList instance; - return &instance; - } - - bool Initialize() - { - Reset(); - - auto hOodleHandle = GetModuleHandle(TEXT("oo2ext_7_win64.dll")); - if (hOodleHandle == nullptr) - { - spdlog::error("Could not get Oodle access"); - return false; - } - - auto OodleLZ_Decompress = reinterpret_cast(GetProcAddress(hOodleHandle, "OodleLZ_Decompress")); - if (OodleLZ_Decompress == nullptr) - { - spdlog::error("Could not get OodleLZ_Decompress"); - return false; - } - - auto filepath = CET::Get().GetPaths().CETRoot() / c_defaultFilename; - if (!exists(filepath)) - return false; - - m_resources.reserve(1485150); - - try - { - std::ifstream file(filepath, std::ios::binary); - file.exceptions(std::ios::badbit); - - size_t headerSize = 8; - - std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - std::string buffer; - buffer.resize(*reinterpret_cast(content.data() + 4)); - - char workingMemory[0x80000]; - - auto size = OodleLZ_Decompress(content.data() + headerSize, content.size() - headerSize, buffer.data(), - buffer.size(), 1, 1, 0, 0, 0, 0, 0, workingMemory, std::size(workingMemory), 3); - - if (size > 0) - { - buffer.resize(size); - } - else - { - spdlog::error("Decompress failed!"); - return false; - } - - std::istringstream iss(buffer); - - std::string filename; - while (std::getline(iss, filename)) - { - filename.resize(filename.size() - 1); - m_resources.emplace_back(filename); - } - - for (auto& resource : m_resources) - { - m_resourcesByHash.emplace(resource.m_hash, &resource); - } - - return m_isInitialized = true; - } - // this is easier for now - catch (std::exception&) - { - return m_isInitialized = false; - } - } - - bool IsInitialized() - { - return m_isInitialized; - } - - const std::string& Resolve(uint64_t aHash) - { - static std::string defaultName = "ERROR_UNKNOWN_RESOURCE"; - - const auto it = m_resourcesByHash.find(aHash); - if (it == m_resourcesByHash.end()) - return defaultName; - else - return it->second->m_name; - } - - TiltedPhoques::Vector& GetResources() - { - return m_resources; - } - -protected: - void Reset() - { - m_isInitialized = false; - m_resources.clear(); - m_resourcesByHash.clear(); - } - -private: - bool m_isInitialized = false; - TiltedPhoques::Vector m_resources; - TiltedPhoques::Map m_resourcesByHash; -}; - namespace ImGui { std::pair InputTextCStr(const char* acpLabel, const char* acpBuf, size_t aBufSize, @@ -344,80 +35,66 @@ std::pair InputTextCStr(const char* acpLabel, const char* ac isModified = false; tempStr.clear(); aFlags |= ImGuiInputTextFlags_CallbackResize; - bool ret = ImGui::InputText(acpLabel, const_cast(acpBuf), aBufSize + 1, aFlags, - [](ImGuiInputTextCallbackData* apData) - { - if (apData->EventFlag == ImGuiInputTextFlags_CallbackResize) - { - tempStr.resize(apData->BufTextLen); - apData->Buf = tempStr.data(); - isModified = true; - } - return 0; - }); + const bool ret = InputText(acpLabel, const_cast(acpBuf), aBufSize + 1, aFlags, + [](ImGuiInputTextCallbackData* apData) + { + if (apData->EventFlag == ImGuiInputTextFlags_CallbackResize) + { + tempStr.resize(apData->BufTextLen); + apData->Buf = tempStr.data(); + isModified = true; + } + return 0; + }); return {ret && isModified, &tempStr}; } - else - { - ImGui::InputText(acpLabel, const_cast(acpBuf), aBufSize, aFlags); - return {false, nullptr}; - } + + InputText(acpLabel, const_cast(acpBuf), aBufSize, aFlags); + return {false, nullptr}; } void SetTooltipUnformatted(const char* acpText) { - ImGui::BeginTooltip(); - ImGui::TextUnformatted(acpText); - ImGui::EndTooltip(); + BeginTooltip(); + TextUnformatted(acpText); + EndTooltip(); } bool NextItemVisible(const ImVec2& aSize = ImVec2(1, 1), bool aClaimSpaceIfInvisible = true) { - ImVec2 rectMin = ImGui::GetCursorScreenPos(); - ImVec2 rectMax = ImVec2(rectMin.x + aSize.x, rectMin.y + aSize.y); - bool visible = ImGui::IsRectVisible(rectMin, rectMax); + const ImVec2 rectMin = GetCursorScreenPos(); + const auto rectMax = ImVec2(rectMin.x + aSize.x, rectMin.y + aSize.y); + const bool visible = IsRectVisible(rectMin, rectMax); if (!visible && aClaimSpaceIfInvisible) { - ImGui::Dummy(aSize); + Dummy(aSize); } return visible; } } -bool SortString(const std::string& acLeft, const std::string& acRight) -{ - size_t minLength = std::min(acLeft.size(), acRight.size()); - for (size_t i = 0; i != minLength; ++i) - { - char a = std::tolower(acLeft[i]); - char b = std::tolower(acRight[i]); - if (a != b) - return a < b; - } - return acLeft.size() < acRight.size(); -} - bool SortTweakDBIDString(const std::string& acLeft, const std::string& acRight) { // unknown TweakDBID should be at the bottom if (acLeft[0] == '<' && acRight[0] != '<') return false; - else if (acLeft[0] != '<' && acRight[0] == '<') + + if (acLeft[0] != '<' && acRight[0] == '<') return true; - return SortString(acLeft, acRight); + return std::ranges::lexicographical_compare(acLeft, acRight); } bool StringContains(const std::string_view& acString, const std::string_view& acSearch, bool aRegex = false) { - if (acSearch.size() == 0) + if (acSearch.empty()) return false; if (aRegex) { try { - std::regex searchRegex(acSearch.begin(), acSearch.end()); + const std::regex searchRegex(acSearch.begin(), acSearch.end()); return std::regex_search(acString.begin(), acString.end(), searchRegex); } catch (std::regex_error&) @@ -425,30 +102,20 @@ bool StringContains(const std::string_view& acString, const std::string_view& ac return false; } } - else - { - const auto it = std::search(acString.begin(), acString.end(), acSearch.begin(), acSearch.end(), - [](char a, char b) { return std::tolower(a) == std::tolower(b); }); - return it != acString.end(); - } -} -TweakDBEditor::TweakDBEditor(LuaVM& aVm) - : m_vm(aVm) -{ -} -bool TweakDBEditor::OnEnable() -{ - return true; + const auto it = std::ranges::search(acString, acSearch, + [](char a, char b) { return std::tolower(a) == std::tolower(b); }).begin(); + return it != acString.end(); } -bool TweakDBEditor::OnDisable() +TweakDBEditor::TweakDBEditor(LuaVM& aVm) + : Widget("TweakDB Editor") + , m_vm(aVm) { - return true; } -void TweakDBEditor::Update() +void TweakDBEditor::OnUpdate() { // LuaVM is initialized after TweakDB, let's wait for it if (!m_vm.IsInitialized()) @@ -459,7 +126,6 @@ void TweakDBEditor::Update() if (!m_initialized) { - CDPRTweakDBMetadata::Get()->Initialize(); RefreshAll(); m_initialized = true; } @@ -512,65 +178,44 @@ void TweakDBEditor::RefreshRecords() { auto* pTDB = RED4ext::TweakDB::Get(); - std::shared_lock _(pTDB->mutex01); - TiltedPhoques::Map> map; + std::shared_lock _(pTDB->mutex01); + + m_cachedRecords.clear(); - size_t recordsCount = 0; pTDB->recordsByType.for_each( - [this, &map, &recordsCount](RED4ext::CBaseRTTIType* aRTTIType, - RED4ext::DynArray> aRecords) + [this](const RED4ext::CBaseRTTIType* acpRTTIType, + const RED4ext::DynArray>& aRecords) { - recordsCount += aRecords.size; - RED4ext::CName typeName; - aRTTIType->GetName(typeName); + const auto typeName = acpRTTIType->GetName(); + + if (!m_cachedRecords.contains(typeName)) + m_cachedRecords.emplace(typeName, typeName); + auto& groupRecords = m_cachedRecords.at(typeName).m_records; - TiltedPhoques::Vector& recordsVec = map[typeName]; - recordsVec.reserve(aRecords.size); - for (RED4ext::Handle handle : aRecords) + groupRecords.reserve(groupRecords.size() + aRecords.size); + for (auto& handle : aRecords) { - auto* record = reinterpret_cast(handle.GetPtr()); + const auto* record = reinterpret_cast(handle.GetPtr()); std::string recordName = GetTweakDBIDStringRecord(record->recordID); if (TweakDB::IsACreatedRecord(record->recordID)) recordName.insert(0, "* "); CachedRecord cachedRecord(std::move(recordName), record->recordID); - recordsVec.emplace_back(std::move(cachedRecord)); + cachedRecord.InitializeFlats(); + groupRecords.emplace_back(std::move(cachedRecord)); } }); - - m_cachedRecords.clear(); - m_cachedRecords.reserve(recordsCount); - for (auto it = map.begin(); it != map.end(); ++it) - { - const auto cTypeName = it.key(); - auto& records = it.value(); - - std::for_each(std::execution::par_unseq, records.begin(), records.end(), - [](CachedRecord& record) { record.InitializeFlats(); }); - - CachedRecordGroup recordGroup(cTypeName); - recordGroup.m_records = std::move(records); - m_cachedRecords.emplace_back(std::move(recordGroup)); - } - - std::ranges::sort(m_cachedRecords, - [](const CachedRecordGroup& acLeft, const CachedRecordGroup& acRight) - { - return SortString(acLeft.m_name, acRight.m_name); - }); } void TweakDBEditor::RefreshFlats() { auto* pTDB = RED4ext::TweakDB::Get(); - constexpr uint64_t unknownGroupHash = RED4ext::FNV1a("!Unknown!"); - constexpr uint64_t badGroupHash = RED4ext::FNV1a("!BadName!"); - std::shared_lock _1(pTDB->mutex00); - std::shared_lock _2(pTDB->mutex01); - std::mutex mapMutex; - TiltedPhoques::Map map; + std::shared_lock _1(pTDB->mutex00); + std::shared_lock _2(pTDB->mutex01); + + m_cachedFlatGroups.clear(); - std::for_each(std::execution::par_unseq, pTDB->flats.begin(), pTDB->flats.end(), [&](RED4ext::TweakDBID& dbid) + std::ranges::for_each(pTDB->flats, [&](RED4ext::TweakDBID dbid) { const uint64_t dbidBase = m_vm.GetTDBIDBase(dbid); if (dbidBase != 0 && pTDB->recordsByID.Get(dbidBase) != nullptr) @@ -578,36 +223,33 @@ void TweakDBEditor::RefreshFlats() std::string name; CachedFlatGroup* flatGroup = nullptr; - bool unknownFlatName = !GetTweakDBIDStringFlat(dbid, name); - std::lock_guard _(mapMutex); + const bool unknownFlatName = !GetTweakDBIDStringFlat(dbid, name); if (unknownFlatName) { - const auto it = map.find(unknownGroupHash); - if (it == map.end()) - flatGroup = &map.emplace(unknownGroupHash, CachedFlatGroup("!Unknown!")).first.value(); - else + if (const auto it = m_cachedFlatGroups.find("!Unknown!"); it != m_cachedFlatGroups.cend()) flatGroup = &it.value(); + else + flatGroup = &m_cachedFlatGroups.emplace("!Unknown!", "!Unknown!").first.value(); } else { size_t idx = name.find('.'); if (idx == std::string::npos) { - const auto it = map.find(badGroupHash); - if (it == map.end()) - flatGroup = &map.emplace(badGroupHash, CachedFlatGroup("!BadName!")).first.value(); - else + if (const auto it = m_cachedFlatGroups.find("!BadName!"); it != m_cachedFlatGroups.cend()) flatGroup = &it.value(); + else + flatGroup = &m_cachedFlatGroups.emplace("!BadName!", "!BadName!").first.value(); } else { // < 1 || > size = group as much as possible for (int32_t i = 1; i != m_flatGroupNameDepth; ++i) { - if ((idx + 1) == name.size()) + if (idx + 1 == name.size()) break; - size_t idx2 = name.find('.', idx + 1); + const size_t idx2 = name.find('.', idx + 1); if (idx2 == std::string::npos) break; @@ -615,35 +257,27 @@ void TweakDBEditor::RefreshFlats() } std::string groupName = name.substr(0, idx); - uint64_t groupHash = RED4ext::FNV1a(groupName.c_str()); - const auto it = map.find(groupHash); - if (it == map.end()) - flatGroup = &map.emplace(groupHash, std::move(groupName)).first.value(); - else + if (const auto it = m_cachedFlatGroups.find(groupName.c_str()); it != m_cachedFlatGroups.cend()) flatGroup = &it.value(); + else + flatGroup = &m_cachedFlatGroups.emplace(groupName.c_str(), groupName.c_str()).first.value(); } } flatGroup->m_flats.emplace_back(std::move(name), dbid); }); - m_cachedFlatGroups.clear(); - m_cachedFlatGroups.reserve(map.size()); - for (auto it = map.begin(); it != map.end(); ++it) + if (const auto it = m_cachedFlatGroups.find("!Unknown!"); it != m_cachedFlatGroups.cend()) { - const auto cGroupHash = it.key(); auto& group = it.value(); - if (cGroupHash == unknownGroupHash || cGroupHash == badGroupHash) - group.m_name = fmt::format("{} - {} flats!", group.m_name, group.m_flats.size()); - - m_cachedFlatGroups.emplace_back(std::move(group)); + group.m_name = fmt::format("{} - {} flats!", group.m_name, group.m_flats.size()); } - std::ranges::sort(m_cachedFlatGroups, - [](const CachedFlatGroup& acLeft, const CachedFlatGroup& acRight) - { - return SortString(acLeft.m_name, acRight.m_name); - }); + if (const auto it = m_cachedFlatGroups.find("!BadName!"); it != m_cachedFlatGroups.cend()) + { + auto& group = it.value(); + group.m_name = fmt::format("{} - {} flats!", group.m_name, group.m_flats.size()); + } } void TweakDBEditor::FilterAll() @@ -669,10 +303,12 @@ RED4ext::TweakDBID ExtractTweakDBIDFromString(const char* acString) void TweakDBEditor::FilterRecords(bool aFilterTab, bool aFilterDropdown) { - RED4ext::TweakDBID dbid = ExtractTweakDBIDFromString(s_recordsFilterBuffer); - RED4ext::TweakDBID dbidDropdown = ExtractTweakDBIDFromString(s_tweakdbidFilterBuffer); - for (CachedRecordGroup& group : m_cachedRecords) + const RED4ext::TweakDBID dbid = ExtractTweakDBIDFromString(s_recordsFilterBuffer); + const RED4ext::TweakDBID dbidDropdown = ExtractTweakDBIDFromString(s_tweakdbidFilterBuffer); + for (auto it = m_cachedRecords.begin(); it != m_cachedRecords.end(); ++it) { + auto& group = it.value(); + bool anyRecordsVisible = false; std::for_each(std::execution::par_unseq, group.m_records.begin(), group.m_records.end(), [&](CachedRecord& record) @@ -732,10 +368,12 @@ void TweakDBEditor::FilterFlats() { if (s_flatsFilterBuffer[0] == '\0') { - for (CachedFlatGroup& group : m_cachedFlatGroups) + for (auto it = m_cachedFlatGroups.begin(); it != m_cachedFlatGroups.end(); ++it) { + auto& group = it.value(); + group.m_isFiltered = false; - for (CachedFlat& flat : group.m_flats) + for (auto& flat : group.m_flats) { flat.m_isFiltered = false; } @@ -743,11 +381,13 @@ void TweakDBEditor::FilterFlats() } else { - RED4ext::TweakDBID dbid = ExtractTweakDBIDFromString(s_flatsFilterBuffer); - for (CachedFlatGroup& group : m_cachedFlatGroups) + const RED4ext::TweakDBID dbid = ExtractTweakDBIDFromString(s_flatsFilterBuffer); + for (auto it = m_cachedFlatGroups.begin(); it != m_cachedFlatGroups.end(); ++it) { + auto& group = it.value(); + bool anyFlatsVisible = false; - for (CachedFlat& flat : group.m_flats) + for (auto& flat : group.m_flats) { if (flat.m_dbid != dbid && !StringContains(flat.m_name, s_flatsFilterBuffer, s_flatsFilterIsRegex)) @@ -769,16 +409,16 @@ void TweakDBEditor::FilterFlats() bool TweakDBEditor::DrawRecordDropdown(const char* acpLabel, RED4ext::TweakDBID& aDBID, float aWidth) { bool valueChanged = false; - if (aWidth) + if (aWidth != 0.0f) ImGui::SetNextItemWidth(aWidth); - std::string recordName = GetTweakDBIDStringRecord(aDBID); - bool comboOpened = ImGui::BeginCombo(acpLabel, recordName.c_str(), ImGuiComboFlags_HeightLargest); + const std::string recordName = GetTweakDBIDStringRecord(aDBID); + const bool comboOpened = ImGui::BeginCombo(acpLabel, recordName.c_str(), ImGuiComboFlags_HeightLargest); if (ImGui::IsItemHovered()) { RED4ext::Handle record; if (RED4ext::TweakDB::Get()->TryGetRecord(aDBID, record)) { - RED4ext::CName typeName = record->GetType()->GetName(); + const RED4ext::CName typeName = record->GetType()->GetName(); ImGui::SetTooltipUnformatted(typeName.ToString()); } else @@ -796,7 +436,7 @@ bool TweakDBEditor::DrawRecordDropdown(const char* acpLabel, RED4ext::TweakDBID& searchTimer = c_searchDelay; } - if (searchTimer) + if (searchTimer != 0.0f) { searchTimer -= ImGui::GetIO().DeltaTime; if (searchTimer <= 0.0f) @@ -808,14 +448,14 @@ bool TweakDBEditor::DrawRecordDropdown(const char* acpLabel, RED4ext::TweakDBID& if (ImGui::BeginChild("##dropdownScroll", ImVec2(0, g_comboDropdownHeight))) { - for (CachedRecordGroup& recordGroup : m_cachedRecords) + for (const auto& recordGroup : m_cachedRecords | std::views::values) { - for (CachedRecord& record : recordGroup.m_records) + for (const auto& record : recordGroup.m_records) { if (record.m_isDropdownFiltered) continue; - bool isSelected = record.m_dbid == aDBID; + const bool isSelected = record.m_dbid == aDBID; if (ImGui::NextItemVisible(ImVec2(1.0f, ImGui::GetTextLineHeight())) && ImGui::Selectable(record.m_name.c_str(), isSelected)) { @@ -849,46 +489,37 @@ std::string TweakDBEditor::GetTweakDBIDStringRecord(RED4ext::TweakDBID aDBID) { std::string name; GetTweakDBIDStringRecord(aDBID, name); - return std::move(name); + return name; } bool TweakDBEditor::GetTweakDBIDStringRecord(RED4ext::TweakDBID aDBID, std::string& aString) { - if (CDPRTweakDBMetadata::Get()->GetRecordName(aDBID, aString)) - return true; - aString = CET::Get().GetVM().GetTDBIDString(aDBID); - return aString[0] != '<' || aString[aString.size() - 1] != '>'; + return !aString.starts_with('<') || !aString.ends_with('>'); } std::string TweakDBEditor::GetTweakDBIDStringFlat(RED4ext::TweakDBID aDBID) { std::string name; GetTweakDBIDStringFlat(aDBID, name); - return std::move(name); + return name; } bool TweakDBEditor::GetTweakDBIDStringFlat(RED4ext::TweakDBID aDBID, std::string& aString) { - if (CDPRTweakDBMetadata::Get()->GetFlatName(aDBID, aString)) - return true; - aString = CET::Get().GetVM().GetTDBIDString(aDBID); - return aString[0] != '<' || aString[aString.size() - 1] != '>'; + return !aString.starts_with('<') || !aString.ends_with('>'); } std::string TweakDBEditor::GetTweakDBIDStringQuery(RED4ext::TweakDBID aDBID) { std::string name; GetTweakDBIDStringQuery(aDBID, name); - return std::move(name); + return name; } bool TweakDBEditor::GetTweakDBIDStringQuery(RED4ext::TweakDBID aDBID, std::string& aString) { - if (CDPRTweakDBMetadata::Get()->GetQueryName(aDBID, aString)) - return true; - aString = CET::Get().GetVM().GetTDBIDString(aDBID); return aString[0] != '<' || aString[aString.size() - 1] != '>'; } @@ -909,7 +540,7 @@ bool TweakDBEditor::DrawFlat(RED4ext::TweakDBID aDBID) ImGui::PushID(aDBID.name.hash); ImGui::PushID(aDBID.name.length); - bool isModified = DrawFlat(aDBID, data); + const bool isModified = DrawFlat(aDBID, data); ImGui::PopID(); ImGui::PopID(); @@ -935,35 +566,34 @@ bool TweakDBEditor::DrawFlat(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aSta if (aStackType.type->GetType() == RED4ext::ERTTIType::Array) return DrawFlatArray(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pTweakDBIDType) + if (aStackType.type == pTweakDBIDType) return DrawFlatTweakDBID(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pQuaternionType) + if (aStackType.type == pQuaternionType) return DrawFlatQuaternion(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pEulerAnglesType) + if (aStackType.type == pEulerAnglesType) return DrawFlatEulerAngles(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pVector3Type) + if (aStackType.type == pVector3Type) return DrawFlatVector3(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pVector2Type) + if (aStackType.type == pVector2Type) return DrawFlatVector2(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pColorType) + if (aStackType.type == pColorType) return DrawFlatColor(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pGamedataLocKeyWrapperType) + if (aStackType.type == pGamedataLocKeyWrapperType) return DrawFlatLocKeyWrapper(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pRaRefCResourceType) + if (aStackType.type == pRaRefCResourceType) return DrawFlatResourceAsyncRef(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pCNameType) + if (aStackType.type == pCNameType) return DrawFlatCName(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pBoolType) + if (aStackType.type == pBoolType) return DrawFlatBool(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pStringType) + if (aStackType.type == pStringType) return DrawFlatString(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pFloatType) + if (aStackType.type == pFloatType) return DrawFlatFloat(aDBID, aStackType, aReadOnly); - else if (aStackType.type == pInt32Type) + if (aStackType.type == pInt32Type) return DrawFlatInt32(aDBID, aStackType, aReadOnly); - RED4ext::CName typeName; - aStackType.type->GetName(typeName); + const auto typeName = aStackType.type->GetName(); ImGui::Text("unsupported type: %s", typeName.ToString()); return false; } @@ -974,13 +604,12 @@ bool TweakDBEditor::DrawFlatArray(RED4ext::TweakDBID aDBID, RED4ext::CStackType& { static TiltedPhoques::Map editedArrays; - auto* pArrayType = reinterpret_cast(aStackType.type); + auto* pArrayType = reinterpret_cast(aStackType.type); auto* pArrayInnerType = pArrayType->GetInnerType(); - RED4ext::CName arrayTypeName; - pArrayType->GetName(arrayTypeName); + const auto arrayTypeName = pArrayType->GetName(); bool isModified = false; - bool isCachable = !aReadOnly && aDBID.IsValid(); + const bool isCachable = !aReadOnly && aDBID.IsValid(); bool isCached = false; // if it's currently in 'editedArrays' RED4ext::ScriptInstance arrayInstance = aStackType.value; @@ -1016,7 +645,7 @@ bool TweakDBEditor::DrawFlatArray(RED4ext::TweakDBID aDBID, RED4ext::CStackType& { auto* allocator = pArrayType->GetAllocator(); auto result = allocator->AllocAligned(pArrayType->GetSize(), pArrayType->GetAlignment()); - pArrayType->Init(result.memory); + pArrayType->Construct(result.memory); pArrayType->Assign(result.memory, arrayInstance); editedArrays.emplace(arrayKey, result.memory); } @@ -1026,7 +655,7 @@ bool TweakDBEditor::DrawFlatArray(RED4ext::TweakDBID aDBID, RED4ext::CStackType& ImGui::SameLine(); if (ImGui::Button("cancel")) { - pArrayType->Destroy(arrayInstance); + pArrayType->Destruct(arrayInstance); pArrayType->GetAllocator()->Free(arrayInstance); editedArrays.erase(arrayKey); // arraySize = 0; @@ -1039,10 +668,10 @@ bool TweakDBEditor::DrawFlatArray(RED4ext::TweakDBID aDBID, RED4ext::CStackType& ImGui::SameLine(); if (ImGui::Button("save")) { - RED4ext::CStackType newStackType(aStackType.type, arrayInstance); + const RED4ext::CStackType newStackType(aStackType.type, arrayInstance); isModified = TweakDB::InternalSetFlat(aDBID, newStackType); - pArrayType->Destroy(arrayInstance); + pArrayType->Destruct(arrayInstance); pArrayType->GetAllocator()->Free(arrayInstance); editedArrays.erase(arrayKey); // arraySize = 0; @@ -1068,8 +697,8 @@ bool TweakDBEditor::DrawFlatArray(RED4ext::TweakDBID aDBID, RED4ext::CStackType& // TODO: fix column resizing issue on first frame if (ImGui::BeginTable("arrayElements", 2, tableFlags)) { - uint32_t deleteElementIdx = -1; - for (uint32_t i = 0; i != arraySize; ++i) + int32_t deleteElementIdx = -1; + for (int32_t i = 0; i < static_cast(arraySize); ++i) { ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -1092,7 +721,7 @@ bool TweakDBEditor::DrawFlatArray(RED4ext::TweakDBID aDBID, RED4ext::CStackType& RED4ext::CStackType stackType; stackType.type = pArrayInnerType; stackType.value = pArrayType->GetElement(arrayInstance, i); - bool flatModified = DrawFlat({}, stackType, aReadOnly); + const bool flatModified = DrawFlat({}, stackType, aReadOnly); // cached arrays' isModified is managed by the save button if (!isCached) isModified |= flatModified; @@ -1105,14 +734,14 @@ bool TweakDBEditor::DrawFlatArray(RED4ext::TweakDBID aDBID, RED4ext::CStackType& if (!aReadOnly && ImGui::Button("add new")) { pArrayType->InsertAt(arrayInstance, arraySize); - pArrayInnerType->Init(pArrayType->GetElement(arrayInstance, arraySize)); + pArrayInnerType->Construct(pArrayType->GetElement(arrayInstance, arraySize)); } ImGui::EndTable(); - if (deleteElementIdx != -1) + if (deleteElementIdx > -1) { - pArrayType->RemoveAt(arrayInstance, deleteElementIdx); + pArrayType->RemoveAt(arrayInstance, static_cast(deleteElementIdx)); } } @@ -1130,7 +759,7 @@ bool TweakDBEditor::DrawFlatArray(RED4ext::TweakDBID aDBID, RED4ext::CStackType& bool TweakDBEditor::DrawFlatTweakDBID(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pDBID = reinterpret_cast(aStackType.value); + const auto* pDBID = static_cast(aStackType.value); if (aReadOnly) { @@ -1143,7 +772,7 @@ bool TweakDBEditor::DrawFlatTweakDBID(RED4ext::TweakDBID aDBID, RED4ext::CStackT RED4ext::Handle record; if (pDBID->IsValid() && RED4ext::TweakDB::Get()->TryGetRecord(pDBID->value, record)) { - RED4ext::CName typeName = record->GetType()->GetName(); + const RED4ext::CName typeName = record->GetType()->GetName(); ImGui::SetTooltipUnformatted(typeName.ToString()); } else @@ -1161,7 +790,7 @@ bool TweakDBEditor::DrawFlatTweakDBID(RED4ext::TweakDBID aDBID, RED4ext::CStackT RED4ext::TweakDBID dbid = *pDBID; bool valueChanged = DrawRecordDropdown("", dbid, -FLT_MIN); - int32_t flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CharsHexadecimal; + constexpr int32_t flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CharsHexadecimal; ImGui::SetNextItemWidth(-FLT_MIN); valueChanged |= ImGui::InputScalar("##raw", ImGuiDataType_U64, &dbid.value, nullptr, nullptr, "%016llX", flags); @@ -1169,14 +798,12 @@ bool TweakDBEditor::DrawFlatTweakDBID(RED4ext::TweakDBID aDBID, RED4ext::CStackT { if (aDBID.IsValid()) { - RED4ext::CStackType newStackType(aStackType.type, &dbid); + const RED4ext::CStackType newStackType(aStackType.type, &dbid); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &dbid); - return true; - } + + aStackType.type->Move(aStackType.value, &dbid); + return true; } } @@ -1185,14 +812,14 @@ bool TweakDBEditor::DrawFlatTweakDBID(RED4ext::TweakDBID aDBID, RED4ext::CStackT bool TweakDBEditor::DrawFlatQuaternion(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pQuat = static_cast(aStackType.value); + const auto* pQuat = static_cast(aStackType.value); float i = pQuat->i; float j = pQuat->j; float k = pQuat->k; float r = pQuat->r; - int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; + const int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; ImGui::TextUnformatted("I"); ImGui::SameLine(); @@ -1216,7 +843,7 @@ bool TweakDBEditor::DrawFlatQuaternion(RED4ext::TweakDBID aDBID, RED4ext::CStack if (!aReadOnly && valueChanged) { - RED4ext::Quaternion newQuat; + RED4ext::Quaternion newQuat{}; newQuat.i = i; newQuat.j = j; newQuat.k = k; @@ -1224,14 +851,12 @@ bool TweakDBEditor::DrawFlatQuaternion(RED4ext::TweakDBID aDBID, RED4ext::CStack if (aDBID.IsValid()) { - RED4ext::CStackType newStackType(aStackType.type, &newQuat); + const RED4ext::CStackType newStackType(aStackType.type, &newQuat); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &newQuat); - return true; - } + + aStackType.type->Move(aStackType.value, &newQuat); + return true; } return false; @@ -1239,13 +864,13 @@ bool TweakDBEditor::DrawFlatQuaternion(RED4ext::TweakDBID aDBID, RED4ext::CStack bool TweakDBEditor::DrawFlatEulerAngles(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pEular = static_cast(aStackType.value); + const auto* pEular = static_cast(aStackType.value); float roll = pEular->Roll; float pitch = pEular->Pitch; float yaw = pEular->Yaw; - int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; + const int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; ImGui::TextUnformatted("Roll "); ImGui::SameLine(); @@ -1264,21 +889,19 @@ bool TweakDBEditor::DrawFlatEulerAngles(RED4ext::TweakDBID aDBID, RED4ext::CStac if (!aReadOnly && valueChanged) { - RED4ext::EulerAngles newEular; + RED4ext::EulerAngles newEular{}; newEular.Roll = roll; newEular.Pitch = pitch; newEular.Yaw = yaw; if (aDBID.IsValid()) { - RED4ext::CStackType newStackType(aStackType.type, &newEular); + const RED4ext::CStackType newStackType(aStackType.type, &newEular); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &newEular); - return true; - } + + aStackType.type->Move(aStackType.value, &newEular); + return true; } return false; @@ -1286,13 +909,13 @@ bool TweakDBEditor::DrawFlatEulerAngles(RED4ext::TweakDBID aDBID, RED4ext::CStac bool TweakDBEditor::DrawFlatVector3(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pVec = static_cast(aStackType.value); + const auto* pVec = static_cast(aStackType.value); float x = pVec->X; float y = pVec->Y; float z = pVec->Z; - int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; + const int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; ImGui::TextUnformatted("X"); ImGui::SameLine(); @@ -1311,21 +934,19 @@ bool TweakDBEditor::DrawFlatVector3(RED4ext::TweakDBID aDBID, RED4ext::CStackTyp if (!aReadOnly && valueChanged) { - RED4ext::Vector3 newVec; + RED4ext::Vector3 newVec{}; newVec.X = x; newVec.Y = y; newVec.Z = z; if (aDBID.IsValid()) { - RED4ext::CStackType newStackType(aStackType.type, &newVec); + const RED4ext::CStackType newStackType(aStackType.type, &newVec); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &newVec); - return true; - } + + aStackType.type->Move(aStackType.value, &newVec); + return true; } return false; @@ -1333,12 +954,12 @@ bool TweakDBEditor::DrawFlatVector3(RED4ext::TweakDBID aDBID, RED4ext::CStackTyp bool TweakDBEditor::DrawFlatVector2(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pVec = static_cast(aStackType.value); + const auto* pVec = static_cast(aStackType.value); float x = pVec->X; float y = pVec->Y; - int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; + const int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; ImGui::TextUnformatted("X"); ImGui::SameLine(); @@ -1352,20 +973,18 @@ bool TweakDBEditor::DrawFlatVector2(RED4ext::TweakDBID aDBID, RED4ext::CStackTyp if (!aReadOnly && valueChanged) { - RED4ext::Vector2 newVec; + RED4ext::Vector2 newVec{}; newVec.X = x; newVec.Y = y; if (aDBID.IsValid()) { - RED4ext::CStackType newStackType(aStackType.type, &newVec); + const RED4ext::CStackType newStackType(aStackType.type, &newVec); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &newVec); - return true; - } + + aStackType.type->Move(aStackType.value, &newVec); + return true; } return false; @@ -1373,7 +992,7 @@ bool TweakDBEditor::DrawFlatVector2(RED4ext::TweakDBID aDBID, RED4ext::CStackTyp bool TweakDBEditor::DrawFlatColor(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pColor = static_cast(aStackType.value); + const auto* pColor = static_cast(aStackType.value); float rgba[4]; rgba[0] = pColor->Red / 255.0f; @@ -1385,7 +1004,7 @@ bool TweakDBEditor::DrawFlatColor(RED4ext::TweakDBID aDBID, RED4ext::CStackType& ImGui::TextUnformatted("'Color' is not supported yet"); ImGui::SameLine(); - int32_t flags = aReadOnly ? ImGuiColorEditFlags_NoInputs : ImGuiColorEditFlags_None; + const int32_t flags = aReadOnly ? ImGuiColorEditFlags_NoInputs : ImGuiColorEditFlags_None; ImGui::SetNextItemWidth(-FLT_MIN); bool valueChanged = ImGui::ColorEdit4("", rgba, flags | ImGuiColorEditFlags_AlphaPreview); // Color picker returns true everytime it changes @@ -1395,22 +1014,20 @@ bool TweakDBEditor::DrawFlatColor(RED4ext::TweakDBID aDBID, RED4ext::CStackType& if (!aReadOnly && valueChanged) { - RED4ext::Color newColor; - newColor.Red = std::clamp(rgba[0], 0.0f, 1.0f) * 255; - newColor.Green = std::clamp(rgba[1], 0.0f, 1.0f) * 255; - newColor.Blue = std::clamp(rgba[2], 0.0f, 1.0f) * 255; - newColor.Alpha = std::clamp(rgba[3], 0.0f, 1.0f) * 255; + RED4ext::Color newColor{}; + newColor.Red = static_cast(std::clamp(rgba[0], 0.0f, 1.0f) * 255); + newColor.Green = static_cast(std::clamp(rgba[1], 0.0f, 1.0f) * 255); + newColor.Blue = static_cast(std::clamp(rgba[2], 0.0f, 1.0f) * 255); + newColor.Alpha = static_cast(std::clamp(rgba[3], 0.0f, 1.0f) * 255); if (aDBID.IsValid()) { - RED4ext::CStackType newStackType(aStackType.type, &newColor); + const RED4ext::CStackType newStackType(aStackType.type, &newColor); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &newColor); - return true; - } + + aStackType.type->Move(aStackType.value, &newColor); + return true; } return false; @@ -1418,22 +1035,21 @@ bool TweakDBEditor::DrawFlatColor(RED4ext::TweakDBID aDBID, RED4ext::CStackType& bool TweakDBEditor::DrawFlatLocKeyWrapper(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pLocKey = static_cast(aStackType.value); + const auto* pLocKey = static_cast(aStackType.value); ImGui::TextUnformatted("This is a LocalizationKey"); ImGui::TextUnformatted("Game.GetLocalizedTextByKey(...)"); uint64_t key = pLocKey->primaryKey; - int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; + const int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; ImGui::SetNextItemWidth(-FLT_MIN); - bool valueChanged = ImGui::InputScalar("", ImGuiDataType_U64, &key, nullptr, nullptr, nullptr, flags); + const bool valueChanged = ImGui::InputScalar("", ImGuiDataType_U64, &key, nullptr, nullptr, nullptr, flags); { RED4ext::CString localizedText; ExecuteGlobalFunction("GetLocalizedTextByKey", &localizedText, key); ImGui::SetNextItemWidth(-FLT_MIN); - ImGui::InputText("##string", (char*)localizedText.c_str(), localizedText.Length(), - ImGuiInputTextFlags_ReadOnly); + ImGui::TextUnformatted(localizedText.c_str(), localizedText.c_str() + localizedText.Length()); } if (!aReadOnly && valueChanged) @@ -1443,14 +1059,12 @@ bool TweakDBEditor::DrawFlatLocKeyWrapper(RED4ext::TweakDBID aDBID, RED4ext::CSt if (aDBID.IsValid()) { - RED4ext::CStackType newStackType(aStackType.type, &newLocKey); + const RED4ext::CStackType newStackType(aStackType.type, &newLocKey); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &newLocKey); - return true; - } + + aStackType.type->Move(aStackType.value, &newLocKey); + return true; } return false; @@ -1458,7 +1072,7 @@ bool TweakDBEditor::DrawFlatLocKeyWrapper(RED4ext::TweakDBID aDBID, RED4ext::CSt bool TweakDBEditor::DrawFlatResourceAsyncRef(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pRaRef = static_cast*>(aStackType.value); + const auto* pRaRef = static_cast*>(aStackType.value); uint64_t hashRef = pRaRef->path.hash; @@ -1483,13 +1097,13 @@ bool TweakDBEditor::DrawFlatResourceAsyncRef(RED4ext::TweakDBID aDBID, RED4ext:: searchTimer = c_searchDelay; } - if (searchTimer) + if (searchTimer != 0.0f) { searchTimer -= ImGui::GetIO().DeltaTime; if (searchTimer <= 0.0f) { auto& resources = ResourcesList::Get()->GetResources(); - resourcesCount = std::count_if(std::execution::par_unseq, resources.begin(), resources.end(), + resourcesCount = static_cast(std::count_if(std::execution::par_unseq, resources.begin(), resources.end(), [](ResourcesList::Resource& resource) { if (comboSearchStr[0] == '\0' || @@ -1503,7 +1117,7 @@ bool TweakDBEditor::DrawFlatResourceAsyncRef(RED4ext::TweakDBID aDBID, RED4ext:: } return !resource.m_isFiltered; - }); + })); searchTimer = 0.0f; } @@ -1537,7 +1151,7 @@ bool TweakDBEditor::DrawFlatResourceAsyncRef(RED4ext::TweakDBID aDBID, RED4ext:: if (resource.m_isFiltered) continue; - bool isSelected = resource.m_hash == hashRef; + const bool isSelected = resource.m_hash == hashRef; if (ImGui::Selectable(resource.m_name.c_str(), isSelected)) { hashRef = resource.m_hash; @@ -1566,14 +1180,12 @@ bool TweakDBEditor::DrawFlatResourceAsyncRef(RED4ext::TweakDBID aDBID, RED4ext:: if (aDBID.IsValid()) { - RED4ext::CStackType newStackType(aStackType.type, &newRaRef); + const RED4ext::CStackType newStackType(aStackType.type, &newRaRef); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &newRaRef); - return true; - } + + aStackType.type->Move(aStackType.value, &newRaRef); + return true; } return false; @@ -1581,7 +1193,7 @@ bool TweakDBEditor::DrawFlatResourceAsyncRef(RED4ext::TweakDBID aDBID, RED4ext:: bool TweakDBEditor::DrawFlatCName(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pCName = static_cast(aStackType.value); + const auto* pCName = static_cast(aStackType.value); ImGui::TextUnformatted("Game is expecting specific values."); // Is it worth it to implement a dropdown like DrawTweakDBID? @@ -1596,14 +1208,14 @@ bool TweakDBEditor::DrawFlatCName(RED4ext::TweakDBID aDBID, RED4ext::CStackType& auto [strChanged, pModifiedStr] = ImGui::InputTextCStr("", pStr, strlen(pStr), flags); if (strChanged) { - if (stricmp(pModifiedStr->c_str(), "none") == 0) + if (_stricmp(pModifiedStr->c_str(), "none") == 0) newCName.hash = 0; else newCName = RED4ext::CName(pModifiedStr->c_str()); valueChanged = newCName.hash != pCName->hash; } - if (ImGui::IsItemHovered() && strnicmp(pStr, "LocKey#", 7) == 0) + if (ImGui::IsItemHovered() && _strnicmp(pStr, "LocKey#", 7) == 0) { RED4ext::CString localizedText; ExecuteGlobalFunction("GetLocalizedTextByKey", &localizedText, atoll(pStr + 7)); @@ -1612,8 +1224,8 @@ bool TweakDBEditor::DrawFlatCName(RED4ext::TweakDBID aDBID, RED4ext::CStackType& uint64_t hash = pCName->hash; ImGui::SetNextItemWidth(-FLT_MIN); - bool rawChanged = ImGui::InputScalar("##raw", ImGuiDataType_U64, &hash, nullptr, nullptr, "%016llX", - flags | ImGuiInputTextFlags_CharsHexadecimal); + const bool rawChanged = ImGui::InputScalar("##raw", ImGuiDataType_U64, &hash, nullptr, nullptr, "%016llX", + flags | ImGuiInputTextFlags_CharsHexadecimal); if (rawChanged) { newCName.hash = hash; @@ -1627,11 +1239,9 @@ bool TweakDBEditor::DrawFlatCName(RED4ext::TweakDBID aDBID, RED4ext::CStackType& const RED4ext::CStackType newStackType(aStackType.type, &newCName); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &newCName); - return true; - } + + aStackType.type->Move(aStackType.value, &newCName); + return true; } return valueChanged; @@ -1639,7 +1249,7 @@ bool TweakDBEditor::DrawFlatCName(RED4ext::TweakDBID aDBID, RED4ext::CStackType& bool TweakDBEditor::DrawFlatBool(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pBool = static_cast(aStackType.value); + const auto* pBool = static_cast(aStackType.value); bool val = *pBool; const bool valueChanged = ImGui::Checkbox("", &val); @@ -1650,11 +1260,9 @@ bool TweakDBEditor::DrawFlatBool(RED4ext::TweakDBID aDBID, RED4ext::CStackType& const RED4ext::CStackType newStackType(aStackType.type, &val); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &val); - return true; - } + + aStackType.type->Move(aStackType.value, &val); + return true; } return false; @@ -1662,15 +1270,15 @@ bool TweakDBEditor::DrawFlatBool(RED4ext::TweakDBID aDBID, RED4ext::CStackType& bool TweakDBEditor::DrawFlatString(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pCString = reinterpret_cast(aStackType.value); + const auto* pCString = static_cast(aStackType.value); - int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; + const int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; ImGui::SetNextItemWidth(-FLT_MIN); auto [valueChanged, pModifiedStr] = ImGui::InputTextCStr("", pCString->c_str(), pCString->Length(), flags); - if (ImGui::IsItemHovered() && strnicmp(pCString->c_str(), "LocKey#", 7) == 0) + if (ImGui::IsItemHovered() && _strnicmp(pCString->c_str(), "LocKey#", 7) == 0) { RED4ext::CString localizedText; - RED4ext::ExecuteGlobalFunction("GetLocalizedTextByKey", &localizedText, atoll(pCString->c_str() + 7)); + ExecuteGlobalFunction("GetLocalizedTextByKey", &localizedText, atoll(pCString->c_str() + 7)); ImGui::SetTooltipUnformatted(localizedText.c_str()); } @@ -1679,14 +1287,12 @@ bool TweakDBEditor::DrawFlatString(RED4ext::TweakDBID aDBID, RED4ext::CStackType RED4ext::CString newCString(pModifiedStr->c_str()); if (aDBID.IsValid()) { - RED4ext::CStackType newStackType(aStackType.type, &newCString); + const RED4ext::CStackType newStackType(aStackType.type, &newCString); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &newCString); - return true; - } + + aStackType.type->Move(aStackType.value, &newCString); + return true; } return false; @@ -1694,24 +1300,22 @@ bool TweakDBEditor::DrawFlatString(RED4ext::TweakDBID aDBID, RED4ext::CStackType bool TweakDBEditor::DrawFlatFloat(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pFloat = reinterpret_cast(aStackType.value); + const auto* pFloat = static_cast(aStackType.value); float val = *pFloat; ImGui::SetNextItemWidth(-FLT_MIN); - bool valueChanged = ImGui::InputFloat( + const bool valueChanged = ImGui::InputFloat( "", &val, 0, 0, "%f", aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue); if (valueChanged) { if (aDBID.IsValid()) { - RED4ext::CStackType newStackType(aStackType.type, &val); + const RED4ext::CStackType newStackType(aStackType.type, &val); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &val); - return true; - } + + aStackType.type->Move(aStackType.value, &val); + return true; } return false; @@ -1719,24 +1323,22 @@ bool TweakDBEditor::DrawFlatFloat(RED4ext::TweakDBID aDBID, RED4ext::CStackType& bool TweakDBEditor::DrawFlatInt32(RED4ext::TweakDBID aDBID, RED4ext::CStackType& aStackType, bool aReadOnly) { - auto* pInt = static_cast(aStackType.value); + const auto* pInt = static_cast(aStackType.value); int32_t val = *pInt; ImGui::SetNextItemWidth(-FLT_MIN); - bool valueChanged = ImGui::InputInt( + const bool valueChanged = ImGui::InputInt( "", &val, 0, 0, aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue); if (valueChanged) { if (aDBID.IsValid()) { - RED4ext::CStackType newStackType(aStackType.type, &val); + const RED4ext::CStackType newStackType(aStackType.type, &val); return TweakDB::InternalSetFlat(aDBID, newStackType); } - else - { - aStackType.type->Move(aStackType.value, &val); - return true; - } + + aStackType.type->Move(aStackType.value, &val); + return true; } return false; @@ -1758,7 +1360,7 @@ void TweakDBEditor::DrawRecordsTab() searchTimer = -1.0f; } - if (searchTimer) + if (searchTimer != 0.0f) { searchTimer -= ImGui::GetIO().DeltaTime; if (searchTimer <= 0.0f) @@ -1771,8 +1373,10 @@ void TweakDBEditor::DrawRecordsTab() if (!ImGui::BeginChild("##scrollable")) ImGui::EndChild(); - for (auto& group : m_cachedRecords) + for (auto it = m_cachedRecords.begin(); it != m_cachedRecords.end(); ++it) { + auto& group = it.value(); + if (group.m_isFiltered || !group.m_visibilityChecker.IsVisible()) continue; @@ -1852,7 +1456,7 @@ void TweakDBEditor::DrawQueriesTab() { auto* pTDB = RED4ext::TweakDB::Get(); - std::shared_lock _(pTDB->mutex01); + std::shared_lock _(pTDB->mutex01); pTDB->queries.ForEach([this](const RED4ext::TweakDBID& queryID, RED4ext::DynArray& recordIDs) { const auto queryName = GetTweakDBIDStringQuery(queryID.value); @@ -1884,7 +1488,7 @@ void TweakDBEditor::DrawFlatsTab() searchTimer = -1.0f; } - if (searchTimer) + if (searchTimer != 0.0f) { searchTimer -= ImGui::GetIO().DeltaTime; if (searchTimer <= 0.0f) @@ -1897,8 +1501,10 @@ void TweakDBEditor::DrawFlatsTab() if (!ImGui::BeginChild("##scrollable")) ImGui::EndChild(); - for (CachedFlatGroup& group : m_cachedFlatGroups) + for (auto it = m_cachedFlatGroups.begin(); it != m_cachedFlatGroups.end(); ++it) { + auto& group = it.value(); + if (group.m_isFiltered || !group.m_visibilityChecker.IsVisible()) continue; @@ -1958,73 +1564,13 @@ void TweakDBEditor::DrawFlatsTab() void TweakDBEditor::DrawAdvancedTab() { - if (CDPRTweakDBMetadata::Get()->IsInitialized()) - { - ImGui::PushStyleColor(ImGuiCol_Text, 0xFF00FF00); - ImGui::TextUnformatted("'tweakdb.str' is loaded!"); - ImGui::PopStyleColor(); - } - else - { - ImGui::PushStyleColor(ImGuiCol_Text, 0xFF0000FF); - ImGui::TextUnformatted("'tweakdb.str' is not loaded."); - ImGui::PopStyleColor(); - ImGui::TreePush(); - ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32_BLACK_TRANS); - ImGui::PushStyleColor(ImGuiCol_Text, 0xFFF66409); - char pLink[] = "https://www.cyberpunk.net/en/modding-support"; - ImGui::InputText("##cdprLink", pLink, sizeof(pLink) - 1, ImGuiInputTextFlags_ReadOnly); - ImGui::PopStyleColor(2); - ImGui::TextUnformatted("1) Download and unpack 'Metadata'"); - ImGui::TextUnformatted("2) Copy 'tweakdb.str' to 'plugins\\cyber_engine_tweaks\\tweakdb.str'"); - std::string cetDir = CET::Get().GetPaths().CETRoot().string(); - ImGui::Text("Full path: %s", cetDir.c_str()); - if (ImGui::Button("3) Load tweakdb.str")) - { - if (CDPRTweakDBMetadata::Get()->Initialize()) - { - RefreshAll(); - FilterAll(); - } - } - ImGui::TreePop(); - } - - if (ResourcesList::Get()->IsInitialized()) - { - ImGui::PushStyleColor(ImGuiCol_Text, 0xFF00FF00); - ImGui::TextUnformatted("'usedhashes.kark' is loaded!"); - ImGui::PopStyleColor(); - } - else - { - ImGui::PushStyleColor(ImGuiCol_Text, 0xFF0000FF); - ImGui::TextUnformatted("'usedhashes.kark' is not loaded."); - ImGui::PopStyleColor(); - ImGui::TreePush(); - ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32_BLACK_TRANS); - ImGui::PushStyleColor(ImGuiCol_Text, 0xFFF66409); - char pLink[] = "https://github.com/WolvenKit/WolvenKit/blob/main/WolvenKit.Common/Resources/usedhashes.kark"; - ImGui::InputText("##wkitLink", pLink, sizeof(pLink) - 1, ImGuiInputTextFlags_ReadOnly); - ImGui::PopStyleColor(2); - ImGui::TextUnformatted("1) Download and put 'usedhashes.kark' in 'plugins\\cyber_engine_tweaks\\'"); - std::string cetDir = CET::Get().GetPaths().CETRoot().string(); - ImGui::Text("Full path: %s", cetDir.c_str()); - if (ImGui::Button("3) Load usedhashes.kark")) - { - ResourcesList::Get()->Initialize(); - } - ImGui::TreePop(); - } - - ImGui::SetNextItemWidth(50); if (ImGui::InputScalar("'Flats' Grouping depth", ImGuiDataType_S8, &m_flatGroupNameDepth, nullptr, nullptr, nullptr, ImGuiInputTextFlags_EnterReturnsTrue)) { RefreshFlats(); FilterFlats(); } - ImGui::SetNextItemWidth(100); + ImGui::InputFloat("ComboBox dropdown height", &g_comboDropdownHeight, 0, 0); if (ImGui::Button("Refresh all")) @@ -2035,7 +1581,7 @@ void TweakDBEditor::DrawAdvancedTab() if (ImGui::BeginChild("CreateRecord", ImVec2(0.0f, 170.0f), true)) { - static const char* status = ""; + static auto status = ""; static float statusTimer = 0.0f; static char recordName[256]{}; static char comboSearchBuffer[70]{}; @@ -2063,7 +1609,7 @@ void TweakDBEditor::DrawAdvancedTab() ImGui::InputTextWithHint("##dropdownSearch", "Search", comboSearchBuffer, sizeof(comboSearchBuffer)); if (ImGui::BeginChild("##dropdownScroll", ImVec2(0, g_comboDropdownHeight))) { - for (CachedRecordGroup& recordGroup : m_cachedRecords) + for (const auto& recordGroup : m_cachedRecords | std::views::values) { if (comboSearchBuffer[0] != '\0' && !StringContains(recordGroup.m_name, comboSearchBuffer)) @@ -2071,7 +1617,7 @@ void TweakDBEditor::DrawAdvancedTab() continue; } - bool isSelected = recordTypeName == recordGroup.m_typeName; + const bool isSelected = recordTypeName == recordGroup.m_typeName; if (ImGui::NextItemVisible(ImVec2(1.0f, ImGui::GetTextLineHeight())) && ImGui::Selectable(recordGroup.m_name.c_str(), isSelected)) { @@ -2107,7 +1653,7 @@ void TweakDBEditor::DrawAdvancedTab() SetStatus("Failed. check console!"); } - if (statusTimer) + if (statusTimer != 0.0f) { statusTimer -= ImGui::GetIO().DeltaTime; if (statusTimer <= 0.0f) @@ -2133,7 +1679,7 @@ void TweakDBEditor::CachedFlat::Update(int32_t aTDBOffset) if (aTDBOffset == -1) { - std::shared_lock _(pTDB->mutex00); + std::shared_lock _(pTDB->mutex00); const auto it = pTDB->flats.Find(m_dbid); if (it != pTDB->flats.end()) { @@ -2162,7 +1708,7 @@ void TweakDBEditor::CachedFlatGroup::Initialize() return; // order flats inside group - std::sort(m_flats.begin(), m_flats.end(), [](const CachedFlat& aLeft, const CachedFlat& aRight) + std::ranges::sort(m_flats, [](const CachedFlat& aLeft, const CachedFlat& aRight) { return SortTweakDBIDString(aLeft.m_name, aRight.m_name); }); @@ -2183,7 +1729,7 @@ void TweakDBEditor::CachedRecord::Initialize() InitializeFlats(); - std::sort(m_flats.begin(), m_flats.end(), [](const CachedFlat& aLeft, const CachedFlat& aRight) + std::ranges::sort(m_flats, [](const CachedFlat& aLeft, const CachedFlat& aRight) { return SortTweakDBIDString(aLeft.m_name, aRight.m_name); }); @@ -2193,7 +1739,7 @@ void TweakDBEditor::CachedRecord::Initialize() void TweakDBEditor::CachedRecord::InitializeFlats() { - if (m_flats.size() != 0) + if (!m_flats.empty()) return; TiltedPhoques::Vector recordFlats; @@ -2203,7 +1749,7 @@ void TweakDBEditor::CachedRecord::InitializeFlats() m_flats.reserve(recordFlats.size()); for (uint64_t flatID : recordFlats) { - m_flats.emplace_back(TweakDBEditor::GetTweakDBIDStringFlat(flatID), flatID); + m_flats.emplace_back(GetTweakDBIDStringFlat(flatID), flatID); } } } @@ -2215,8 +1761,8 @@ void TweakDBEditor::CachedRecord::Update() const } TweakDBEditor::CachedRecordGroup::CachedRecordGroup(RED4ext::CName aTypeName) - : m_typeName(aTypeName) - , m_name(aTypeName.ToString()) + : m_name(aTypeName.ToString()) + , m_typeName(aTypeName) { } @@ -2225,7 +1771,7 @@ void TweakDBEditor::CachedRecordGroup::Initialize() if (m_isInitialized) return; - std::sort(m_records.begin(), m_records.end(), [](const CachedRecord& aLeft, const CachedRecord& aRight) + std::ranges::sort(m_records, [](const CachedRecord& aLeft, const CachedRecord& aRight) { return SortTweakDBIDString(aLeft.m_name, aRight.m_name); }); @@ -2245,7 +1791,7 @@ void TweakDBEditor::ImGuiVisibilityChecker::Begin() void TweakDBEditor::ImGuiVisibilityChecker::End() { - float endCursorY = ImGui::GetCursorPosY(); + const float endCursorY = ImGui::GetCursorPosY(); if (endCursorY < m_beginCursorY) { m_itemSize = ImVec2(0.0f, 0.0f); diff --git a/src/overlay/widgets/TweakDBEditor.h b/src/overlay/widgets/TweakDBEditor.h index 523ff7ad..b5924931 100644 --- a/src/overlay/widgets/TweakDBEditor.h +++ b/src/overlay/widgets/TweakDBEditor.h @@ -3,17 +3,14 @@ #include "Widget.h" struct LuaVM; - struct TweakDBEditor : Widget { TweakDBEditor(LuaVM& aVm); ~TweakDBEditor() override = default; - bool OnEnable() override; - bool OnDisable() override; - void Update() override; - protected: + void OnUpdate() override; + void RefreshAll(); void RefreshRecords(); void RefreshFlats(); @@ -62,10 +59,10 @@ struct TweakDBEditor : Widget void End(); private: - ImVec2 m_itemSize; - float m_beginCursorY; + ImVec2 m_itemSize{ 0.0f, 0.0f }; + float m_beginCursorY{ 0.0f }; }; - + struct CachedFlat { bool m_isFiltered = false; @@ -130,8 +127,8 @@ struct TweakDBEditor : Widget LuaVM& m_vm; bool m_initialized = false; int32_t m_flatGroupNameDepth = 1; - TiltedPhoques::Vector m_cachedFlatGroups; - TiltedPhoques::Vector m_cachedRecords; + TiltedPhoques::Map m_cachedFlatGroups; + TiltedPhoques::Map m_cachedRecords; static bool s_recordsFilterIsRegex; static bool s_flatsFilterIsRegex; static char s_recordsFilterBuffer[256]; diff --git a/src/overlay/widgets/Widget.cpp b/src/overlay/widgets/Widget.cpp new file mode 100644 index 00000000..513f1aca --- /dev/null +++ b/src/overlay/widgets/Widget.cpp @@ -0,0 +1,109 @@ +#include + +#include "Widget.h" + +#include + +Widget::Widget(const std::string& acpName, bool aOwnerDraw) + : m_name(acpName) + , m_ownerDraw(aOwnerDraw) +{ +} + +WidgetResult Widget::OnEnable() +{ + m_enabled = true; + return WidgetResult::ENABLED; +} + +WidgetResult Widget::OnDisable() +{ + m_enabled = false; + return WidgetResult::DISABLED; +} + +bool Widget::IsEnabled() const +{ + return m_enabled; +} + +void Widget::Toggle() +{ + m_toggle = true; +} + +WidgetResult Widget::OnPopup() +{ + return m_enabled ? WidgetResult::DISABLED : WidgetResult::ENABLED; +} + +void Widget::OnToggle() +{ + if (m_enabled) + { + const auto ret = OnDisable(); + if (ret == WidgetResult::CANCEL) + { + m_toggle = false; + } + if (ret == WidgetResult::DISABLED) + { + m_enabled = false; + m_toggle = false; + } + } + else + { + const auto ret = OnEnable(); + if (ret == WidgetResult::CANCEL) + { + m_toggle = false; + } + if (ret == WidgetResult::ENABLED) + { + m_enabled = true; + m_toggle = false; + } + } +} + +void Widget::Draw() +{ + if (m_drawPopup) + { + CET::Get().GetVM().BlockDraw(true); + m_drawPopup = OnPopup() == WidgetResult::ENABLED; + if (!m_drawPopup) + { + CET::Get().GetVM().BlockDraw(false); + ImGui::CloseCurrentPopup(); + } + } + + if (m_toggle) + OnToggle(); + + if (!m_enabled) + return; + + bool newEnabled = m_enabled; + + if (m_ownerDraw) + OnUpdate(); + else + { + const auto [width, height] = CET::Get().GetD3D12().GetResolution(); + ImGui::SetNextWindowPos(ImVec2(width * 0.2f, height * 0.2f), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(width * 0.6f, height * 0.6f), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSizeConstraints(ImVec2(420, 315), ImVec2(FLT_MAX, FLT_MAX)); + if (ImGui::Begin(m_name.c_str(), &newEnabled)) + OnUpdate(); + ImGui::End(); + } + + if (!newEnabled) + { + Toggle(); + OnToggle(); + } +} diff --git a/src/overlay/widgets/Widget.h b/src/overlay/widgets/Widget.h index d1264230..809d51b9 100644 --- a/src/overlay/widgets/Widget.h +++ b/src/overlay/widgets/Widget.h @@ -1,19 +1,44 @@ #pragma once -enum class WidgetID +enum class WidgetResult { - CONSOLE, - BINDINGS, - SETTINGS, - TWEAKDB, - COUNT + DISABLED, + ENABLED, + CANCEL }; struct Widget { + Widget(const std::string& acpName, bool aOwnerDraw = false); virtual ~Widget() = default; - virtual bool OnEnable() = 0; - virtual bool OnDisable() = 0; - virtual void Update() = 0; + virtual WidgetResult OnEnable(); + virtual WidgetResult OnDisable(); + + bool IsEnabled() const; + void Toggle(); + + void Draw(); + +protected: + virtual void OnUpdate() = 0; + + virtual WidgetResult OnPopup(); + virtual void OnToggle(); + + std::string m_name; + bool m_ownerDraw{ false }; + bool m_enabled{ false }; + bool m_toggle{ false }; + bool m_drawPopup{ false }; +}; + +using TWidgetCB = std::function; + +enum class TChangedCBResult +{ + CHANGED, + APPLY, + DISCARD, + CANCEL }; diff --git a/src/patches/DisableBoundaries.cpp b/src/patches/DisableBoundaries.cpp index e0504baa..069ea6e2 100644 --- a/src/patches/DisableBoundaries.cpp +++ b/src/patches/DisableBoundaries.cpp @@ -1,12 +1,10 @@ #include -#include "Image.h" - -void DisableBoundaryTeleportPatch(const Image* apImage) +void DisableBoundaryTeleportPatch() { // Disarm the WorldBoundarySystem/Tick function // Going out of bounds will still play the glitchy-screen effect that normally happens when game teleports you, but the actual teleport won't happen - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_BoundaryTeleport); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_BoundaryTeleport); const auto pLocation = func.GetAddr(); if (pLocation == nullptr) diff --git a/src/patches/DisableIntroMovies.cpp b/src/patches/DisableIntroMovies.cpp index eb352253..92a8f559 100644 --- a/src/patches/DisableIntroMovies.cpp +++ b/src/patches/DisableIntroMovies.cpp @@ -1,7 +1,5 @@ #include -#include "Image.h" - using TInitScriptMemberVariable = void*(void* a1, void* a2, uint64_t a3, uint64_t nameHash, void* a5, void* a6, void* a7); TInitScriptMemberVariable* RealInitScriptMemberVariable = nullptr; @@ -15,12 +13,12 @@ void* HookInitScriptMemberVariable(void* a1, void* a2, uint64_t a3, uint64_t nam // Ideally I think the real solution is to change GameFramework/InitialState INI variable from "Initialization" to "PreGameSession" or "MainMenu" instead // Unfortunately that causes a black screen on launch though, likely only works properly on non-shipping builds - if (nameHash == RED4ext::FNV1a("logoTrainWBBink") || - nameHash == RED4ext::FNV1a("logoTrainNamcoBink") || - nameHash == RED4ext::FNV1a("logoTrainStadiaBink") || - nameHash == RED4ext::FNV1a("logoTrainNoRTXBink") || - nameHash == RED4ext::FNV1a("logoTrainRTXBink") || - nameHash == RED4ext::FNV1a("introMessageBink")) + if (nameHash == RED4ext::FNV1a64("logoTrainWBBink") || + nameHash == RED4ext::FNV1a64("logoTrainNamcoBink") || + nameHash == RED4ext::FNV1a64("logoTrainStadiaBink") || + nameHash == RED4ext::FNV1a64("logoTrainNoRTXBink") || + nameHash == RED4ext::FNV1a64("logoTrainRTXBink") || + nameHash == RED4ext::FNV1a64("introMessageBink")) { nameHash = ~nameHash; } @@ -28,10 +26,10 @@ void* HookInitScriptMemberVariable(void* a1, void* a2, uint64_t a3, uint64_t nam return RealInitScriptMemberVariable(a1, a2, a3, nameHash, a5, a6, a7); } -void DisableIntroMoviesPatch(const Image* apImage) +void DisableIntroMoviesPatch() { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_IntroMovie); - RealInitScriptMemberVariable = static_cast(func.GetAddr()); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_IntroMovie); + RealInitScriptMemberVariable = reinterpret_cast(func.GetAddr()); if (RealInitScriptMemberVariable == nullptr) { @@ -39,6 +37,8 @@ void DisableIntroMoviesPatch(const Image* apImage) return; } - MH_CreateHook(RealInitScriptMemberVariable, &HookInitScriptMemberVariable, reinterpret_cast(&RealInitScriptMemberVariable)); + MH_CreateHook(reinterpret_cast(RealInitScriptMemberVariable), + reinterpret_cast(&HookInitScriptMemberVariable), + reinterpret_cast(&RealInitScriptMemberVariable)); Log::Info("Disable intro movies patch: success"); } diff --git a/src/patches/DisableVignette.cpp b/src/patches/DisableVignette.cpp index b4e111f4..7e815059 100644 --- a/src/patches/DisableVignette.cpp +++ b/src/patches/DisableVignette.cpp @@ -1,10 +1,8 @@ #include -#include "Image.h" - -void DisableVignettePatch(const Image* apImage) +void DisableVignettePatch() { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_Vignette); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_Vignette); const auto pLocation = func.GetAddr(); if (pLocation == nullptr) diff --git a/src/patches/EnableDebug.cpp b/src/patches/EnableDebug.cpp index f03dd5c0..83c8aa50 100644 --- a/src/patches/EnableDebug.cpp +++ b/src/patches/EnableDebug.cpp @@ -1,10 +1,8 @@ #include -#include "Image.h" - using ScriptExecutionPointer = uint64_t; -void HookIsFinal(void* a, ScriptExecutionPointer* apExecutionPointer, uint8_t* apReturnValue, void* d) +void HookIsFinal(void*, ScriptExecutionPointer* apExecutionPointer, uint8_t* apReturnValue, void*) { (*apExecutionPointer)++; @@ -12,7 +10,7 @@ void HookIsFinal(void* a, ScriptExecutionPointer* apExecutionPointer, uint8_t* a *apReturnValue = 0; } -void HookIsDebug(void* a, ScriptExecutionPointer* apExecutionPointer, uint8_t* apReturnValue, void* d) +void HookIsDebug(void*, ScriptExecutionPointer* apExecutionPointer, uint8_t* apReturnValue, void*) { (*apExecutionPointer)++; @@ -30,13 +28,13 @@ void HookRegisterScriptFunction(void* a, uint64_t hash, uint64_t hash2, void* fu { // IsFinal (global) // if false shows debug menu option on main menu & pause menu - if (hash == RED4ext::FNV1a("IsFinal")) - func = &HookIsFinal; + if (hash == RED4ext::FNV1a64("IsFinal")) + func = reinterpret_cast(&HookIsFinal); // AreDebugContextsEnabled (global) // unknown effect - else if (hash == RED4ext::FNV1a("AreDebugContextsEnabled")) - func = &HookIsDebug; + else if (hash == RED4ext::FNV1a64("AreDebugContextsEnabled")) + func = reinterpret_cast(&HookIsDebug); RealRegisterScriptFunction(a, hash, hash2, func); } @@ -46,28 +44,30 @@ void HookRegisterScriptMemberFunction(void* a, void* parentClass, uint64_t hash, // WorldMapMenuGameController::CanDebugTeleport // allows using world_map_menu_debug_teleport binding on map screen to teleport // (must be set inside r6\config\inputUserMappings.xml first) - if (hash == RED4ext::FNV1a("CanDebugTeleport")) - func = &HookIsDebug; + if (hash == RED4ext::FNV1a64("CanDebugTeleport")) + func = reinterpret_cast(&HookIsDebug); // TargetShootComponent::IsDebugEnabled // unknown effect - else if (hash == RED4ext::FNV1a("IsDebugEnabled")) - func = &HookIsDebug; + else if (hash == RED4ext::FNV1a64("IsDebugEnabled")) + func = reinterpret_cast(&HookIsDebug); RealRegisterScriptMemberFunction(a, parentClass, hash, hash2, func, flag); } -void EnableDebugPatch(const Image* apImage) +void EnableDebugPatch() { - RED4ext::RelocPtr registerFunction(RED4ext::Addresses::CBaseFunction_Register); - RED4ext::RelocPtr registerMemberFunction(CyberEngineTweaks::Addresses::CScript_RegisterMemberFunction); + const RED4ext::RelocPtr registerFunction(RED4ext::Addresses::CBaseFunction_Register); + const RED4ext::RelocPtr registerMemberFunction(CyberEngineTweaks::Addresses::CScript_RegisterMemberFunction); RealRegisterScriptFunction = reinterpret_cast(registerFunction.GetAddr()); - MH_CreateHook(RealRegisterScriptFunction, &HookRegisterScriptFunction, + MH_CreateHook(reinterpret_cast(RealRegisterScriptFunction), + reinterpret_cast(&HookRegisterScriptFunction), reinterpret_cast(&RealRegisterScriptFunction)); RealRegisterScriptMemberFunction = reinterpret_cast(registerMemberFunction.GetAddr()); - MH_CreateHook(RealRegisterScriptMemberFunction, &HookRegisterScriptMemberFunction, + MH_CreateHook(reinterpret_cast(RealRegisterScriptMemberFunction), + reinterpret_cast(&HookRegisterScriptMemberFunction), reinterpret_cast(&RealRegisterScriptMemberFunction)); } diff --git a/src/patches/MinimapFlicker.cpp b/src/patches/MinimapFlicker.cpp index 800eef50..a1679436 100644 --- a/src/patches/MinimapFlicker.cpp +++ b/src/patches/MinimapFlicker.cpp @@ -1,10 +1,8 @@ #include -#include "Image.h" - -void MinimapFlickerPatch(const Image* apImage) +void MinimapFlickerPatch() { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_MinimapFlicker); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_MinimapFlicker); uint8_t* pLocation = func.GetAddr(); if (pLocation == nullptr) diff --git a/src/patches/OptionsPatch.cpp b/src/patches/OptionsPatch.cpp index ef73221d..aeacd33d 100644 --- a/src/patches/OptionsPatch.cpp +++ b/src/patches/OptionsPatch.cpp @@ -1,10 +1,7 @@ -#include "CET.h" - #include -#include "Image.h" -#include "Options.h" -#include "scripting/GameOptions.h" +#include +#include using TGameOptionInit = void*(void*); TGameOptionInit* RealGameOptionInit = nullptr; @@ -14,9 +11,9 @@ void* HookGameOptionInit(GameOption* apThis) auto& gameOptions = GameOptions::GetList(); auto& options = CET::Get().GetOptions(); - if (std::find(gameOptions.begin(), gameOptions.end(), apThis) == gameOptions.end()) + if (std::ranges::find(gameOptions, apThis) == gameOptions.end()) { - gameOptions.push_back(apThis); + gameOptions.emplace_back(apThis); } if (options.DumpGameOptions) @@ -40,14 +37,16 @@ void* HookGameOptionInit(GameOption* apThis) return RealGameOptionInit(apThis); } -void OptionsInitHook(const Image* apImage) +void OptionsInitHook() { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_OptionsInit); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_OptionsInit); uint8_t* pLocation = func.GetAddr(); if (pLocation) { - MH_CreateHook(pLocation, &HookGameOptionInit, reinterpret_cast(&RealGameOptionInit)); + MH_CreateHook(pLocation, + reinterpret_cast(&HookGameOptionInit), + reinterpret_cast(&RealGameOptionInit)); MH_EnableHook(pLocation); Log::Info("Hidden options hook: success"); diff --git a/src/patches/RemovePeds.cpp b/src/patches/RemovePeds.cpp index 26f2a673..49a4f239 100644 --- a/src/patches/RemovePeds.cpp +++ b/src/patches/RemovePeds.cpp @@ -1,8 +1,6 @@ #include -#include "Image.h" - -void RemovePedsPatch(const Image* apImage) +void RemovePedsPatch() { /* RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_); uint8_t* pLocation = func.GetAddr(); diff --git a/src/patches/SkipStartScreen.cpp b/src/patches/SkipStartScreen.cpp index b9ff4e04..9a5add74 100644 --- a/src/patches/SkipStartScreen.cpp +++ b/src/patches/SkipStartScreen.cpp @@ -1,10 +1,8 @@ #include -#include "Image.h" - -void StartScreenPatch(const Image* apImage) +void StartScreenPatch() { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_SkipStartScreen); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_SkipStartScreen); uint8_t* pLocation = func.GetAddr(); if (pLocation == nullptr) diff --git a/src/patches/Smt.cpp b/src/patches/Smt.cpp index cbe6b965..47cb10a9 100644 --- a/src/patches/Smt.cpp +++ b/src/patches/Smt.cpp @@ -1,10 +1,8 @@ #include -#include "Image.h" - -void SmtAmdPatch(const Image* apImage) +void SmtAmdPatch() { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_AmdSMT); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CPatches_AmdSMT); uint8_t* pLocation = func.GetAddr(); if (pLocation == nullptr) diff --git a/src/reverse/Addresses.h b/src/reverse/Addresses.h index 6d154603..f0709ebd 100644 --- a/src/reverse/Addresses.h +++ b/src/reverse/Addresses.h @@ -32,13 +32,14 @@ constexpr uintptr_t CPhotoMode_SetRecordID = 0x142DBB7E0 - ImageBase; // 48 8B C #pragma endregion #pragma region CRenderGlobal -constexpr uintptr_t CRenderGlobal_InstanceOffset = 0x144DD6FB0 - ImageBase; // 49 8B 95 ? ? ? ? 48 8D 44 24 30 0F 57 C0, expected: 1, index: 0, offset: 3 +constexpr uintptr_t CRenderGlobal_InstanceOffset = 0x144DD6FB0 - ImageBase; // 48 89 5C 24 08 48 89 6C 24 18 48 89 74 24 20 57 41 56 41 57 48 83 EC 30 8B 01 41 8B F8 4C 8B 35, expected: 1, index: 0, offset: 32 constexpr uintptr_t CRenderGlobal__DoNotUse_RenderQueueOffset = 0x151F68149 - ImageBase; // 49 39 29 0F 84 ? ? ? ? 41 39 69 24 0F 84 ? ? ? ? 49 8B 95, expected: 1, index: 0, offset: 0 constexpr uintptr_t CRenderGlobal_Resize = 0x142CDCA30 - ImageBase; // 44 88 4C 24 20 44 89 44 24 18 89 54 24 10 89 4C, expected: 1, index: 0 +constexpr uintptr_t CRenderGlobal_Shutdown = 0x142CC71A0 - ImageBase; // 48 89 6C 24 20 41 56 48 83 EC 20 48 8D 05 3E 85 99 00, expected: 1, index: 0 #pragma endregion #pragma region CRenderNode_Present -constexpr uintptr_t CRenderNode_Present_DoInternal = 0x142CDEFC0 - ImageBase; // 48 89 5C 24 08 48 89 6C 24 18 48 89 74 24 20 57 41 56 41 57 48 83 EC 30 8B 01 41 8B F8 4C 8B 35, expected: 1, index: 0 +constexpr uintptr_t CRenderNode_Present_DoInternal = 0x142CDEFC0 - ImageBase; // 48 89 5C 24 08 48 89 6C 24 18 48 89 74 24 20 57 41 56 41 57 48 83 EC 30 8B 01 41 8B F8 4C 8B 35, expected: 1, index: 0 #pragma endregion #pragma region CScript @@ -49,6 +50,7 @@ constexpr uintptr_t CScript_ToStringDEBUG = 0x140BD4D30 - ImageBase; // 48 89 5C constexpr uintptr_t CScript_LogChannel = 0x1401ED730 - ImageBase; // 4C 8B DC 49 89 5B 08 49 89 73 18 57 48 83 EC 70 48 8B 02 ? ? ? ? ? ? ? FE 42 62 4D 8D 43 10 33 FF 45 33 C9 49 89 7B 10 48 8B DA 48 89 7A, expected: 1, index: 0 constexpr uintptr_t CScript_TDBIDConstructorDerive = 0x142BE62D0 - ImageBase; // 40 53 48 83 EC 30 33 C0 4C 89 44 24 20 48 8B DA, expected: 1, index: 0 constexpr uintptr_t CScript_ProcessRunningState = 0x140A72C10 - ImageBase; // 40 53 48 83 EC 20 48 8B 0D ? ? ? ? 48 8B DA E8 ? ? ? ? 84 C0, expected: 1, index: 0 +constexpr uintptr_t CScript_ProcessShutdownState = 0x140A72D20 - ImageBase; // 48 89 6C 24 18 56 48 83 EC 30 48 8B 0D ? ? ? ?, expected: 1, index: 0 constexpr uintptr_t CScript_TranslateBytecode = 0x14027AB20 - ImageBase; // 4C 8B DC 55 53 57 41 55 49 8D 6B A1 48 81 EC 98 00 00 00 48 8B 1A 4C 8B E9 8B 42 0C 48 8D 3C C3, expected: 1, index: 0 constexpr uintptr_t CScript_TweakDBLoad = 0x140BD3060 - ImageBase; // 48 89 5C 24 18 55 57 41 56 48 8B EC 48 83 EC 70 48 8B D9 45 33 F6 48 8D, expected: 1, index: 0 constexpr uintptr_t CScript_RegisterMemberFunction = 0x14020A700 - ImageBase; // 48 89 5C 24 08 57 48 83 EC 20 49 8B C1 4D 8B D0 44 8B 4C 24 58 48 8B DA 41 83 C9 03, expected: 1, index: 0 @@ -66,4 +68,8 @@ constexpr uintptr_t gameIGameSystem_Spawn = 0x142DBE400 - ImageBase; // 48 89 5C constexpr uintptr_t gameIGameSystem_Despawn = 0x142DBBEA0 - ImageBase; // 48 89 5C 24 10 48 89 6C 24 18 56 57 41 54 41 56 41 57 48 83 EC 50 4C 8B F9 0F 57 C0 48 83 C1 41, expected: 1, index: 0 constexpr uintptr_t gameIGameSystem_SpawnCallback = 0x1410F1780 - ImageBase; // 48 89 5C 24 18 48 89 6C 24 20 56 57 41 56 48 83 EC 70 48 8B F1 48 8B EA 48 83 C1 48 E8, expected: 1, index: 0 #pragma endregion + +#pragma region PlayerSystem +constexpr uintptr_t PlayerSystem_OnPlayerSpawned = 0x14271CBD0 - ImageBase; // 48 8B C4 4C 89 48 20 55 56 57 48 8B EC 48 81 EC 80 00 00 00 44 8B 15 25 60 54 02 48 8B F1, expected: 1, index: 0 +#pragma endregion } // namespace CyberEngineTweaks::Addresses diff --git a/src/reverse/BasicTypes.cpp b/src/reverse/BasicTypes.cpp index 01b9361f..38effda0 100644 --- a/src/reverse/BasicTypes.cpp +++ b/src/reverse/BasicTypes.cpp @@ -2,12 +2,13 @@ #include "BasicTypes.h" -#include +#include +#include std::string Vector3::ToString() const noexcept { - return fmt::format("ToVector3{{ x = {0}, y = {1}, z = {2} }}", x, y, z); + return fmt::format("ToVector3{{ x = {}, y = {}, z = {} }}", x, y, z); } bool Vector3::operator==(const Vector3& acRhs) const noexcept @@ -17,7 +18,7 @@ bool Vector3::operator==(const Vector3& acRhs) const noexcept std::string Vector4::ToString() const noexcept { - return fmt::format("ToVector4{{ x = {0}, y = {1}, z = {2}, w = {3} }}", x, y, z, w); + return fmt::format("ToVector4{{ x = {}, y = {}, z = {}, w = {} }}", x, y, z, w); } bool Vector4::operator==(const Vector4& acRhs) const noexcept @@ -27,7 +28,7 @@ bool Vector4::operator==(const Vector4& acRhs) const noexcept std::string EulerAngles::ToString() const noexcept { - return fmt::format("ToEulerAngles{{ roll = {0}, pitch = {1}, yaw = {2} }}", roll, pitch, yaw); + return fmt::format("ToEulerAngles{{ roll = {}, pitch = {}, yaw = {} }}", roll, pitch, yaw); } bool EulerAngles::operator==(const EulerAngles& acRhs) const noexcept @@ -37,7 +38,7 @@ bool EulerAngles::operator==(const EulerAngles& acRhs) const noexcept std::string Quaternion::ToString() const noexcept { - return fmt::format("ToQuaternion{{ i = {0}, j = {1}, k = {2}, r = {3} }}", i, j, k, r); + return fmt::format("ToQuaternion{{ i = {}, j = {}, k = {}, r = {} }}", i, j, k, r); } bool Quaternion::operator==(const Quaternion& acRhs) const noexcept @@ -52,12 +53,12 @@ std::string CName::AsString() const noexcept std::string CName::ToString() const noexcept { - RED4ext::CName internal(hash); + const RED4ext::CName internal(hash); const auto resolved = internal.ToString(); if (!resolved) - return fmt::format("ToCName{{ hash_lo = 0x{0:08X}, hash_hi = 0x{1:08X} }}", hash_lo, hash_hi); - return fmt::format("ToCName{{ hash_lo = 0x{0:08X}, hash_hi = 0x{1:08X} --[[ {2} --]] }}", hash_lo, hash_hi, resolved); + return fmt::format("ToCName{{ hash_lo = 0x{:08X}, hash_hi = 0x{:08X} }}", hash_lo, hash_hi); + return fmt::format("ToCName{{ hash_lo = 0x{:08X}, hash_hi = 0x{:08X} --[[ {} --]] }}", hash_lo, hash_hi, resolved); } bool CName::operator==(const CName& acRhs) const noexcept @@ -70,9 +71,17 @@ void CName::Add(const std::string& aName) RED4ext::CNamePool::Add(aName.c_str()); } +std::string TweakDBID::AsString() const noexcept +{ + return CET::Get().GetVM().GetTDBIDString(value); +} + std::string TweakDBID::ToString() const noexcept { - return fmt::format("ToTweakDBID{{ hash = 0x{0:08X}, length = {1:d} }}", name_hash, name_length); + const auto resolved = CET::Get().GetVM().GetTDBIDString(value, true); + if (!resolved.empty()) + return fmt::format("ToTweakDBID{{ hash = 0x{:08X}, length = {:d} }}", name_hash, name_length); + return fmt::format("ToTweakDBID{{ hash = 0x{:08X}, length = {:d} --[[ {} --]] }}", name_hash, name_length, resolved); } bool TweakDBID::operator==(const TweakDBID& acRhs) const noexcept @@ -82,12 +91,12 @@ bool TweakDBID::operator==(const TweakDBID& acRhs) const noexcept TweakDBID TweakDBID::operator+(const std::string_view acName) const noexcept { - return TweakDBID(*this, acName); + return {*this, acName}; } std::string ItemID::ToString() const noexcept { - return fmt::format("ToItemID{{ id = {0}, rng_seed = {1}, unknown = {2}, maybe_type = {3} }}", id.ToString(), rng_seed, unknown, maybe_type); + return fmt::format("ToItemID{{ id = {}, rng_seed = {}, unknown = {}, maybe_type = {} }}", id.ToString(), rng_seed, unknown, maybe_type); } bool ItemID::operator==(const ItemID& acRhs) const noexcept @@ -150,7 +159,7 @@ RED4ext::CBaseRTTIType* Variant::GetType() const noexcept RED4ext::ScriptInstance Variant::GetDataPtr() const noexcept { - return IsInlined() ? const_cast(inlined) : instance; + return IsInlined() ? inlined : instance; } bool Variant::Init(const RED4ext::CBaseRTTIType* aType) @@ -161,7 +170,7 @@ bool Variant::Init(const RED4ext::CBaseRTTIType* aType) return false; } - RED4ext::CBaseRTTIType* ownType = GetType(); + const RED4ext::CBaseRTTIType* ownType = GetType(); RED4ext::ScriptInstance ownData = GetDataPtr(); if (ownType) @@ -182,7 +191,7 @@ bool Variant::Init(const RED4ext::CBaseRTTIType* aType) if (CanBeInlined(aType)) { - reinterpret_cast(type) |= kInlineFlag; + type = reinterpret_cast(reinterpret_cast(type) | kInlineFlag); ownData = inlined; } else @@ -209,11 +218,11 @@ bool Variant::Fill(const RED4ext::CBaseRTTIType* aType, const RED4ext::ScriptIns return true; } -bool Variant::Extract(RED4ext::ScriptInstance aBuffer) +bool Variant::Extract(RED4ext::ScriptInstance aBuffer) const { if (IsEmpty()) return false; - + GetType()->Assign(aBuffer, GetDataPtr()); return true; @@ -223,9 +232,9 @@ void Variant::Free() { if (IsEmpty()) return; - - RED4ext::CBaseRTTIType* ownType = GetType(); - RED4ext::ScriptInstance ownData = GetDataPtr(); + + const RED4ext::CBaseRTTIType* ownType = GetType(); + const RED4ext::ScriptInstance ownData = GetDataPtr(); if (ownData) { @@ -246,7 +255,7 @@ bool Variant::CanBeInlined(const RED4ext::CBaseRTTIType* aType) noexcept std::string CRUID::ToString() const noexcept { - return fmt::format("CRUID({0}ull)", hash); + return fmt::format("CRUID({}ull)", hash); } bool CRUID::operator==(const CRUID& acRhs) const noexcept @@ -256,68 +265,10 @@ bool CRUID::operator==(const CRUID& acRhs) const noexcept std::string gamedataLocKeyWrapper::ToString() const noexcept { - return fmt::format("LocKey({0}ull)", hash); + return fmt::format("LocKey({}ull)", hash); } bool gamedataLocKeyWrapper::operator==(const gamedataLocKeyWrapper& acRhs) const noexcept { return hash == acRhs.hash; } - -static const unsigned int crc32_table[] = -{ - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d -}; - -uint32_t crc32(const char* buf, size_t len, uint32_t seed) -{ - uint32_t crc = ~seed; - auto q = buf + len; - for (auto p = buf; p < q; p++) { - auto octet = *p; - crc = (crc >> 8) ^ crc32_table[(crc & 0xff) ^ octet]; - } - return ~crc; -} diff --git a/src/reverse/BasicTypes.h b/src/reverse/BasicTypes.h index 05c5d6fa..08205f1b 100644 --- a/src/reverse/BasicTypes.h +++ b/src/reverse/BasicTypes.h @@ -20,7 +20,7 @@ struct Vector4 Vector4(float aX = 0.f, float aY = 0.f, float aZ = 0.f, float aW = 0.f) : x(aX), y(aY), z(aZ), w(aW) {} - + float x; float y; float z; @@ -36,11 +36,11 @@ struct EulerAngles EulerAngles(float aRoll = 0.f, float aPitch = 0.f, float aYaw = 0.f) : roll(aRoll), pitch(aPitch), yaw(aYaw) {} - + float roll; float pitch; float yaw; - + std::string ToString() const noexcept; bool operator==(const EulerAngles& acRhs) const noexcept; @@ -51,7 +51,7 @@ struct Quaternion Quaternion(float aI = 0.f, float aJ = 0.f, float aK = 0.f, float aR = 0.f) : i(aI), j(aJ), k(aK), r(aR) {} - + float i; float j; float k; @@ -62,8 +62,6 @@ struct Quaternion bool operator==(const Quaternion& acRhs) const noexcept; }; -uint32_t crc32(const char* buf, size_t len, uint32_t seed); - struct CName { CName(uint64_t aHash = 0) : hash(aHash){} @@ -73,14 +71,14 @@ struct CName CName(const std::string& aName) { if (aName != "None") - hash = RED4ext::FNV1a(aName.c_str()); + hash = RED4ext::FNV1a64(aName.c_str()); else hash = 0; } union { - uint64_t hash; + uint64_t hash{ 0 }; struct { uint32_t hash_lo; @@ -116,23 +114,24 @@ struct TweakDBID TweakDBID(const std::string_view aName) { - name_hash = crc32(aName.data(), aName.size(), 0); - name_length = aName.size(); + name_hash = RED4ext::CRC32(aName.data(), 0); + name_length = static_cast(aName.size()); unk5 = unk7 = 0; } TweakDBID(const TweakDBID& aBase, const std::string_view aName) { - name_hash = crc32(aName.data(), aName.size(), aBase.name_hash); - name_length = aName.size() + aBase.name_length; + name_hash = RED4ext::CRC32(aName.data(), aBase.name_hash); + name_length = static_cast(aName.size() + aBase.name_length); unk5 = unk7 = 0; } + std::string AsString() const noexcept; std::string ToString() const noexcept; bool operator==(const TweakDBID& acRhs) const noexcept; TweakDBID operator+(const std::string_view acName) const noexcept; - + union { uint64_t value{0}; @@ -157,12 +156,12 @@ struct ItemID std::string ToString() const noexcept; bool operator==(const ItemID& acRhs) const noexcept; - + TweakDBID id; uint32_t rng_seed{ 2 }; uint16_t unknown{ 0 }; uint8_t maybe_type{ 0 }; - uint8_t pad; + uint8_t pad{ 0 }; }; static_assert(sizeof(ItemID) == 0x10); @@ -179,13 +178,13 @@ struct Variant bool IsEmpty() const noexcept; bool IsInlined() const noexcept; - + RED4ext::CBaseRTTIType* GetType() const noexcept; RED4ext::ScriptInstance GetDataPtr() const noexcept; - + bool Init(const RED4ext::CBaseRTTIType* aType); bool Fill(const RED4ext::CBaseRTTIType* aType, const RED4ext::ScriptInstance aData); - bool Extract(RED4ext::ScriptInstance aBuffer); + bool Extract(RED4ext::ScriptInstance aBuffer) const; void Free(); inline static bool CanBeInlined(const RED4ext::CBaseRTTIType* aType) noexcept; @@ -205,7 +204,7 @@ struct Variant const RED4ext::CBaseRTTIType* type{ nullptr }; union { - uint8_t inlined[kInlineSize]{ 0 }; + mutable uint8_t inlined[kInlineSize]{ 0 }; RED4ext::ScriptInstance instance; }; }; diff --git a/src/reverse/ClassReference.cpp b/src/reverse/ClassReference.cpp index 3de7a402..c3932355 100644 --- a/src/reverse/ClassReference.cpp +++ b/src/reverse/ClassReference.cpp @@ -13,7 +13,7 @@ ClassReference::ClassReference(const TiltedPhoques::Locked::Ref& aView, - RED4ext::CBaseRTTIType* apClass) +ClassStatic::ClassStatic(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass) : ClassType(aView, apClass) { } diff --git a/src/reverse/ClassStatic.h b/src/reverse/ClassStatic.h index a797e837..dd113c92 100644 --- a/src/reverse/ClassStatic.h +++ b/src/reverse/ClassStatic.h @@ -6,8 +6,8 @@ struct ClassStatic : ClassType { ClassStatic(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass); - ~ClassStatic(); - + ~ClassStatic() override; + sol::function GetFactory(); private: diff --git a/src/reverse/Converter.h b/src/reverse/Converter.h index 43eaa44c..a154499d 100644 --- a/src/reverse/Converter.h +++ b/src/reverse/Converter.h @@ -1,6 +1,5 @@ #pragma once -#include "BasicTypes.h" #include "Enum.h" #include "ClassReference.h" #include "LuaRED.h" @@ -46,7 +45,7 @@ struct CNameConverter : LuaRED { sol::state_view v(aObject.lua_state()); std::string str = v["tostring"](aObject); - *(CName*)apType->value = CName(str); + *static_cast(apType->value) = CName(str); } else { @@ -79,7 +78,7 @@ struct TweakDBIDConverter : LuaRED { if (aObject != sol::nil && aObject.get_type() == sol::type::string) { - *(TweakDBID*)apType->value = TweakDBID(aObject.as()); + *static_cast(apType->value) = TweakDBID(aObject.as()); } else { @@ -155,13 +154,13 @@ struct EnumConverter : LuaRED else if (aObject == sol::nil) { auto* enumType = static_cast(apType->type); - Enum en(enumType, 0); + const Enum en(enumType, 0); en.Set(*apType); } else if (aObject.get_type() == sol::type::number) // Enum from number cast { auto* enumType = static_cast(apType->type); - Enum en(enumType, aObject.as()); + const Enum en(enumType, aObject.as()); en.Set(*apType); } else if (aObject.get_type() == sol::type::string) // Enum from string cast @@ -169,7 +168,7 @@ struct EnumConverter : LuaRED auto* enumType = static_cast(apType->type); sol::state_view v(aObject.lua_state()); std::string str = v["tostring"](aObject); - Enum en(enumType, str); + const Enum en(enumType, str); en.Set(*apType); } } @@ -218,9 +217,9 @@ struct ClassConverter : LuaRED //{ // // The implicit table to instance conversion `Game.FindEntityByID({ hash = 1 })` has potential issue: // // When the overloaded function takes an array and an object for the same arg the implicit conversion - // // can produce an empty instance making the unwanted overload callable. So for better experience it's + // // can produce an empty instance making the unwanted overload callable. So for better experience it's // // important to distinguish between linear array and array of props. - // + // // // Size check excludes non-empty linear arrays since only the table with sequential and integral keys // // has size (length). And iterator check excludes empty tables `{}`. // sol::table props = aObject.as(); @@ -274,8 +273,7 @@ struct RawConverter : LuaRED return make_object(aLua.Get(), UnknownType(aLua, aResult.type, aResult.value)); } - RED4ext::CStackType ToRED(sol::object aObject, RED4ext::CBaseRTTIType* apRtti, - TiltedPhoques::Allocator* apAllocator) + RED4ext::CStackType ToRED(sol::object aObject, RED4ext::CBaseRTTIType* apRtti, TiltedPhoques::Allocator*) { RED4ext::CStackType result; result.type = apRtti; diff --git a/src/reverse/Enum.cpp b/src/reverse/Enum.cpp index 17f08a60..5ad5e4b4 100644 --- a/src/reverse/Enum.cpp +++ b/src/reverse/Enum.cpp @@ -9,21 +9,21 @@ Enum::Enum(const RED4ext::CStackType& aStackType) Enum::Enum(const std::string& acTypeName, const std::string& acValue) { - auto* pType = static_cast(RED4ext::CRTTISystem::Get()->GetEnum(RED4ext::FNV1a(acTypeName.c_str()))); - if (pType) + const auto* cpType = RED4ext::CRTTISystem::Get()->GetEnum(RED4ext::FNV1a64(acTypeName.c_str())); + if (cpType) { - m_cpType = pType; + m_cpType = cpType; SetValueByName(acValue); } } Enum::Enum(const std::string& acTypeName, uint32_t aValue) { - auto* pType = static_cast(RED4ext::CRTTISystem::Get()->GetEnum(RED4ext::FNV1a(acTypeName.c_str()))); - if (pType) + const auto* cpType = RED4ext::CRTTISystem::Get()->GetEnum(RED4ext::FNV1a64(acTypeName.c_str())); + if (cpType) { - m_cpType = pType; - SetValueSafe(static_cast(aValue)); + m_cpType = cpType; + SetValueSafe(aValue); } } @@ -38,14 +38,14 @@ Enum::Enum(const RED4ext::CEnum* acpType, const std::string& acValue) Enum::Enum(const RED4ext::CEnum* acpType, uint32_t aValue) : m_cpType(acpType) { - SetValueSafe(static_cast(aValue)); + SetValueSafe(aValue); } void Enum::SetValueSafe(uint64_t aValue) { - for (auto i = 0; i < m_cpType->valueList.size; ++i) + for (uint32_t i = 0; i < m_cpType->valueList.size; ++i) { - if (m_cpType->valueList[i] == aValue) + if (m_cpType->valueList[i] == static_cast(aValue)) { m_value = aValue; break; @@ -117,10 +117,10 @@ std::string Enum::GetValueName() const { if (!m_cpType) return ""; - - for (auto i = 0; i < m_cpType->valueList.size; ++i) + + for (uint32_t i = 0; i < m_cpType->valueList.size; ++i) { - if (m_cpType->valueList[i] == m_value) + if (m_cpType->valueList[i] == static_cast(m_value)) { return m_cpType->hashList[i].ToString(); } @@ -134,10 +134,10 @@ void Enum::SetValueByName(const std::string& acValue) { if (!m_cpType) return; - + const RED4ext::CName cValueName(acValue.c_str()); - for (auto i = 0; i < m_cpType->hashList.size; ++i) + for (uint32_t i = 0; i < m_cpType->hashList.size; ++i) { if (m_cpType->hashList[i] == cValueName) { @@ -151,7 +151,7 @@ std::string Enum::ToString() const { if (m_cpType) { - RED4ext::CName name = m_cpType->GetName(); + const RED4ext::CName name = m_cpType->GetName(); return name.ToString() + std::string(" : ") + GetValueName() + std::string(" (") + std::to_string(m_value) + std::string(")"); } @@ -163,8 +163,8 @@ bool Enum::operator==(const Enum& acRhs) const noexcept if (!m_cpType || !acRhs.m_cpType) return false; - RED4ext::CName name = m_cpType->GetName(); - RED4ext::CName nameRhs = acRhs.m_cpType->GetName(); + const RED4ext::CName name = m_cpType->GetName(); + const RED4ext::CName nameRhs = acRhs.m_cpType->GetName(); return name == nameRhs && m_value == acRhs.m_value; } diff --git a/src/reverse/EnumStatic.cpp b/src/reverse/EnumStatic.cpp index 40ef0e68..ee0b40d5 100644 --- a/src/reverse/EnumStatic.cpp +++ b/src/reverse/EnumStatic.cpp @@ -15,18 +15,20 @@ EnumStatic::~EnumStatic() = default; sol::object EnumStatic::Index_Impl(const std::string& acName, sol::this_environment aThisEnv) { + // TODO - this should not use Type:: probably but ClassType auto result = Type::Index_Impl(acName, aThisEnv); if (result != sol::nil) return result; - auto* pEnum = static_cast(m_pType); + const auto* cpEnum = static_cast(m_pType); - if (!pEnum) + if (!cpEnum) return sol::nil; auto lockedState = m_lua.Lock(); - auto& luaState = lockedState.Get(); + const auto& cLuaState = lockedState.Get(); - return Type::NewIndex_Impl(acName, std::move(sol::make_object(luaState, Enum(pEnum, acName)))); + // TODO - this should not use Type:: probably but ClassType + return Type::NewIndex_Impl(acName, make_object(cLuaState, Enum(cpEnum, acName))); } diff --git a/src/reverse/EnumStatic.h b/src/reverse/EnumStatic.h index 47914361..9c212988 100644 --- a/src/reverse/EnumStatic.h +++ b/src/reverse/EnumStatic.h @@ -6,7 +6,7 @@ struct EnumStatic : ClassType { EnumStatic(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass); - ~EnumStatic(); - + ~EnumStatic() override; + sol::object Index_Impl(const std::string& acName, sol::this_environment aThisEnv) override; }; diff --git a/src/reverse/LuaRED.h b/src/reverse/LuaRED.h index d9e52ad0..90fec609 100644 --- a/src/reverse/LuaRED.h +++ b/src/reverse/LuaRED.h @@ -7,12 +7,12 @@ template struct LuaRED { static constexpr char const* Name = REDName; - + sol::object ToLua(RED4ext::CStackType& aResult, TiltedPhoques::Locked& aLua) { - if constexpr (std::is_integral_v && (sizeof(T) == sizeof(uint64_t))) + if constexpr (std::is_integral_v && sizeof(T) == sizeof(uint64_t)) { - constexpr auto format { (std::is_signed_v) ? ("return {}ll") : ("return {}ull") }; + constexpr auto format { std::is_signed_v ? "return {}ll" : "return {}ull" }; auto res { aLua.Get().script(fmt::format(format, *static_cast(aResult.value))) }; assert(res.valid()); return res.get(); @@ -23,8 +23,7 @@ struct LuaRED } } - RED4ext::CStackType ToRED(sol::object aObject, RED4ext::CBaseRTTIType* apRtti, - TiltedPhoques::Allocator* apAllocator) + RED4ext::CStackType ToRED(sol::object aObject, RED4ext::CBaseRTTIType*, TiltedPhoques::Allocator* apAllocator) { RED4ext::CStackType result; result.type = m_pRtti; @@ -32,13 +31,13 @@ struct LuaRED { result.value = apAllocator->New(); } - else if constexpr (std::is_integral_v && (sizeof(T) == sizeof(uint64_t))) + else if constexpr (std::is_integral_v && sizeof(T) == sizeof(uint64_t)) { if (aObject.get_type() == sol::type::number) { sol::state_view v(aObject.lua_state()); double value = v["tonumber"](aObject); - result.value = apAllocator->New(value); + result.value = apAllocator->New(static_cast(value)); } else if (IsLuaCData(aObject)) { @@ -65,7 +64,7 @@ struct LuaRED { sol::state_view v(aObject.lua_state()); double value = v["tonumber"](aObject); - result.value = apAllocator->New(value); + result.value = apAllocator->New(static_cast(value)); } } else if (aObject.is()) @@ -82,13 +81,13 @@ struct LuaRED { *reinterpret_cast(apType->value) = T{}; } - else if constexpr (std::is_integral_v && (sizeof(T) == sizeof(uint64_t))) + else if constexpr (std::is_integral_v && sizeof(T) == sizeof(uint64_t)) { if (aObject.get_type() == sol::type::number) { sol::state_view v(aObject.lua_state()); double value = v["tonumber"](aObject); - *reinterpret_cast(apType->value) = value; + *reinterpret_cast(apType->value) = static_cast(value); } else if (IsLuaCData(aObject)) { @@ -115,7 +114,7 @@ struct LuaRED { sol::state_view v(aObject.lua_state()); double value = v["tonumber"](aObject); - *reinterpret_cast(apType->value) = value; + *reinterpret_cast(apType->value) = static_cast(value); } } else if (aObject.is()) @@ -133,7 +132,7 @@ struct LuaRED { if (!Resolve()) return false; - + return apRtti == m_pRtti; } @@ -145,7 +144,7 @@ struct LuaRED return true; auto* pRtti = RED4ext::CRTTISystem::Get(); - m_pRtti = pRtti->GetType(RED4ext::FNV1a(Name)); + m_pRtti = pRtti->GetType(RED4ext::FNV1a64(Name)); return m_pRtti != nullptr; } diff --git a/src/reverse/RTTIExtender.cpp b/src/reverse/RTTIExtender.cpp index bd793119..e593d9be 100644 --- a/src/reverse/RTTIExtender.cpp +++ b/src/reverse/RTTIExtender.cpp @@ -1,17 +1,17 @@ #include "RTTIExtender.h" + #include #include #include #include #include - template struct GameCall { GameCall(uintptr_t aAddress, const int32_t acOffset = 0) { - RED4ext::RelocPtr addr(aAddress); + const RED4ext::RelocPtr addr(aAddress); const auto* pLocation = addr.GetAddr(); m_address = pLocation ? reinterpret_cast(pLocation + acOffset) : nullptr; } @@ -45,7 +45,7 @@ struct REDSmartPtr AddRef(); } - REDSmartPtr(REDSmartPtr&& aOther) + REDSmartPtr(REDSmartPtr&& aOther) noexcept : data(aOther.data) { aOther.data = nullptr; @@ -67,6 +67,9 @@ struct REDSmartPtr REDSmartPtr& operator=(const REDSmartPtr& acOther) { + if (this == &acOther) + return *this; + Reset(); data = acOther.data; AddRef(); @@ -74,8 +77,11 @@ struct REDSmartPtr return *this; } - REDSmartPtr& operator=(REDSmartPtr&& aOther) + REDSmartPtr& operator=(REDSmartPtr&& aOther) noexcept { + if (this == &aOther) + return *this; + Reset(); data = aOther.data; aOther.data = nullptr; @@ -118,7 +124,7 @@ struct IUpdatableSystem : RED4ext::IScriptable struct gameIGameSystem : IUpdatableSystem { - RED4ext::GameInstance* gameInstance; + RED4ext::GameInstance* gameInstance = nullptr; static void CallConstructor(void* apAddress) { @@ -205,7 +211,7 @@ struct TEMP_SpawnSettings constexpr auto FixedToFloat = [](const int32_t acValue) { return acValue * (1.f / (2 << 16)); - }; + }; transform.position.X = FixedToFloat(acWorldTransform.Position.x.Bits); transform.position.Y = FixedToFloat(acWorldTransform.Position.y.Bits); @@ -215,10 +221,10 @@ struct TEMP_SpawnSettings // ------------------------------ // Normalize Quats - float quatLength = sqrtf(acWorldTransform.Orientation.i * acWorldTransform.Orientation.i + - acWorldTransform.Orientation.j * acWorldTransform.Orientation.j + - acWorldTransform.Orientation.k * acWorldTransform.Orientation.k + - acWorldTransform.Orientation.r * acWorldTransform.Orientation.r); + const float quatLength = sqrtf(acWorldTransform.Orientation.i * acWorldTransform.Orientation.i + + acWorldTransform.Orientation.j * acWorldTransform.Orientation.j + + acWorldTransform.Orientation.k * acWorldTransform.Orientation.k + + acWorldTransform.Orientation.r * acWorldTransform.Orientation.r); if (quatLength != 0.0f) { transform.orientation.i = acWorldTransform.Orientation.i / quatLength; @@ -256,7 +262,7 @@ struct TEMP_Spawner uintptr_t unk58 = 0; uintptr_t unk60 = 0; - TEMP_Spawner(RED4ext::IMemoryAllocator* apAllocator = nullptr) + TEMP_Spawner(RED4ext::Memory::IAllocator* apAllocator = nullptr) : unk00(apAllocator) , spawnedEntities(apAllocator) , pendingEntities(apAllocator) @@ -287,7 +293,7 @@ struct TEMP_Spawner REDSmartPtr pendingEntity; func(this, &pendingEntity, aSettings, acEntityPath); - RED4ext::ent::EntityID entityID; + RED4ext::ent::EntityID entityID{}; entityID.hash = pendingEntity ? pendingEntity->entityID.hash : 0; return entityID; } @@ -299,7 +305,7 @@ struct TEMP_Spawner RED4ext::CName entityPath; if (!pTDB->TryGetValue(RED4ext::TweakDBID(acRecordDBID, ".entityTemplatePath"), entityPath)) { - RED4ext::ent::EntityID entityID; + RED4ext::ent::EntityID entityID{}; entityID.hash = 0; return entityID; } @@ -320,7 +326,7 @@ struct TEMP_Spawner return Spawn(entityPath, aSettings); } - void Despawn(RED4ext::Handle aEntity) + void Despawn(const RED4ext::Handle& aEntity) { using TFunc = void(*)(TEMP_Spawner*, RED4ext::IScriptable*); static GameCall func(CyberEngineTweaks::Addresses::gameIGameSystem_Despawn); @@ -332,10 +338,10 @@ RED4EXT_ASSERT_SIZE(TEMP_Spawner, 0x68); #pragma endregion -void CreateSingleton(RED4ext::Handle apClassInstance) +void CreateSingleton(const RED4ext::Handle& apClassInstance) { auto* pRTTI = RED4ext::CRTTISystem::Get(); - auto* pType = reinterpret_cast(apClassInstance->GetNativeType()); + auto* pType = apClassInstance->GetNativeType(); auto* pGameInstance = RED4ext::CGameEngine::Get()->framework->gameInstance; if (pGameInstance->GetInstance(pType) != nullptr) return; // already init @@ -402,8 +408,8 @@ void CreateSingleton(const RED4ext::CName acTypeName) if (pGameInstance->GetInstance(pType) != nullptr) return; // already init - auto* pClassInstance = (RED4ext::IScriptable*)pType->AllocInstance(); - RED4ext::Handle handle(pClassInstance); + auto* pClassInstance = static_cast(pType->CreateInstance()); + RED4ext::Handle handle(pClassInstance); CreateSingleton(handle); } @@ -413,8 +419,8 @@ void CreateSingleton(const RED4ext::CName acTypeName) // This is kept for backward compatibility. // Use exEntitySpawner -void WorldFunctionalTests_SpawnEntity(RED4ext::IScriptable* apContext, RED4ext::CStackFrame* apFrame, - RED4ext::ent::EntityID* apOut, int64_t a4) +void WorldFunctionalTests_SpawnEntity(RED4ext::IScriptable*, RED4ext::CStackFrame* apFrame, + RED4ext::ent::EntityID* apOut, int64_t) { struct FunctionalTestsGameSystem { @@ -423,12 +429,12 @@ void WorldFunctionalTests_SpawnEntity(RED4ext::IScriptable* apContext, RED4ext:: }; RED4ext::CString entityPath; - RED4ext::WorldTransform worldTransform; + RED4ext::WorldTransform worldTransform{}; RED4ext::CString unknown; - RED4ext::GetParameter(apFrame, &entityPath); - RED4ext::GetParameter(apFrame, &worldTransform); - RED4ext::GetParameter(apFrame, &unknown); + GetParameter(apFrame, &entityPath); + GetParameter(apFrame, &worldTransform); + GetParameter(apFrame, &unknown); apFrame->code++; // skip ParamEnd auto* pRTTI = RED4ext::CRTTISystem::Get(); @@ -437,7 +443,7 @@ void WorldFunctionalTests_SpawnEntity(RED4ext::IScriptable* apContext, RED4ext:: auto* pFunctionalSystem = reinterpret_cast(pGameInstance->GetInstance(pFunctionalType)); uint32_t oldSize = pFunctionalSystem->spawner.pendingEntities.size; - RED4ext::ExecuteFunction("WorldFunctionalTests", "Internal_SpawnEntity", nullptr, entityPath, worldTransform, + ExecuteFunction("WorldFunctionalTests", "Internal_SpawnEntity", nullptr, entityPath, worldTransform, unknown); // if any entity was spawned @@ -449,15 +455,14 @@ void WorldFunctionalTests_SpawnEntity(RED4ext::IScriptable* apContext, RED4ext:: } } -void WorldFunctionalTests_DespawnEntity(RED4ext::IScriptable* apContext, RED4ext::CStackFrame* apFrame, void* apOut, - int64_t a4) +void WorldFunctionalTests_DespawnEntity(RED4ext::IScriptable*, RED4ext::CStackFrame* apFrame, void*, int64_t) { RED4ext::Handle entity; - RED4ext::GetParameter(apFrame, &entity); + GetParameter(apFrame, &entity); apFrame->code++; // skip ParamEnd - RED4ext::ExecuteFunction("WorldFunctionalTests", "Internal_DespawnEntity", nullptr, entity); + ExecuteFunction("WorldFunctionalTests", "Internal_DespawnEntity", nullptr, entity); } #pragma endregion @@ -480,25 +485,25 @@ struct exEntitySpawnerSystem : gameIGameSystem constexpr int32_t VftableSize = 0x1A8; static uintptr_t* ClonedVftable = nullptr; - gameIGameSystem::CallConstructor(apAddress); + CallConstructor(apAddress); if (ClonedVftable == nullptr) { - ClonedVftable = reinterpret_cast(malloc(VftableSize)); - memcpy(ClonedVftable, *reinterpret_cast(apAddress), VftableSize); + ClonedVftable = static_cast(malloc(VftableSize)); + memcpy(ClonedVftable, *static_cast(apAddress), VftableSize); - ClonedVftable[0] = (uintptr_t)&HOOK_GetNativeType; - ClonedVftable[0x118 / 8] = (uintptr_t)&HOOK_OnInitialize; - ClonedVftable[0x120 / 8] = (uintptr_t)&HOOK_OnShutdown; + ClonedVftable[0] = reinterpret_cast(&HOOK_GetNativeType); + ClonedVftable[0x118 / 8] = reinterpret_cast(&HOOK_OnInitialize); + ClonedVftable[0x120 / 8] = reinterpret_cast(&HOOK_OnShutdown); } // overwrite vftable with our clone - *(uintptr_t**)apAddress = ClonedVftable; + *static_cast(apAddress) = ClonedVftable; - return reinterpret_cast(apAddress); + return static_cast(apAddress); } - static RED4ext::CBaseRTTIType* HOOK_GetNativeType(exEntitySpawnerSystem* apThis) + static RED4ext::CBaseRTTIType* HOOK_GetNativeType(exEntitySpawnerSystem*) { auto* pRTTI = RED4ext::CRTTISystem::Get(); return pRTTI->GetClass(NATIVE_TYPE_STR); @@ -509,14 +514,14 @@ struct exEntitySpawnerSystem : gameIGameSystem exEntitySpawner_Spawner.Initialize(apThis->gameInstance); } - static void HOOK_OnShutdown(exEntitySpawnerSystem* apThis) + static void HOOK_OnShutdown(exEntitySpawnerSystem*) { exEntitySpawner_Spawner.UnInitialize(); } static void SpawnCallback(TEMP_PendingEntity::Unk00& aUnk) { - using TFunc = void(*)(RED4ext::IScriptable*, RED4ext::ent::Entity*); + using TFunc = void(*)(IScriptable*, RED4ext::ent::Entity*); static GameCall func(CyberEngineTweaks::Addresses::gameIGameSystem_SpawnCallback); struct GameInstance_78_Unk @@ -550,21 +555,20 @@ struct exEntitySpawnerSystem : gameIGameSystem auto* pAllocator = pRTTI->GetClass(PARENT_TYPE)->GetAllocator(); Singleton = CreateNew(pAllocator->AllocAligned(sizeof(exEntitySpawnerSystem), alignof(exEntitySpawnerSystem)).memory); - CreateSingleton(RED4ext::Handle(Singleton)); + CreateSingleton(RED4ext::Handle(Singleton)); } - static void Spawn(RED4ext::IScriptable* apContext, RED4ext::CStackFrame* apFrame, RED4ext::ent::EntityID* apOut, - int64_t a4) + static void Spawn(IScriptable*, RED4ext::CStackFrame* apFrame, RED4ext::ent::EntityID* apOut, int64_t) { RED4ext::CName entityPath; // <- raRef - RED4ext::WorldTransform worldTransform; + RED4ext::WorldTransform worldTransform{}; RED4ext::CName appearance = "default"; RED4ext::TweakDBID recordDBID = 0; - RED4ext::GetParameter(apFrame, &entityPath); - RED4ext::GetParameter(apFrame, &worldTransform); - RED4ext::GetParameter(apFrame, &appearance); - RED4ext::GetParameter(apFrame, &recordDBID); + GetParameter(apFrame, &entityPath); + GetParameter(apFrame, &worldTransform); + GetParameter(apFrame, &appearance); + GetParameter(apFrame, &recordDBID); apFrame->code++; // skip ParamEnd if (appearance == RED4ext::CName("")) @@ -579,7 +583,7 @@ struct exEntitySpawnerSystem : gameIGameSystem settings.SetRecordID(recordDBID); settings.SetTransform(worldTransform); - auto entityID = exEntitySpawner_Spawner.Spawn(entityPath, settings); + const auto entityID = exEntitySpawner_Spawner.Spawn(entityPath, settings); if (apOut != nullptr) { @@ -587,15 +591,14 @@ struct exEntitySpawnerSystem : gameIGameSystem } } - static void SpawnRecord(RED4ext::IScriptable* apContext, RED4ext::CStackFrame* apFrame, - RED4ext::ent::EntityID* apOut, int64_t a4) + static void SpawnRecord(IScriptable*, RED4ext::CStackFrame* apFrame, RED4ext::ent::EntityID* apOut, int64_t) { RED4ext::TweakDBID recordDBID = 0; - RED4ext::WorldTransform worldTransform; + RED4ext::WorldTransform worldTransform{}; RED4ext::CName appearance = "default"; - RED4ext::GetParameter(apFrame, &recordDBID); - RED4ext::GetParameter(apFrame, &worldTransform); - RED4ext::GetParameter(apFrame, &appearance); + GetParameter(apFrame, &recordDBID); + GetParameter(apFrame, &worldTransform); + GetParameter(apFrame, &appearance); apFrame->code++; // skip ParamEnd if (appearance == RED4ext::CName("")) @@ -609,7 +612,7 @@ struct exEntitySpawnerSystem : gameIGameSystem settings.callback = SpawnCallback; settings.SetTransform(worldTransform); - auto entityID = exEntitySpawner_Spawner.SpawnRecord(recordDBID, settings); + const auto entityID = exEntitySpawner_Spawner.SpawnRecord(recordDBID, settings); if (apOut != nullptr) { @@ -617,11 +620,11 @@ struct exEntitySpawnerSystem : gameIGameSystem } } - static void Despawn(RED4ext::IScriptable* apContext, RED4ext::CStackFrame* apFrame, void* apOut, int64_t a4) + static void Despawn(IScriptable*, RED4ext::CStackFrame* apFrame, void*, int64_t) { - RED4ext::Handle entity; + RED4ext::Handle entity; - RED4ext::GetParameter(apFrame, &entity); + GetParameter(apFrame, &entity); apFrame->code++; // skip ParamEnd exEntitySpawner_Spawner.Despawn(entity); diff --git a/src/reverse/RTTIExtender.h b/src/reverse/RTTIExtender.h index eac5da57..7bc73383 100644 --- a/src/reverse/RTTIExtender.h +++ b/src/reverse/RTTIExtender.h @@ -1,5 +1,4 @@ #pragma once -#include class RTTIExtender { @@ -8,4 +7,4 @@ class RTTIExtender public: static void Initialize(); -}; \ No newline at end of file +}; diff --git a/src/reverse/RTTIHelper.cpp b/src/reverse/RTTIHelper.cpp index d2193ed0..f17ca59a 100644 --- a/src/reverse/RTTIHelper.cpp +++ b/src/reverse/RTTIHelper.cpp @@ -2,23 +2,23 @@ #include "RTTIHelper.h" -#include "Type.h" #include "ClassReference.h" #include "StrongReference.h" #include "WeakReference.h" -#include "scripting/Scripting.h" -#include "Utils.h" + #include +#include +#include -static constexpr const bool s_cEnableOverloads = true; -static constexpr const bool s_cLogAllOverloadVariants = true; -static constexpr const bool s_cThrowLuaErrors = true; +static constexpr bool s_cEnableOverloads = true; +static constexpr bool s_cLogAllOverloadVariants = true; +static constexpr bool s_cThrowLuaErrors = true; static std::unique_ptr s_pInstance{ nullptr }; -void RTTIHelper::Initialize(const LockableState& acLua) +void RTTIHelper::Initialize(const LockableState& acLua, LuaSandbox& apSandbox) { - s_pInstance.reset(new RTTIHelper(acLua)); + s_pInstance.reset(new RTTIHelper(acLua, apSandbox)); } void RTTIHelper::Shutdown() @@ -34,8 +34,9 @@ RTTIHelper& RTTIHelper::Get() return *s_pInstance; } -RTTIHelper::RTTIHelper(const LockableState& acLua) - : m_lua(acLua) +RTTIHelper::RTTIHelper(const LockableState& acpLua, LuaSandbox& apSandbox) + : m_lua(acpLua) + , m_sandbox(apSandbox) { InitializeRTTI(); ParseGlobalStatics(); @@ -44,13 +45,13 @@ RTTIHelper::RTTIHelper(const LockableState& acLua) void RTTIHelper::InitializeRTTI() { m_pRtti = RED4ext::CRTTISystem::Get(); - m_pGameInstanceType = m_pRtti->GetClass(RED4ext::FNV1a("ScriptGameInstance")); + m_pGameInstanceType = m_pRtti->GetClass(RED4ext::FNV1a64("ScriptGameInstance")); const auto cpEngine = RED4ext::CGameEngine::Get(); const auto cpGameInstance = cpEngine->framework->gameInstance; - const auto cpPlayerSystemType = m_pRtti->GetType(RED4ext::FNV1a("cpPlayerSystem")); + const auto cpPlayerSystemType = m_pRtti->GetType(RED4ext::FNV1a64("cpPlayerSystem")); - m_pGameInstance = reinterpret_cast(m_pGameInstanceType->AllocInstance()); + m_pGameInstance = static_cast(m_pGameInstanceType->CreateInstance()); m_pGameInstance->gameInstance = cpGameInstance; m_pPlayerSystem = reinterpret_cast(cpGameInstance->GetInstance(cpPlayerSystemType)); @@ -67,8 +68,8 @@ void RTTIHelper::ParseGlobalStatics() const auto cClassName = cOrigName.substr(0, cClassSep); const auto cFullName = cOrigName.substr(cClassSep + 2); - const auto cClassHash = RED4ext::FNV1a(cClassName.c_str()); - const auto cFullHash = RED4ext::FNV1a(cFullName.c_str()); + const auto cClassHash = RED4ext::FNV1a64(cClassName.c_str()); + const auto cFullHash = RED4ext::FNV1a64(cFullName.c_str()); if (!m_extendedFunctions.contains(cClassHash)) m_extendedFunctions.emplace(cClassHash, 0); @@ -78,12 +79,12 @@ void RTTIHelper::ParseGlobalStatics() }); } -void RTTIHelper::AddFunctionAlias(const std::string& acAliasFuncName, const std::string& acOrigClassName, const std::string& acOrigFuncName) +void RTTIHelper::AddFunctionAlias(const std::string&, const std::string& acOrigClassName, const std::string& acOrigFuncName) { - auto* pClass = m_pRtti->GetClass(RED4ext::FNV1a(acOrigClassName.c_str())); + auto* pClass = m_pRtti->GetClass(RED4ext::FNV1a64(acOrigClassName.c_str())); if (pClass) { - auto* pFunc = FindFunction(pClass, RED4ext::FNV1a(acOrigFuncName.c_str())); + auto* pFunc = FindFunction(pClass, RED4ext::FNV1a64(acOrigFuncName.c_str())); if (pFunc) { @@ -95,17 +96,17 @@ void RTTIHelper::AddFunctionAlias(const std::string& acAliasFuncName, const std: } } -void RTTIHelper::AddFunctionAlias(const std::string& acAliasClassName, const std::string& acAliasFuncName, +void RTTIHelper::AddFunctionAlias(const std::string& acAliasClassName, const std::string&, const std::string& acOrigClassName, const std::string& acOrigFuncName) { - auto* pClass = m_pRtti->GetClass(RED4ext::FNV1a(acOrigClassName.c_str())); + auto* pClass = m_pRtti->GetClass(RED4ext::FNV1a64(acOrigClassName.c_str())); if (pClass) { - auto* pFunc = FindFunction(pClass, RED4ext::FNV1a(acOrigFuncName.c_str())); + auto* pFunc = FindFunction(pClass, RED4ext::FNV1a64(acOrigFuncName.c_str())); if (pFunc) { - const auto cClassHash = RED4ext::FNV1a(acAliasClassName.c_str()); + const auto cClassHash = RED4ext::FNV1a64(acAliasClassName.c_str()); if (!m_extendedFunctions.contains(cClassHash)) m_extendedFunctions.emplace(cClassHash, 0); @@ -117,12 +118,12 @@ void RTTIHelper::AddFunctionAlias(const std::string& acAliasClassName, const std bool RTTIHelper::IsFunctionAlias(RED4ext::CBaseFunction* apFunc) { - static const auto s_cTweakDBInterfaceHash = RED4ext::FNV1a("gamedataTweakDBInterface"); - static const auto s_cTDBIDHelperHash = RED4ext::FNV1a("gamedataTDBIDHelper"); + static constexpr auto s_cTweakDBInterfaceHash = RED4ext::FNV1a64("gamedataTweakDBInterface"); + static constexpr auto s_cTDBIDHelperHash = RED4ext::FNV1a64("gamedataTDBIDHelper"); if (m_extendedFunctions.contains(kGlobalHash)) { - auto& extendedFuncs = m_extendedFunctions.at(kGlobalHash); + const auto& extendedFuncs = m_extendedFunctions.at(kGlobalHash); if (extendedFuncs.contains(apFunc->fullName.hash)) return true; @@ -139,7 +140,7 @@ bool RTTIHelper::IsFunctionAlias(RED4ext::CBaseFunction* apFunc) if (m_extendedFunctions.contains(cClassHash)) { - auto& extendedFuncs = m_extendedFunctions.at(cClassHash); + const auto& extendedFuncs = m_extendedFunctions.at(cClassHash); if (extendedFuncs.contains(apFunc->fullName.hash)) return true; @@ -304,12 +305,12 @@ sol::function RTTIHelper::ResolveFunction(const std::string& acFuncName) if (!m_pRtti) return sol::nil; - const auto cFuncHash = RED4ext::FNV1a(acFuncName.c_str()); - + const auto cFuncHash = RED4ext::FNV1a64(acFuncName.c_str()); + auto invokable = GetResolvedFunction(cFuncHash); if (invokable != sol::nil) return invokable; - + const auto cIsFullName = acFuncName.find(';') != std::string::npos; if (cIsFullName) @@ -359,8 +360,8 @@ sol::function RTTIHelper::ResolveFunction(RED4ext::CClass* apClass, const std::s if (apClass == nullptr) return ResolveFunction(acFuncName); - const auto cClassHash = RED4ext::FNV1a(apClass->name.ToString()); - const auto cFuncHash = RED4ext::FNV1a(acFuncName.c_str()); + const auto cClassHash = RED4ext::FNV1a64(apClass->name.ToString()); + const auto cFuncHash = RED4ext::FNV1a64(acFuncName.c_str()); auto invokable = GetResolvedFunction(cClassHash, cFuncHash, aIsMember); if (invokable != sol::nil) @@ -370,7 +371,7 @@ sol::function RTTIHelper::ResolveFunction(RED4ext::CClass* apClass, const std::s if (cIsFullName) { - auto pFunc = FindFunction(apClass, cFuncHash); + const auto pFunc = FindFunction(apClass, cFuncHash); if (!pFunc) return sol::nil; @@ -412,7 +413,7 @@ sol::function RTTIHelper::MakeInvokableFunction(RED4ext::CBaseFunction* apFunc) return MakeSolFunction(luaState, [this, apFunc, cAllowNull](sol::variadic_args aArgs, sol::this_state aState, sol::this_environment aEnv) -> sol::variadic_results { uint64_t argOffset = 0; - RED4ext::ScriptInstance pHandle = ResolveHandle(apFunc, aArgs, argOffset); + const RED4ext::ScriptInstance pHandle = ResolveHandle(apFunc, aArgs, argOffset); std::string errorMessage; auto result = ExecuteFunction(apFunc, pHandle, aArgs, argOffset, errorMessage, cAllowNull); @@ -435,23 +436,23 @@ sol::function RTTIHelper::MakeInvokableFunction(RED4ext::CBaseFunction* apFunc) }); } -sol::function RTTIHelper::MakeInvokableOverload(std::map aOverloadedFuncs) +sol::function RTTIHelper::MakeInvokableOverload(std::map aOverloadedFuncs) const { auto lockedState = m_lua.Lock(); auto& luaState = lockedState.Get(); TiltedPhoques::Vector variants; - for (const auto& cFunc : aOverloadedFuncs) - variants.emplace_back(cFunc.second); + for (const auto& func : aOverloadedFuncs | std::views::values) + variants.emplace_back(func); return MakeSolFunction(luaState, [this, variants](sol::variadic_args aArgs, sol::this_state aState, sol::this_environment aEnv) mutable -> sol::variadic_results { - for (auto variant = variants.begin(); variant != variants.end(); variant++) + for (auto variant = variants.begin(); variant != variants.end(); ++variant) { variant->lastError.clear(); uint64_t argOffset = 0; - RED4ext::ScriptInstance pHandle = ResolveHandle(variant->func, aArgs, argOffset); + const RED4ext::ScriptInstance pHandle = ResolveHandle(variant->func, aArgs, argOffset); auto result = ExecuteFunction(variant->func, pHandle, aArgs, argOffset, variant->lastError); @@ -463,7 +464,7 @@ sol::function RTTIHelper::MakeInvokableOverload(std::maptotalCalls > previous->totalCalls) std::iter_swap(previous, variant); @@ -477,10 +478,10 @@ sol::function RTTIHelper::MakeInvokableOverload(std::map>(); + const auto logger = cEnv["__logger"].get>(); - for (auto variant = variants.begin(); variant != variants.end(); variant++) - logger->info("{}: {}", variant->func->fullName.ToString(), variant->lastError); + for (auto& variant : variants) + logger->info("{}: {}", variant.func->fullName.ToString(), variant.lastError); logger->flush(); } @@ -617,7 +618,7 @@ sol::variadic_results RTTIHelper::ExecuteFunction(RED4ext::CBaseFunction* apFunc // Allocator management ++s_callDepth; - ScopeGuard allocatorGuard([&]() { + ScopeGuard allocatorGuard([&]{ --s_callDepth; if (s_callDepth == 0u) @@ -633,10 +634,9 @@ sol::variadic_results RTTIHelper::ExecuteFunction(RED4ext::CBaseFunction* apFunc uint32_t callArgOffset = 0u; - ScopeGuard argsGuard([&]() { + ScopeGuard argsGuard([&]{ for (auto j = 0u; j < callArgOffset; ++j) { - const auto cpParam = apFunc->params[callArgToParam[j]]; const bool isNew = argNeedsFree[j]; FreeInstance(callArgs[j], !isNew, isNew, &s_scratchMemory); @@ -692,8 +692,7 @@ sol::variadic_results RTTIHelper::ExecuteFunction(RED4ext::CBaseFunction* apFunc if (!callArgs[callArgOffset].value) { - RED4ext::CName typeName; - cpParam->type->GetName(typeName); + const auto typeName = cpParam->type->GetName(); aErrorMessage = fmt::format("Function '{}' parameter {} must be {}.", apFunc->shortName.ToString(), i + 1, typeName.ToString()); return {}; @@ -703,7 +702,7 @@ sol::variadic_results RTTIHelper::ExecuteFunction(RED4ext::CBaseFunction* apFunc ++callArgOffset; } - const bool hasReturnType = (apFunc->returnType) != nullptr && (apFunc->returnType->type) != nullptr; + const bool hasReturnType = apFunc->returnType != nullptr && apFunc->returnType->type != nullptr; uint8_t buffer[1000]{ 0 }; RED4ext::CStackType result; @@ -741,7 +740,7 @@ sol::variadic_results RTTIHelper::ExecuteFunction(RED4ext::CBaseFunction* apFunc // the underlying value must be unwrapped and the wrapper explicitly destroyed. The workaround has been proven to work, // but it seems too much for only one known case, so it is not included for now. - results.push_back(Scripting::ToLua(lockedState, result)); + results.emplace_back(Scripting::ToLua(lockedState, result)); FreeInstance(result, false, false, &s_scratchMemory); } @@ -750,7 +749,7 @@ sol::variadic_results RTTIHelper::ExecuteFunction(RED4ext::CBaseFunction* apFunc const auto cpParam = apFunc->params[callArgToParam[i]]; if (cpParam->flags.isOut) - results.push_back(Scripting::ToLua(lockedState, callArgs[i])); + results.emplace_back(Scripting::ToLua(lockedState, callArgs[i])); } return results; @@ -761,7 +760,7 @@ RED4ext::ScriptInstance RTTIHelper::NewPlaceholder(RED4ext::CBaseRTTIType* apTyp { auto* pMemory = apAllocator->Allocate(apType->GetSize()); memset(pMemory, 0, apType->GetSize()); - apType->Init(pMemory); + apType->Construct(pMemory); return pMemory; } @@ -780,7 +779,7 @@ RED4ext::ScriptInstance RTTIHelper::NewInstance(RED4ext::CBaseRTTIType* apType, } else if (apType->GetType() == RED4ext::ERTTIType::Handle) { - auto* pInnerType = reinterpret_cast(apType)->GetInnerType(); + auto* pInnerType = reinterpret_cast(apType)->GetInnerType(); if (pInnerType->GetType() == RED4ext::ERTTIType::Class) pClass = reinterpret_cast(pInnerType); @@ -791,15 +790,12 @@ RED4ext::ScriptInstance RTTIHelper::NewInstance(RED4ext::CBaseRTTIType* apType, // AllocInstance() seems to be the only function that initializes an instance. // Neither Init() nor InitCls() initializes properties. - auto* pInstance = pClass->AllocInstance(); + auto* pInstance = pClass->CreateInstance(); if (aProps.has_value()) SetProperties(pClass, pInstance, aProps.value()); - if (apType->GetType() == RED4ext::ERTTIType::Handle) - return apAllocator->New>((RED4ext::IScriptable*)pInstance); - else - return pInstance; + return (apType->GetType() == RED4ext::ERTTIType::Handle) ? apAllocator->New>(static_cast(pInstance)) : pInstance; } sol::object RTTIHelper::NewInstance(RED4ext::CBaseRTTIType* apType, sol::optional aProps) const @@ -817,7 +813,7 @@ sol::object RTTIHelper::NewInstance(RED4ext::CBaseRTTIType* apType, sol::optiona auto instance = Scripting::ToLua(lockedState, result); FreeInstance(result, true, true, &allocator); - + if (aProps.has_value()) { const auto pInstance = instance.as(); @@ -846,14 +842,14 @@ sol::object RTTIHelper::NewHandle(RED4ext::CBaseRTTIType* apType, sol::optional< // Wrap ISerializable descendants in Handle if (result.value && apType->GetType() == RED4ext::ERTTIType::Class) { - static auto* s_pHandleType = m_pRtti->GetType(RED4ext::FNV1a("handle:Activator")); - static auto* s_pISerializableType = m_pRtti->GetType(RED4ext::FNV1a("ISerializable")); + static auto* s_pHandleType = m_pRtti->GetType(RED4ext::FNV1a64("handle:Activator")); + static auto* s_pISerializableType = m_pRtti->GetType(RED4ext::FNV1a64("ISerializable")); - auto* pClass = reinterpret_cast(apType); + const auto* pClass = reinterpret_cast(apType); if (pClass->IsA(s_pISerializableType)) { - auto* pInstance = reinterpret_cast(result.value); + auto* pInstance = static_cast(result.value); auto* pHandle = allocator.New>(pInstance); result.type = s_pHandleType; // To trick converter and deallocator @@ -882,7 +878,7 @@ sol::object RTTIHelper::GetProperty(RED4ext::CClass* apClass, RED4ext::ScriptIns if (!m_pRtti) return sol::nil; - auto* pProp = apClass->GetProperty(acPropName.c_str()); + const auto* pProp = apClass->GetProperty(acPropName.c_str()); if (!pProp) return sol::nil; @@ -901,7 +897,7 @@ void RTTIHelper::SetProperty(RED4ext::CClass* apClass, RED4ext::ScriptInstance a if (!m_pRtti) return; - auto* pProp = apClass->GetProperty(acPropName.c_str()); + const auto* pProp = apClass->GetProperty(acPropName.c_str()); if (!pProp) return; @@ -920,7 +916,7 @@ void RTTIHelper::SetProperty(RED4ext::CClass* apClass, RED4ext::ScriptInstance a if (stackType.value) { - pProp->SetValue(apHandle, static_cast(stackType.value)); + pProp->SetValue(apHandle, stackType.value); FreeInstance(stackType, true, false, &s_scratchMemory); aSuccess = true; @@ -938,11 +934,11 @@ void RTTIHelper::SetProperties(RED4ext::CClass* apClass, RED4ext::ScriptInstance // Check if type is implemented using ClassReference bool RTTIHelper::IsClassReferenceType(RED4ext::CClass* apClass) const { - static const auto s_cHashVector3 = RED4ext::FNV1a("Vector3"); - static const auto s_cHashVector4 = RED4ext::FNV1a("Vector4"); - static const auto s_cHashEulerAngles = RED4ext::FNV1a("EulerAngles"); - static const auto s_cHashQuaternion = RED4ext::FNV1a("Quaternion"); - static const auto s_cHashItemID = RED4ext::FNV1a("gameItemID"); + static constexpr auto s_cHashVector3 = RED4ext::FNV1a64("Vector3"); + static constexpr auto s_cHashVector4 = RED4ext::FNV1a64("Vector4"); + static constexpr auto s_cHashEulerAngles = RED4ext::FNV1a64("EulerAngles"); + static constexpr auto s_cHashQuaternion = RED4ext::FNV1a64("Quaternion"); + static constexpr auto s_cHashItemID = RED4ext::FNV1a64("gameItemID"); return apClass->name.hash != s_cHashVector3 && apClass->name.hash != s_cHashVector4 && apClass->name.hash != s_cHashEulerAngles && apClass->name.hash != s_cHashQuaternion && @@ -954,8 +950,7 @@ void RTTIHelper::FreeInstance(RED4ext::CStackType& aStackType, bool aOwn, bool a FreeInstance(aStackType.type, aStackType.value, aOwn, aNew, apAllocator); } -void RTTIHelper::FreeInstance(RED4ext::CBaseRTTIType* apType, void* apValue, bool aOwn, bool aNew, - TiltedPhoques::Allocator* apAllocator) const +void RTTIHelper::FreeInstance(RED4ext::CBaseRTTIType* apType, void* apValue, bool aOwn, bool aNew, TiltedPhoques::Allocator*) const { if (!apValue) return; @@ -972,20 +967,20 @@ void RTTIHelper::FreeInstance(RED4ext::CBaseRTTIType* apType, void* apValue, boo // Skip basic types if (IsClassReferenceType(pClass)) { - pClass->DestroyCls(reinterpret_cast(apValue)); + pClass->DestructCls(apValue); pClass->GetAllocator()->Free(apValue); } } } else { - apType->Destroy(apValue); + apType->Destruct(apValue); } } else { if (aNew || apType->GetType() != RED4ext::ERTTIType::Class) - apType->Destroy(apValue); + apType->Destruct(apValue); } // Right now it's a workaround that doesn't cover all cases but most. diff --git a/src/reverse/RTTIHelper.h b/src/reverse/RTTIHelper.h index 4e44062e..f10ef88f 100644 --- a/src/reverse/RTTIHelper.h +++ b/src/reverse/RTTIHelper.h @@ -2,6 +2,7 @@ #include "BasicTypes.h" +struct LuaSandbox; struct RTTIHelper { using LockableState = TiltedPhoques::Lockable::Ref; @@ -16,12 +17,12 @@ struct RTTIHelper sol::function ResolveFunction(const std::string& acFuncName); sol::function ResolveFunction(RED4ext::CClass* apClass, const std::string& acFuncName, bool aIsMember); - + RED4ext::ScriptInstance ResolveHandle(RED4ext::CBaseFunction* apFunc, sol::variadic_args& aArgs, uint64_t& aArgOffset) const; sol::variadic_results ExecuteFunction(RED4ext::CBaseFunction* apFunc, RED4ext::ScriptInstance apHandle, sol::variadic_args aLuaArgs, uint64_t aLuaArgOffset, std::string& aErrorMessage, bool aAllowNull = false) const; - + RED4ext::ScriptInstance NewInstance(RED4ext::CBaseRTTIType* apType, sol::optional aProps, TiltedPhoques::Allocator* apAllocator) const; sol::object NewInstance(RED4ext::CBaseRTTIType* apType, sol::optional aProps) const; @@ -35,14 +36,14 @@ struct RTTIHelper RED4ext::CBaseFunction* FindFunction(RED4ext::CClass* apClass, const uint64_t acFullNameHash) const; std::map FindFunctions(const uint64_t acShortNameHash) const; std::map FindFunctions(RED4ext::CClass* apClass, const uint64_t acShortNameHash, bool aIsMember) const; - - static void Initialize(const LockableState& acLua); + + static void Initialize(const LockableState& acpLua, LuaSandbox& apSandbox); static void Shutdown(); static RTTIHelper& Get(); private: - RTTIHelper(const LockableState& acLua); + RTTIHelper(const LockableState& acLua, LuaSandbox& apSandbox); void InitializeRTTI(); void ParseGlobalStatics(); @@ -55,7 +56,7 @@ struct RTTIHelper void AddResolvedFunction(const uint64_t acClassHash, const uint64_t acFuncHash, sol::function& acFunc, bool aIsMember); sol::function MakeInvokableFunction(RED4ext::CBaseFunction* apFunc); - sol::function MakeInvokableOverload(std::map aOverloadedFuncs); + sol::function MakeInvokableOverload(std::map aOverloadedFuncs) const; RED4ext::ScriptInstance NewPlaceholder(RED4ext::CBaseRTTIType* apType, TiltedPhoques::Allocator* apAllocator) const; bool IsClassReferenceType(RED4ext::CClass* apClass) const; @@ -79,10 +80,11 @@ struct RTTIHelper }; LockableState m_lua; - RED4ext::CRTTISystem* m_pRtti; - RED4ext::CClass* m_pGameInstanceType; - ScriptGameInstance* m_pGameInstance; - RED4ext::ScriptInstance m_pPlayerSystem; + RED4ext::CRTTISystem* m_pRtti{ nullptr }; + RED4ext::CClass* m_pGameInstanceType{ nullptr }; + ScriptGameInstance* m_pGameInstance{ nullptr }; + RED4ext::ScriptInstance m_pPlayerSystem{ nullptr }; RedFunctionMap m_extendedFunctions; LuaFunctionMap m_resolvedFunctions[2]; + LuaSandbox& m_sandbox; }; diff --git a/src/reverse/RTTILocator.h b/src/reverse/RTTILocator.h index 4fecdc5f..9c237672 100644 --- a/src/reverse/RTTILocator.h +++ b/src/reverse/RTTILocator.h @@ -9,5 +9,5 @@ struct RTTILocator private: const RED4ext::CName m_name; - RED4ext::CBaseRTTIType* m_pRtti; + RED4ext::CBaseRTTIType* m_pRtti = nullptr; }; diff --git a/src/reverse/RTTIMapper.cpp b/src/reverse/RTTIMapper.cpp index bd866350..547f01ba 100644 --- a/src/reverse/RTTIMapper.cpp +++ b/src/reverse/RTTIMapper.cpp @@ -3,16 +3,15 @@ #include "RTTIMapper.h" #include "RTTIHelper.h" #include "BasicTypes.h" -#include "Type.h" #include "Enum.h" #include "EnumStatic.h" #include "ClassStatic.h" #include "scripting/Scripting.h" #include "Utils.h" -RTTIMapper::RTTIMapper(const LockableState& acLua, const std::string& acGlobal) - : m_lua(acLua) - , m_global(acGlobal) +RTTIMapper::RTTIMapper(const LockableState& acpLua, LuaSandbox& apSandbox) + : m_lua(acpLua) + , m_sandbox(apSandbox) { } @@ -23,19 +22,18 @@ RTTIMapper::~RTTIMapper() void RTTIMapper::Register() { - RTTIHelper::Initialize(m_lua); + RTTIHelper::Initialize(m_lua, m_sandbox); auto lockedState = m_lua.Lock(); auto& luaState = lockedState.Get(); - auto luaGlobal = luaState.get(m_global); auto* pRtti = RED4ext::CRTTISystem::Get(); - RegisterSimpleTypes(luaState, luaGlobal); - RegisterDirectTypes(luaState, luaGlobal, pRtti); - RegisterDirectGlobals(luaGlobal, pRtti); - RegisterScriptAliases(luaGlobal, pRtti); - RegisterSpecialAccessors(luaState, luaGlobal); + RegisterSimpleTypes(luaState, m_sandbox.GetEnvironment()); + RegisterDirectTypes(luaState, m_sandbox.GetEnvironment(), pRtti); + RegisterDirectGlobals(m_sandbox.GetEnvironment(), pRtti); + RegisterScriptAliases(m_sandbox.GetEnvironment(), pRtti); + RegisterSpecialAccessors(luaState, m_sandbox.GetEnvironment()); } void RTTIMapper::Refresh() @@ -43,23 +41,22 @@ void RTTIMapper::Refresh() auto lockedState = m_lua.Lock(); auto& luaState = lockedState.Get(); - auto luaGlobal = luaState.get(m_global); + sol::table luaGlobal = luaState.globals(); auto* pRtti = RED4ext::CRTTISystem::Get(); RegisterDirectTypes(luaState, luaGlobal, pRtti); RegisterDirectGlobals(luaGlobal, pRtti); } -void RTTIMapper::RegisterSimpleTypes(sol::state& aLuaState, sol::table& aLuaGlobal) +void RTTIMapper::RegisterSimpleTypes(sol::state& aLuaState, sol::table& aLuaGlobal) const { - aLuaState.new_usertype("Variant", + aLuaGlobal.new_usertype("Variant", sol::meta_function::construct, sol::no_constructor); - - MakeSolUsertypeImmutable(aLuaState["Variant"], aLuaState); + MakeSolUsertypeImmutable(aLuaGlobal["Variant"], aLuaState); aLuaGlobal["ToVariant"] = sol::overload( - [this](Type& aInstance, sol::this_state aState) -> sol::object { - auto* pType = aInstance.GetType(); + [](const Type& aInstance, sol::this_state aState) -> sol::object { + const auto* pType = aInstance.GetType(); auto* pValue = aInstance.GetValuePtr(); if (!pType || !pValue) @@ -68,22 +65,16 @@ void RTTIMapper::RegisterSimpleTypes(sol::state& aLuaState, sol::table& aLuaGlob return sol::nil; } - auto luaLock = m_lua.Lock(); - auto& luaState = luaLock.Get(); - - return sol::object(luaState, sol::in_place, Variant(pType, pValue)); + return {aState, sol::in_place, Variant(pType, pValue)}; }, - [this](Enum& aEnum) -> sol::object { + [](const Enum& aEnum, sol::this_state aState) -> sol::object { RED4ext::CStackType data; data.type = const_cast(aEnum.GetType()); data.value = const_cast(aEnum.GetValuePtr()); - auto luaLock = m_lua.Lock(); - auto& luaState = luaLock.Get(); - - return sol::object(luaState, sol::in_place, Variant(data)); + return {aState, sol::in_place, Variant(data)}; }, - [this](sol::object aValue, const std::string& aTypeName, sol::this_state aState) -> sol::object { + [](sol::object aValue, const std::string& aTypeName, sol::this_state aState) -> sol::object { auto* pType = RED4ext::CRTTISystem::Get()->GetType(aTypeName.c_str()); if (!pType) @@ -97,13 +88,10 @@ void RTTIMapper::RegisterSimpleTypes(sol::state& aLuaState, sol::table& aLuaGlob Scripting::ToRED(aValue, data); - auto luaLock = m_lua.Lock(); - auto& luaState = luaLock.Get(); - - return sol::object(luaState, sol::in_place, std::move(variant)); + return {aState, sol::in_place, std::move(variant)}; }, - [this](sol::object aValue, sol::this_state aState) -> sol::object { - RED4ext::CName typeName = TryResolveTypeName(aValue); + [](sol::object aValue, sol::this_state aState) -> sol::object { + const RED4ext::CName typeName = TryResolveTypeName(aValue); if (typeName.IsNone()) { @@ -124,14 +112,11 @@ void RTTIMapper::RegisterSimpleTypes(sol::state& aLuaState, sol::table& aLuaGlob Scripting::ToRED(aValue, data); - auto luaLock = m_lua.Lock(); - auto& luaState = luaLock.Get(); - - return sol::object(luaState, sol::in_place, std::move(variant)); + return {aState, sol::in_place, std::move(variant)}; } ); - aLuaGlobal["FromVariant"] = [this](Variant& aVariant) -> sol::object { + aLuaGlobal["FromVariant"] = [this](const Variant& aVariant) -> sol::object { if (aVariant.IsEmpty()) return sol::nil; @@ -147,25 +132,24 @@ void RTTIMapper::RegisterSimpleTypes(sol::state& aLuaState, sol::table& aLuaGlob void RTTIMapper::RegisterDirectTypes(sol::state& aLuaState, sol::table& aLuaGlobal, RED4ext::CRTTISystem* apRtti) { - bool reinit = aLuaState["EnumStatic"] != sol::nil; + const bool reinit = aLuaGlobal["EnumStatic"] != sol::nil; if (!reinit) { - aLuaState.new_usertype("EnumStatic", + aLuaGlobal.new_usertype("EnumStatic", sol::meta_function::construct, sol::no_constructor, sol::base_classes, sol::bases(), sol::meta_function::index, &EnumStatic::Index, sol::meta_function::new_index, &EnumStatic::NewIndex); + MakeSolUsertypeImmutable(aLuaGlobal["EnumStatic"], aLuaState); - aLuaState.new_usertype("ClassStatic", + aLuaGlobal.new_usertype("ClassStatic", sol::meta_function::construct, sol::no_constructor, sol::base_classes, sol::bases(), sol::meta_function::index, &ClassStatic::Index, sol::meta_function::new_index, &ClassStatic::NewIndex, - "new", sol::property(&ClassStatic::GetFactory)); - - MakeSolUsertypeImmutable(aLuaState["EnumStatic"], aLuaState); - MakeSolUsertypeImmutable(aLuaState["ClassStatic"], aLuaState); + "new", property(&ClassStatic::GetFactory)); + MakeSolUsertypeImmutable(aLuaGlobal["ClassStatic"], aLuaState); } apRtti->types.for_each([&](RED4ext::CName aTypeName, RED4ext::CBaseRTTIType* apType) { @@ -179,7 +163,6 @@ void RTTIMapper::RegisterDirectTypes(sol::state& aLuaState, sol::table& aLuaGlob { case RED4ext::ERTTIType::Enum: { - auto* pEnum = reinterpret_cast(apType); auto luaEnum = make_object(aLuaState, EnumStatic(m_lua, apType)); MakeSolUsertypeImmutable(luaEnum, aLuaState); @@ -189,7 +172,6 @@ void RTTIMapper::RegisterDirectTypes(sol::state& aLuaState, sol::table& aLuaGlob } case RED4ext::ERTTIType::Class: { - auto* pClass = reinterpret_cast(apType); auto luaClass = make_object(aLuaState, ClassStatic(m_lua, apType)); MakeSolUsertypeImmutable(luaClass, aLuaState); @@ -203,15 +185,14 @@ void RTTIMapper::RegisterDirectTypes(sol::state& aLuaState, sol::table& aLuaGlob void RTTIMapper::RegisterDirectGlobals(sol::table& aLuaGlobal, RED4ext::CRTTISystem* apRtti) { - apRtti->funcs.for_each([&aLuaGlobal](RED4ext::CName aOrigName, RED4ext::CGlobalFunction* apFunc) { + apRtti->funcs.for_each([&aLuaGlobal](RED4ext::CName, RED4ext::CGlobalFunction* apFunc) { const std::string cShortName = apFunc->shortName.ToString(); - const FuncFlags cFlags = *(FuncFlags*)(&apFunc->flags); - if (aLuaGlobal[cShortName] == sol::nil && !cFlags.isExec) + if (aLuaGlobal[cShortName] == sol::nil && !apFunc->flags.isExec) { const std::string cFullName = apFunc->fullName.ToString(); const auto cIsClassFunc = cFullName.find("::") != std::string::npos; - const auto cIsOperatorFunc = cShortName.find(";") != std::string::npos; + const auto cIsOperatorFunc = cShortName.find(';') != std::string::npos; if (!cIsClassFunc && !cIsOperatorFunc) { @@ -228,28 +209,25 @@ void RTTIMapper::RegisterScriptAliases(sol::table& aLuaGlobal, RED4ext::CRTTISys }); } -void RTTIMapper::RegisterSpecialAccessors(sol::state& aLuaState, sol::table& aLuaGlobal) +void RTTIMapper::RegisterSpecialAccessors(sol::state& aLuaState, sol::table& aLuaGlobal) const { - // Replace RTTI version of class with our own version - aLuaGlobal["Vector3"] = aLuaState["Vector3"]; - + // Add global alias `Game.GetSystemRequestsHandler()` + // Replacement for `GetSingleton("inkMenuScenario"):GetSystemRequestsHandler()` + RTTIHelper::Get().AddFunctionAlias("GetSystemRequestsHandler", "inkMenuScenario", "GetSystemRequestsHandler"); + // Merge RTTI versions of basic types with our own versions // Allows usertype and RTTI functions to be used under the same name ExtendUsertype("Vector4", aLuaState, aLuaGlobal); ExtendUsertype("EulerAngles", aLuaState, aLuaGlobal); ExtendUsertype("Quaternion", aLuaState, aLuaGlobal); ExtendUsertype("ItemID", aLuaState, aLuaGlobal); - - // Add global alias `Game.GetSystemRequestsHandler()` - // Replacement for `GetSingleton("inkMenuScenario"):GetSystemRequestsHandler()` - RTTIHelper::Get().AddFunctionAlias("GetSystemRequestsHandler", "inkMenuScenario", "GetSystemRequestsHandler"); } template -void RTTIMapper::ExtendUsertype(const std::string acTypeName, sol::state& aLuaState, sol::table& aLuaGlobal) +void RTTIMapper::ExtendUsertype(const std::string acTypeName, sol::state& aLuaState, sol::table& aLuaGlobal) const { auto& classStatic = aLuaGlobal.get(acTypeName); - auto classIndexer = [&classStatic](const sol::object& acSelf, const std::string& acName, sol::this_environment aEnv) { + auto classIndexer = [&classStatic](const sol::object&, const std::string& acName, sol::this_environment aEnv) { return classStatic.Index(acName, aEnv); }; @@ -263,7 +241,7 @@ void RTTIMapper::ExtendUsertype(const std::string acTypeName, sol::state& aLuaSt void RTTIMapper::SanitizeName(std::string& aName) { - std::replace(aName.begin(), aName.end(), '.', '_'); + std::ranges::replace(aName, '.', '_'); } RED4ext::CName RTTIMapper::TryResolveTypeName(sol::object aValue) @@ -282,8 +260,8 @@ RED4ext::CName RTTIMapper::TryResolveTypeName(sol::object aValue) case sol::type::userdata: { sol::userdata userdata = aValue; - sol::table metatable = userdata[sol::metatable_key]; - std::string name = metatable.raw_get_or("__name", ""); + const sol::table metatable = userdata[sol::metatable_key]; + const auto name = metatable.raw_get_or("__name", ""); if (!name.empty()) return name.c_str() + 4; @@ -295,9 +273,9 @@ RED4ext::CName RTTIMapper::TryResolveTypeName(sol::object aValue) { sol::table table = aValue; - if (table.size() > 0) + if (!table.empty()) { - RED4ext::CName innerType = TryResolveTypeName(table[1]); + const RED4ext::CName innerType = TryResolveTypeName(table[1]); if (!innerType.IsNone()) return std::string("array:").append(innerType.ToString()).c_str(); @@ -307,5 +285,5 @@ RED4ext::CName RTTIMapper::TryResolveTypeName(sol::object aValue) } } - return RED4ext::CName(); + return {}; } diff --git a/src/reverse/RTTIMapper.h b/src/reverse/RTTIMapper.h index 9676b788..7c29f1cb 100644 --- a/src/reverse/RTTIMapper.h +++ b/src/reverse/RTTIMapper.h @@ -1,12 +1,11 @@ #pragma once -#include "ClassStatic.h" - +struct LuaSandbox; struct RTTIMapper { using LockableState = TiltedPhoques::Lockable::Ref; - RTTIMapper(const LockableState& acLua, const std::string& acGlobal); + RTTIMapper(const LockableState& acpLua, LuaSandbox& apSandbox); ~RTTIMapper(); void Register(); @@ -39,15 +38,15 @@ struct RTTIMapper }; RED4EXT_ASSERT_SIZE(FuncFlags, 0x4); - void RegisterSimpleTypes(sol::state& aLuaState, sol::table& aLuaGlobal); + void RegisterSimpleTypes(sol::state& aLuaState, sol::table& aLuaGlobal) const; void RegisterDirectTypes(sol::state& aLuaState, sol::table& aLuaGlobal, RED4ext::CRTTISystem* apRtti); void RegisterDirectGlobals(sol::table& aLuaGlobal, RED4ext::CRTTISystem* apRtti); void RegisterScriptAliases(sol::table& aLuaGlobal, RED4ext::CRTTISystem* apRtti); - void RegisterSpecialAccessors(sol::state& aLuaState, sol::table& aLuaGlobal); + void RegisterSpecialAccessors(sol::state& aLuaState, sol::table& aLuaGlobal) const; template - void ExtendUsertype(const std::string acTypeName, sol::state& aLuaState, sol::table& aLuaGlobal); + void ExtendUsertype(const std::string acTypeName, sol::state& aLuaState, sol::table& aLuaGlobal) const; LockableState m_lua; - std::string m_global; + LuaSandbox& m_sandbox; }; diff --git a/src/reverse/RenderContext.h b/src/reverse/RenderContext.h index 52df5012..e2f08222 100644 --- a/src/reverse/RenderContext.h +++ b/src/reverse/RenderContext.h @@ -4,7 +4,7 @@ struct RenderContext { struct Device { - IDXGISwapChain* pSwapChain; + IDXGISwapChain4* pSwapChain; uint8_t pad8[0x90 - 0x8]; }; diff --git a/src/reverse/ResourceAsyncReference.cpp b/src/reverse/ResourceAsyncReference.cpp index a94b239d..5860ecde 100644 --- a/src/reverse/ResourceAsyncReference.cpp +++ b/src/reverse/ResourceAsyncReference.cpp @@ -7,7 +7,7 @@ ResourceAsyncReference::ResourceAsyncReference(const TiltedPhoques::Locked& aView, RED4ext::CBaseRTTIType* apType, RED4ext::ResourceAsyncReference* apReference) - : ClassType(aView, reinterpret_cast(apType)->innerType) + : ClassType(aView, reinterpret_cast(apType)->innerType) , m_reference(*apReference) { } @@ -20,7 +20,7 @@ uint64_t ResourceAsyncReference::Hash(const std::string& aPath) // 2) / becomes \ // 3) /\/\\ becomes \ - return RED4ext::FNV1a(aPath.c_str()); + return RED4ext::FNV1a64(aPath.c_str()); } RED4ext::ScriptInstance ResourceAsyncReference::GetHandle() const @@ -39,9 +39,9 @@ uint64_t ResourceAsyncReference::GetHash() const } // Manual uint64 to Lua conversion because sol + LuaJIT can't do it -sol::object ResourceAsyncReference::GetLuaHash() +sol::object ResourceAsyncReference::GetLuaHash() const { - static RTTILocator s_uint64Type{RED4ext::FNV1a("Uint64")}; + static RTTILocator s_uint64Type{RED4ext::FNV1a64("Uint64")}; auto lockedState = m_lua.Lock(); diff --git a/src/reverse/ResourceAsyncReference.h b/src/reverse/ResourceAsyncReference.h index 16995cf3..900e9186 100644 --- a/src/reverse/ResourceAsyncReference.h +++ b/src/reverse/ResourceAsyncReference.h @@ -4,16 +4,16 @@ struct ResourceAsyncReference : ClassType { - ResourceAsyncReference(const TiltedPhoques::Locked& aView, + ResourceAsyncReference(const TiltedPhoques::Locked& aView, RED4ext::CBaseRTTIType* apType, RED4ext::ResourceAsyncReference* apReference); static uint64_t Hash(const std::string& aPath); - virtual RED4ext::ScriptInstance GetHandle() const override; - virtual RED4ext::ScriptInstance GetValuePtr() const override; + RED4ext::ScriptInstance GetHandle() const override; + RED4ext::ScriptInstance GetValuePtr() const override; uint64_t GetHash() const; - sol::object GetLuaHash(); + sol::object GetLuaHash() const; private: RED4ext::ResourceAsyncReference m_reference; diff --git a/src/reverse/SingletonReference.cpp b/src/reverse/SingletonReference.cpp index 339edec8..148a9456 100644 --- a/src/reverse/SingletonReference.cpp +++ b/src/reverse/SingletonReference.cpp @@ -12,7 +12,7 @@ SingletonReference::~SingletonReference() = default; RED4ext::ScriptInstance SingletonReference::GetHandle() const { - auto* engine = RED4ext::CGameEngine::Get(); + const auto* engine = RED4ext::CGameEngine::Get(); auto* pGameInstance = engine->framework->gameInstance; return pGameInstance->GetInstance(m_pType); diff --git a/src/reverse/SingletonReference.h b/src/reverse/SingletonReference.h index 71401c36..8c924164 100644 --- a/src/reverse/SingletonReference.h +++ b/src/reverse/SingletonReference.h @@ -6,7 +6,7 @@ struct SingletonReference : ClassType { SingletonReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass); - ~SingletonReference(); + ~SingletonReference() override; protected: RED4ext::ScriptInstance GetHandle() const override; diff --git a/src/reverse/StrongReference.cpp b/src/reverse/StrongReference.cpp index f15faad8..ade28807 100644 --- a/src/reverse/StrongReference.cpp +++ b/src/reverse/StrongReference.cpp @@ -5,7 +5,7 @@ #include "CET.h" -static RTTILocator s_sIScriptableType{RED4ext::FNV1a("IScriptable")}; +static RTTILocator s_sIScriptableType{RED4ext::FNV1a64("IScriptable")}; StrongReference::StrongReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::Handle aStrongHandle) @@ -20,7 +20,7 @@ StrongReference::StrongReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::Handle aStrongHandle, - RED4ext::CHandle* apStrongHandleType) + RED4ext::CRTTIHandleType* apStrongHandleType) : ClassType(aView, nullptr) , m_strongHandle(std::move(aStrongHandle)) { diff --git a/src/reverse/StrongReference.h b/src/reverse/StrongReference.h index 1edfd803..15cddc1f 100644 --- a/src/reverse/StrongReference.h +++ b/src/reverse/StrongReference.h @@ -7,18 +7,18 @@ struct StrongReference : ClassType StrongReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::Handle aStrongHandle); StrongReference(const TiltedPhoques::Lockable::Ref& aView, - RED4ext::Handle aStrongHandle, - RED4ext::CHandle* apStrongHandleType); - virtual ~StrongReference(); + RED4ext::Handle aStrongHandle, + RED4ext::CRTTIHandleType* apStrongHandleType); + ~StrongReference() override; protected: - virtual RED4ext::ScriptInstance GetHandle() const override; - virtual RED4ext::ScriptInstance GetValuePtr() const override; - + RED4ext::ScriptInstance GetHandle() const override; + RED4ext::ScriptInstance GetValuePtr() const override; + private: friend struct Scripting; friend struct TweakDB; - + RED4ext::Handle m_strongHandle; }; diff --git a/src/reverse/TweakDB/FlatPool.cpp b/src/reverse/TweakDB/FlatPool.cpp index 160aa8e5..9371e666 100644 --- a/src/reverse/TweakDB/FlatPool.cpp +++ b/src/reverse/TweakDB/FlatPool.cpp @@ -141,7 +141,7 @@ RED4ext::ScriptInstance FlatPool::GetValuePtr(int32_t aOffset) void FlatPool::Initialize() { - uintptr_t offsetEnd = m_tweakDb->flatDataBufferEnd - m_tweakDb->flatDataBuffer; + const uintptr_t offsetEnd = m_tweakDb->flatDataBufferEnd - m_tweakDb->flatDataBuffer; if (m_offsetEnd == offsetEnd) { @@ -149,7 +149,7 @@ void FlatPool::Initialize() return; } - std::shared_lock flatLockR(m_tweakDb->mutex00); + std::shared_lock flatLockR(m_tweakDb->mutex00); constexpr auto FlatVFTSize = 8u; constexpr auto FlatAlignment = 8u; @@ -228,11 +228,11 @@ uint64_t FlatPool::Hash(const RED4ext::CBaseRTTIType* aType, RED4ext::ScriptInst if (aType->GetType() == RED4ext::ERTTIType::Array) { auto* arrayType = reinterpret_cast(aType); - auto* innerType = arrayType->GetInnerType(); + const auto* innerType = arrayType->GetInnerType(); if (innerType->GetName() == "String") { - const auto* array = reinterpret_cast*>(aValue); + const auto* array = static_cast*>(aValue); hash = RED4ext::FNV1a64(reinterpret_cast(0), 0); // Initial seed for (uint32_t i = 0; i != array->size; ++i) { @@ -244,19 +244,19 @@ uint64_t FlatPool::Hash(const RED4ext::CBaseRTTIType* aType, RED4ext::ScriptInst } else { - const auto* array = reinterpret_cast*>(aValue); + const auto* array = static_cast*>(aValue); hash = RED4ext::FNV1a64(array->entries, array->size * innerType->GetSize()); } } else if (aType->GetName() == "String") { - const auto* str = reinterpret_cast(aValue); + const auto* str = static_cast(aValue); const auto* data = reinterpret_cast(str->c_str()); hash = RED4ext::FNV1a64(data, str->Length()); } else { - const auto* data = reinterpret_cast(aValue); + const auto* data = static_cast(aValue); hash = RED4ext::FNV1a64(data, aType->GetSize()); } diff --git a/src/reverse/TweakDB/FlatPool.h b/src/reverse/TweakDB/FlatPool.h index cc4c5552..bb067b48 100644 --- a/src/reverse/TweakDB/FlatPool.h +++ b/src/reverse/TweakDB/FlatPool.h @@ -2,15 +2,6 @@ #include -template<> -struct std::hash -{ - std::size_t operator()(RED4ext::CName aKey) const - { - return static_cast(aKey.hash); - } -}; - class FlatPool { public: diff --git a/src/reverse/TweakDB/ResourcesList.cpp b/src/reverse/TweakDB/ResourcesList.cpp new file mode 100644 index 00000000..eb052f8f --- /dev/null +++ b/src/reverse/TweakDB/ResourcesList.cpp @@ -0,0 +1,118 @@ +#include + +#include "ResourcesList.h" + +#include +#include + +using TOodleLZ_Decompress = size_t(*)(char *in, int insz, char *out, int outsz, int wantsFuzzSafety, int b, int c, void *d, void *e, void *f, void *g, void *workBuffer, size_t workBufferSize, int j); + +ResourcesList::Resource::Resource(std::string aName) noexcept + : m_isFiltered(false) + , m_name(std::move(aName)) + , m_hash(RED4ext::FNV1a64(m_name.c_str())) +{ +} + +ResourcesList* ResourcesList::Get() +{ + static ResourcesList instance; + return &instance; +} + +bool ResourcesList::Initialize() +{ + Reset(); + + // TODO - share decompression routine with TweakDBMetadata + + auto hOodleHandle = GetModuleHandle(TEXT("oo2ext_7_win64.dll")); + if (hOodleHandle == nullptr) + { + spdlog::error("Could not get Oodle access"); + return false; + } + + auto OodleLZ_Decompress = reinterpret_cast(GetProcAddress(hOodleHandle, "OodleLZ_Decompress")); + if (OodleLZ_Decompress == nullptr) + { + spdlog::error("Could not get OodleLZ_Decompress"); + return false; + } + + auto filepath = GetAbsolutePath(c_defaultFilename, CET::Get().GetPaths().TweakDB(), false, true); + if (!exists(filepath)) + return false; + + m_resources.reserve(1485150); + + try + { + std::ifstream file(filepath, std::ios::binary); + file.exceptions(std::ios::badbit); + + size_t headerSize = 8; + + std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + std::string buffer; + buffer.resize(*reinterpret_cast(content.data() + 4)); + + char workingMemory[0x80000]; + auto size = OodleLZ_Decompress(content.data() + headerSize, static_cast(content.size() - headerSize), buffer.data(), + static_cast(buffer.size()), 1, 1, 0, nullptr, nullptr, nullptr, nullptr, workingMemory, std::size(workingMemory), 3); + + assert(size == buffer.size()); + if (size != buffer.size()) + { + spdlog::error("Decompress failed!"); + return false; + } + + std::istringstream iss(buffer); + + std::string filename; + while (std::getline(iss, filename)) + { + filename.resize(filename.size() - 1); + m_resources.emplace_back(filename); + } + + for (auto& resource : m_resources) + { + m_resourcesByHash.emplace(resource.m_hash, &resource); + } + + return m_isInitialized = true; + } + // this is easier for now + catch (std::exception&) + { + return m_isInitialized = false; + } +} + +bool ResourcesList::IsInitialized() const +{ + return m_isInitialized; +} + +const std::string& ResourcesList::Resolve(uint64_t aHash) +{ + static std::string defaultName = "ERROR_UNKNOWN_RESOURCE"; + + const auto it = m_resourcesByHash.find(aHash); + + return it == m_resourcesByHash.end() ? defaultName : it->second->m_name; +} + +TiltedPhoques::Vector& ResourcesList::GetResources() +{ + return m_resources; +} + +void ResourcesList::Reset() +{ + m_isInitialized = false; + m_resources.clear(); + m_resourcesByHash.clear(); +} diff --git a/src/reverse/TweakDB/ResourcesList.h b/src/reverse/TweakDB/ResourcesList.h new file mode 100644 index 00000000..fcd67ea2 --- /dev/null +++ b/src/reverse/TweakDB/ResourcesList.h @@ -0,0 +1,36 @@ +#pragma once + +struct ResourcesList +{ + inline static const std::string c_defaultFilename = "usedhashes.kark"; + + struct Resource + { + bool m_isFiltered; + std::string m_name; + uint64_t m_hash; + + Resource(std::string aName) noexcept; + + Resource(Resource&&) noexcept = default; + Resource& operator=(Resource&&) noexcept = default; + }; + + static ResourcesList* Get(); + + bool Initialize(); + + bool IsInitialized() const; + + const std::string& Resolve(uint64_t aHash); + + TiltedPhoques::Vector& GetResources(); + +protected: + void Reset(); + +private: + std::atomic_bool m_isInitialized = false; + TiltedPhoques::Vector m_resources; + TiltedPhoques::Map m_resourcesByHash; +}; diff --git a/src/reverse/TweakDB/TweakDB.cpp b/src/reverse/TweakDB/TweakDB.cpp index 1db7450c..7c4e80fe 100644 --- a/src/reverse/TweakDB/TweakDB.cpp +++ b/src/reverse/TweakDB/TweakDB.cpp @@ -1,5 +1,6 @@ #include -#include + +#include "TweakDB.h" #include #include @@ -7,11 +8,6 @@ #include #include -#include - -#include "FlatPool.h" -#include "TweakDB.h" - using namespace std::chrono_literals; std::mutex TweakDB::s_mutex; @@ -22,17 +18,15 @@ TweakDB::TweakDB(const TiltedPhoques::Lockable : m_lua(aLua) { if (!s_flatPool) - { s_flatPool = TiltedPhoques::MakeUnique(); - } } void TweakDB::DebugStats() { auto* pTDB = RED4ext::TweakDB::Get(); - std::shared_lock _1(pTDB->mutex00); - std::shared_lock _2(pTDB->mutex01); - auto logger = spdlog::get("scripting"); // DebugStats should always log to console + std::shared_lock _1(pTDB->mutex00); + std::shared_lock _2(pTDB->mutex01); + const auto logger = spdlog::get("scripting"); // DebugStats should always log to console logger->info("flats: {}", pTDB->flats.size); logger->info("records: {}", pTDB->recordsByID.size); @@ -42,11 +36,11 @@ void TweakDB::DebugStats() logger->info("created records: {}", s_createdRecords.size()); } -sol::object TweakDB::GetRecords(const std::string& acRecordTypeName) +sol::object TweakDB::GetRecords(const std::string& acRecordTypeName) const { static auto* pArrayType = RED4ext::CRTTISystem::Get()->GetType("array:handle:IScriptable"); auto* pTDB = RED4ext::TweakDB::Get(); - std::shared_lock _(pTDB->mutex01); + std::shared_lock _(pTDB->mutex01); auto* pRecordType = RED4ext::CRTTISystem::Get()->GetType(acRecordTypeName.c_str()); if (pRecordType == nullptr) @@ -61,12 +55,12 @@ sol::object TweakDB::GetRecords(const std::string& acRecordTypeName) return Scripting::ToLua(state, stackType); } -sol::object TweakDB::GetRecordByName(const std::string& acRecordName) +sol::object TweakDB::GetRecordByName(const std::string& acRecordName) const { - return std::move(GetRecord(TweakDBID(acRecordName))); + return GetRecord(TweakDBID(acRecordName)); } -sol::object TweakDB::GetRecord(TweakDBID aDBID) +sol::object TweakDB::GetRecord(TweakDBID aDBID) const { auto* pTDB = RED4ext::TweakDB::Get(); @@ -78,16 +72,16 @@ sol::object TweakDB::GetRecord(TweakDBID aDBID) return make_object(state.Get(), StrongReference(m_lua, std::move(record))); } -sol::object TweakDB::QueryByName(const std::string& acQueryName) +sol::object TweakDB::QueryByName(const std::string& acQueryName) const { - return std::move(Query(TweakDBID(acQueryName))); + return Query(TweakDBID(acQueryName)); } -sol::object TweakDB::Query(TweakDBID aDBID) +sol::object TweakDB::Query(TweakDBID aDBID) const { static auto* pArrayTweakDBIDType = RED4ext::CRTTISystem::Get()->GetType("array:TweakDBID"); auto* pTDB = RED4ext::TweakDB::Get(); - std::shared_lock _(pTDB->mutex01); + std::shared_lock _(pTDB->mutex01); RED4ext::DynArray queryResult; if (!pTDB->TryQuery(aDBID.value, queryResult)) @@ -98,12 +92,12 @@ sol::object TweakDB::Query(TweakDBID aDBID) return Scripting::ToLua(state, stackType); } -sol::object TweakDB::GetFlatByName(const std::string& acFlatName) +sol::object TweakDB::GetFlatByName(const std::string& acFlatName) const { - return std::move(GetFlat(TweakDBID(acFlatName))); + return GetFlat(TweakDBID(acFlatName)); } -sol::object TweakDB::GetFlat(TweakDBID aDBID) +sol::object TweakDB::GetFlat(TweakDBID aDBID) const { RED4ext::CStackType data = InternalGetFlat(aDBID.value); @@ -122,14 +116,14 @@ bool TweakDB::SetFlatsByName(const std::string& acRecordName, sol::table aTable, bool TweakDB::SetFlats(TweakDBID aDBID, sol::table aTable, sol::this_environment aThisEnv) { bool success = true; - TweakDBID prepDBID(aDBID, "."); + const TweakDBID prepDBID(aDBID, "."); for (auto& [key, value] : aTable) { if (!key.is()) continue; - TweakDBID flatDBID(prepDBID, key.as()); + const TweakDBID flatDBID(prepDBID, key.as()); success &= SetFlat(flatDBID, value, aThisEnv); } @@ -138,18 +132,18 @@ bool TweakDB::SetFlats(TweakDBID aDBID, sol::table aTable, sol::this_environment return success; } -bool TweakDB::SetFlatByName(const std::string& acFlatName, sol::object aObject, sol::this_environment aThisEnv) +bool TweakDB::SetFlatByName(const std::string& acFlatName, sol::object aObject, sol::this_environment aThisEnv) const { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); return SetOrCreateFlat(TweakDBID(acFlatName), std::move(aObject), acFlatName, "", logger); } -bool TweakDB::SetFlat(TweakDBID aDBID, sol::object aObject, sol::this_environment aThisEnv) +bool TweakDB::SetFlat(TweakDBID aDBID, sol::object aObject, sol::this_environment aThisEnv) const { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); return SetOrCreateFlat(aDBID, std::move(aObject), "", "", logger); } @@ -157,16 +151,15 @@ bool TweakDB::SetFlat(TweakDBID aDBID, sol::object aObject, sol::this_environmen bool TweakDB::SetFlatByNameAutoUpdate(const std::string& acFlatName, sol::object aObject, sol::this_environment aThisEnv) { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); - TweakDBID dbid(acFlatName); + const TweakDBID dbid(acFlatName); if (SetOrCreateFlat(dbid, std::move(aObject), acFlatName, "", logger)) { - uint64_t recordDBID = CET::Get().GetVM().GetTDBIDBase(dbid.value); + const uint64_t recordDBID = CET::Get().GetVM().GetTDBIDBase(dbid.value); if (recordDBID != 0) - { UpdateRecordByID(recordDBID); - } + return true; } @@ -176,39 +169,38 @@ bool TweakDB::SetFlatByNameAutoUpdate(const std::string& acFlatName, sol::object bool TweakDB::SetFlatAutoUpdate(TweakDBID aDBID, sol::object aObject, sol::this_environment aThisEnv) { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); if (SetOrCreateFlat(aDBID, std::move(aObject), "", "", logger)) { - uint64_t recordDBID = CET::Get().GetVM().GetTDBIDBase(aDBID.value); + const uint64_t recordDBID = CET::Get().GetVM().GetTDBIDBase(aDBID.value); if (recordDBID != 0) - { UpdateRecordByID(recordDBID); - } + return true; } return false; } -bool TweakDB::SetTypedFlatByName(const std::string& acFlatName, sol::object aObject, const std::string& acTypeName, sol::this_environment aThisEnv) +bool TweakDB::SetTypedFlatByName(const std::string& acFlatName, sol::object aObject, const std::string& acTypeName, sol::this_environment aThisEnv) const { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); return SetOrCreateFlat(TweakDBID(acFlatName), aObject, acFlatName, acTypeName, logger); } -bool TweakDB::SetTypedFlat(TweakDBID aDBID, sol::object aObject, const std::string& acTypeName, sol::this_environment aThisEnv) +bool TweakDB::SetTypedFlat(TweakDBID aDBID, sol::object aObject, const std::string& acTypeName, sol::this_environment aThisEnv) const { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); return SetOrCreateFlat(aDBID, aObject, "", acTypeName, logger); } bool TweakDB::SetOrCreateFlat(TweakDBID aDBID, sol::object aObject, const std::string& acFlatName, - const std::string& acTypeName, std::shared_ptr aLogger) + const std::string& acTypeName, const std::shared_ptr& aLogger) const { auto* pTDB = RED4ext::TweakDB::Get(); static thread_local TiltedPhoques::ScratchAllocator s_scratchMemory(1 << 22); @@ -225,13 +217,11 @@ bool TweakDB::SetOrCreateFlat(TweakDBID aDBID, sol::object aObject, const std::s RED4ext::CStackType data; { - std::shared_lock _(pTDB->mutex00); + std::shared_lock _(pTDB->mutex00); pFlat = pTDB->flats.Find(aDBID.value); if (pFlat != pTDB->flats.End()) - { data = s_flatPool->GetData(pFlat->ToTDBOffset()); - } } if (data.value) @@ -245,10 +235,11 @@ bool TweakDB::SetOrCreateFlat(TweakDBID aDBID, sol::object aObject, const std::s aLogger->info("[TweakDB::SetFlat] Failed to convert value for {}. Expecting: {}", flatName, data.type->GetName().ToString()); } + return false; } - int32_t newTDBOffset = s_flatPool->AllocateData(data); + const int32_t newTDBOffset = s_flatPool->AllocateData(data); if (newTDBOffset == FlatPool::InvalidOffset) { if (aLogger) @@ -256,11 +247,12 @@ bool TweakDB::SetOrCreateFlat(TweakDBID aDBID, sol::object aObject, const std::s const std::string& flatName = !acFlatName.empty() ? acFlatName : GetTDBIDString(aDBID.value); aLogger->info("[TweakDB::SetFlat] Failed to allocate flat value for {}", flatName); } + return false; } { - std::shared_lock _(pTDB->mutex00); + std::shared_lock _(pTDB->mutex00); pFlat = pTDB->flats.Find(aDBID.value); if (pFlat == pTDB->flats.End()) @@ -270,6 +262,7 @@ bool TweakDB::SetOrCreateFlat(TweakDBID aDBID, sol::object aObject, const std::s const std::string& flatName = !acFlatName.empty() ? acFlatName : GetTDBIDString(aDBID.value); aLogger->info("[TweakDB::SetFlat] Failed to update an existing flat {}", flatName); } + return false; } @@ -282,9 +275,7 @@ bool TweakDB::SetOrCreateFlat(TweakDBID aDBID, sol::object aObject, const std::s auto* pRTTI = RED4ext::CRTTISystem::Get(); if (!acTypeName.empty()) - { data.type = pRTTI->GetType(acTypeName.c_str()); - } else { const auto cTypeName = RTTIMapper::TryResolveTypeName(aObject); @@ -297,6 +288,7 @@ bool TweakDB::SetOrCreateFlat(TweakDBID aDBID, sol::object aObject, const std::s aLogger->info("[TweakDB::SetFlat] Type for {} is ambiguous, use third parameter to specify the type", flatName); } + return false; } @@ -306,18 +298,15 @@ bool TweakDB::SetOrCreateFlat(TweakDBID aDBID, sol::object aObject, const std::s if (data.type == nullptr) { if (aLogger) - { aLogger->info("[TweakDB::SetFlat] Unknown type"); - } + return false; } if (!s_flatPool->IsFlatType(data.type)) { if (aLogger) - { aLogger->info("[TweakDB::SetFlat] Unsupported type: {}", data.type->GetName().ToString()); - } return false; } @@ -333,7 +322,7 @@ bool TweakDB::SetOrCreateFlat(TweakDBID aDBID, sol::object aObject, const std::s return false; } - int32_t newTDBOffset = s_flatPool->AllocateData(data); + const int32_t newTDBOffset = s_flatPool->AllocateData(data); if (newTDBOffset == -1) { if (aLogger) @@ -358,9 +347,7 @@ bool TweakDB::SetOrCreateFlat(TweakDBID aDBID, sol::object aObject, const std::s } if (!acFlatName.empty()) - { CET::Get().GetVM().RegisterTDBIDString(aDBID.value, 0, acFlatName); - } return true; } @@ -373,6 +360,7 @@ bool TweakDB::UpdateRecordByName(const std::string& acRecordName) bool TweakDB::UpdateRecordByID(TweakDBID aDBID) { auto* pTDB = RED4ext::TweakDB::Get(); + return pTDB->UpdateRecord(aDBID.value); } @@ -381,17 +369,13 @@ bool TweakDB::UpdateRecord(sol::object aValue, sol::this_environment aThisEnv) auto* pTDB = RED4ext::TweakDB::Get(); const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); RED4ext::gamedataTweakDBRecord* pRecord; if (aValue.is()) - { - pRecord = reinterpret_cast(aValue.as()->GetHandle()); - } + pRecord = static_cast(aValue.as()->GetHandle()); else if (aValue.is()) - { - pRecord = reinterpret_cast(aValue.as()->GetHandle()); - } + pRecord = static_cast(aValue.as()->GetHandle()); else { logger->info("[TweakDB::UpdateRecord] Expecting handle or whandle"); @@ -401,42 +385,39 @@ bool TweakDB::UpdateRecord(sol::object aValue, sol::this_environment aThisEnv) return pTDB->UpdateRecord(pRecord); } -bool TweakDB::CreateRecord(const std::string& acRecordName, const std::string& acRecordTypeName, - sol::this_environment aThisEnv) +bool TweakDB::CreateRecord(const std::string& acRecordName, const std::string& acRecordTypeName, sol::this_environment aThisEnv) const { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); return InternalCreateRecord(acRecordName, acRecordTypeName, logger); } -bool TweakDB::CreateRecordToID(TweakDBID aDBID, const std::string& acRecordTypeName, - sol::this_environment aThisEnv) +bool TweakDB::CreateRecordToID(TweakDBID aDBID, const std::string& acRecordTypeName, sol::this_environment aThisEnv) const { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); return InternalCreateRecord(aDBID, acRecordTypeName, logger); } -bool TweakDB::CloneRecordByName(const std::string& acRecordName, const std::string& acClonedRecordName, - sol::this_environment aThisEnv) +bool TweakDB::CloneRecordByName(const std::string& acRecordName, const std::string& acClonedRecordName, sol::this_environment aThisEnv) const { return CloneRecord(acRecordName, TweakDBID(acClonedRecordName), std::move(aThisEnv)); } -bool TweakDB::CloneRecord(const std::string& acRecordName, TweakDBID aClonedRecordDBID, sol::this_environment aThisEnv) +bool TweakDB::CloneRecord(const std::string& acRecordName, TweakDBID aClonedRecordDBID, sol::this_environment aThisEnv) const { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); return InternalCloneRecord(acRecordName, aClonedRecordDBID.value, logger); } -bool TweakDB::CloneRecordToID(TweakDBID aDBID, TweakDBID aClonedRecordDBID, sol::this_environment aThisEnv) +bool TweakDB::CloneRecordToID(TweakDBID aDBID, TweakDBID aClonedRecordDBID, sol::this_environment aThisEnv) const { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); return InternalCloneRecord(aDBID, aClonedRecordDBID.value, logger); } @@ -444,7 +425,7 @@ bool TweakDB::CloneRecordToID(TweakDBID aDBID, TweakDBID aClonedRecordDBID, sol: bool TweakDB::DeleteRecord(const std::string& acRecordName, sol::this_environment aThisEnv) { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); return InternalDeleteRecord(RED4ext::TweakDBID(acRecordName), logger); } @@ -452,7 +433,7 @@ bool TweakDB::DeleteRecord(const std::string& acRecordName, sol::this_environmen bool TweakDB::DeleteRecordByID(TweakDBID aDBID, sol::this_environment aThisEnv) { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); return InternalDeleteRecord(RED4ext::TweakDBID(aDBID.name_hash, aDBID.name_length), logger); } @@ -465,7 +446,7 @@ RED4ext::CStackType TweakDB::InternalGetFlat(RED4ext::TweakDBID aDBID) auto* pTDB = RED4ext::TweakDB::Get(); std::shared_lock _(pTDB->mutex00); - auto* flat = pTDB->flats.Find(aDBID); + const auto* flat = pTDB->flats.Find(aDBID); if (flat == pTDB->flats.End()) return {}; @@ -477,18 +458,16 @@ int32_t TweakDB::InternalSetFlat(RED4ext::TweakDBID aDBID, const RED4ext::CStack { auto* pTDB = RED4ext::TweakDB::Get(); - int32_t newTDBOffset = s_flatPool->AllocateData(acStackType); + const int32_t newTDBOffset = s_flatPool->AllocateData(acStackType); if (newTDBOffset == FlatPool::InvalidOffset) return newTDBOffset; { - std::shared_lock _(pTDB->mutex00); + std::shared_lock _(pTDB->mutex00); auto* pDBID = pTDB->flats.Find(aDBID); if (pDBID == pTDB->flats.End()) - { return FlatPool::InvalidOffset; - } pDBID->SetTDBOffset(newTDBOffset); } @@ -497,134 +476,129 @@ int32_t TweakDB::InternalSetFlat(RED4ext::TweakDBID aDBID, const RED4ext::CStack } bool TweakDB::InternalCreateRecord(const std::string& acRecordName, const std::string& acRecordTypeName, - const std::shared_ptr& aLogger) + const std::shared_ptr& acpLogger) { auto* pTDB = RED4ext::TweakDB::Get(); auto* pType = RED4ext::CRTTISystem::Get()->GetType(acRecordTypeName.c_str()); RED4ext::Handle record; { - std::shared_lock _(pTDB->mutex01); + std::shared_lock _(pTDB->mutex01); RED4ext::DynArray> recordsOfSameType; if (!pTDB->TryGetRecordsByType(pType, recordsOfSameType) || recordsOfSameType.size == 0) { - if (aLogger) - { - aLogger->info("Failed to create record '{}'. reason: Unknown type '{}'", acRecordName, - acRecordTypeName); - } + if (acpLogger) + acpLogger->info("Failed to create record '{}'. reason: Unknown type '{}'", acRecordName, acRecordTypeName); + return false; } record = recordsOfSameType[0]; } - auto* pTweakRecord = reinterpret_cast(record.GetPtr()); - return InternalCloneRecord(acRecordName, pTweakRecord, false, aLogger); + const auto* pTweakRecord = reinterpret_cast(record.GetPtr()); + return InternalCloneRecord(acRecordName, pTweakRecord, false, acpLogger); } bool TweakDB::InternalCreateRecord(TweakDBID aDBID, const std::string& acRecordTypeName, - const std::shared_ptr& aLogger) + const std::shared_ptr& acpLogger) { auto* pTDB = RED4ext::TweakDB::Get(); auto* pType = RED4ext::CRTTISystem::Get()->GetType(acRecordTypeName.c_str()); RED4ext::Handle record; { - std::shared_lock _(pTDB->mutex01); + std::shared_lock _(pTDB->mutex01); RED4ext::DynArray> recordsOfSameType; if (!pTDB->TryGetRecordsByType(pType, recordsOfSameType) || recordsOfSameType.size == 0) { - if (aLogger) - { - aLogger->info("Failed to create record '{}'. reason: Unknown type '{}'", aDBID.ToString(), - acRecordTypeName); - } + if (acpLogger) + acpLogger->info("Failed to create record '{}'. reason: Unknown type '{}'", aDBID.ToString(), acRecordTypeName); + return false; } record = recordsOfSameType[0]; } - auto* pTweakRecord = reinterpret_cast(record.GetPtr()); - return InternalCloneRecord(aDBID, pTweakRecord, false, aLogger); + const auto* pTweakRecord = reinterpret_cast(record.GetPtr()); + return InternalCloneRecord(aDBID, pTweakRecord, false, acpLogger); } -bool TweakDB::InternalCloneRecord(const std::string& acRecordName, RED4ext::TweakDBID aClonedRecordDBID, - const std::shared_ptr& aLogger) +bool TweakDB::InternalCloneRecord(const std::string& acRecordName, RED4ext::TweakDBID aClonedRecordDBID, const std::shared_ptr& acpLogger) { auto* pTDB = RED4ext::TweakDB::Get(); RED4ext::Handle record; if (!pTDB->TryGetRecord(aClonedRecordDBID, record)) { - if (aLogger) - { - aLogger->info("Failed to create record '{}'. reason: Couldn't find record '{}' to clone", acRecordName, - GetTDBIDString(aClonedRecordDBID)); - } + if (acpLogger) + acpLogger->info("Failed to create record '{}'. reason: Couldn't find record '{}' to clone", acRecordName, GetTDBIDString(aClonedRecordDBID)); + return false; } - auto* pTweakRecord = reinterpret_cast(record.GetPtr()); - return InternalCloneRecord(acRecordName, pTweakRecord, true, aLogger); + const auto* pTweakRecord = reinterpret_cast(record.GetPtr()); + return InternalCloneRecord(acRecordName, pTweakRecord, true, acpLogger); } bool TweakDB::InternalCloneRecord(TweakDBID aDBID, RED4ext::TweakDBID aClonedRecordDBID, - const std::shared_ptr& aLogger) + const std::shared_ptr& acpLogger) { auto* pTDB = RED4ext::TweakDB::Get(); RED4ext::Handle record; if (!pTDB->TryGetRecord(aClonedRecordDBID, record)) { - if (aLogger) - { - aLogger->info("Failed to create record {}. reason: Couldn't find record '{}' to clone", aDBID.ToString(), - GetTDBIDString(aClonedRecordDBID)); - } + if (acpLogger) + acpLogger->info("Failed to create record {}. reason: Couldn't find record '{}' to clone", aDBID.ToString(), GetTDBIDString(aClonedRecordDBID)); + return false; } - auto* pTweakRecord = reinterpret_cast(record.GetPtr()); - return InternalCloneRecord(aDBID, pTweakRecord, true, aLogger); + const auto* pTweakRecord = reinterpret_cast(record.GetPtr()); + return InternalCloneRecord(aDBID, pTweakRecord, true, acpLogger); } bool TweakDB::InternalCloneRecord(const std::string& acRecordName, const RED4ext::gamedataTweakDBRecord* acClonedRecord, - bool cloneValues, const std::shared_ptr& aLogger) + bool cloneValues, const std::shared_ptr& acpLogger) { auto* pTDB = RED4ext::TweakDB::Get(); - RED4ext::TweakDBID recordDBID(acRecordName); + const RED4ext::TweakDBID recordDBID(acRecordName); if (!pTDB->CreateRecord(recordDBID, acClonedRecord->GetTweakBaseHash())) { - aLogger->info("Failed to create record '{}'. reason: Record already exists", acRecordName); + if (acpLogger) + acpLogger->info("Failed to create record '{}'. reason: Record already exists", acRecordName); + return false; } auto& vm = CET::Get().GetVM(); vm.RegisterTDBIDString(recordDBID, 0, acRecordName); - return InternalCloneFlats(recordDBID, acClonedRecord, cloneValues, aLogger); + return InternalCloneFlats(recordDBID, acClonedRecord, cloneValues, acpLogger); } bool TweakDB::InternalCloneRecord(TweakDBID aDBID, const RED4ext::gamedataTweakDBRecord* acClonedRecord, - bool cloneValues, const std::shared_ptr& aLogger) + bool cloneValues, const std::shared_ptr& acpLogger) { auto* pTDB = RED4ext::TweakDB::Get(); - RED4ext::TweakDBID recordDBID(aDBID.name_hash, aDBID.name_length); + const RED4ext::TweakDBID recordDBID(aDBID.name_hash, aDBID.name_length); if (!pTDB->CreateRecord(recordDBID, acClonedRecord->GetTweakBaseHash())) { - aLogger->info("Failed to create record {}. reason: Record already exists", aDBID.ToString()); + if (acpLogger) + acpLogger->info("Failed to create record {}. reason: Record already exists", aDBID.ToString()); + return false; } - return InternalCloneFlats(recordDBID, acClonedRecord, cloneValues, aLogger); + return InternalCloneFlats(recordDBID, acClonedRecord, cloneValues, acpLogger); } bool TweakDB::InternalCloneFlats(RED4ext::TweakDBID aDBID, const RED4ext::gamedataTweakDBRecord* acClonedRecord, - bool cloneValues, const std::shared_ptr& aLogger) + bool cloneValues, const std::shared_ptr& acpLogger) { auto* pTDB = RED4ext::TweakDB::Get(); auto& vm = CET::Get().GetVM(); @@ -636,7 +610,7 @@ bool TweakDB::InternalCloneFlats(RED4ext::TweakDBID aDBID, const RED4ext::gameda size_t lastCreatedFlatIdx; RED4ext::SortedUniqueArray recordFlats; - recordFlats.Reserve(recordFlatIDs.size()); + recordFlats.Reserve(static_cast(recordFlatIDs.size())); for (lastCreatedFlatIdx = 0; lastCreatedFlatIdx != recordFlatIDs.size(); ++lastCreatedFlatIdx) { @@ -647,29 +621,23 @@ bool TweakDB::InternalCloneFlats(RED4ext::TweakDBID aDBID, const RED4ext::gameda if (!data.value) { - if (aLogger) - { - aLogger->info("Failed to create record {}. reason: Couldn't find flat '<...>{}'", - TweakDBID(aDBID.value).ToString(), propertyName); - } + if (acpLogger) + acpLogger->info("Failed to create record {}. reason: Couldn't find flat '<...>{}'", TweakDBID(aDBID.value).ToString(), propertyName); + success = false; break; } if (cloneValues) - { flatID.SetTDBOffset(s_flatPool->AllocateData(data)); - } else - { flatID.SetTDBOffset(s_flatPool->AllocateDefault(data.type)); - } recordFlats.Insert(flatID); } { - std::unique_lock _(pTDB->mutex00); + std::lock_guard _(pTDB->mutex00); pTDB->flats.InsertOrAssign(recordFlats); } @@ -679,39 +647,36 @@ bool TweakDB::InternalCloneFlats(RED4ext::TweakDBID aDBID, const RED4ext::gameda vm.RemoveTDBIDDerivedFrom(aDBID); // Only RemoveFlat the ones we made for (size_t i = 0; i != lastCreatedFlatIdx; ++i) - { RemoveFlat(recordFlatIDs[i]); - } + return false; } pTDB->UpdateRecord(aDBID); - std::lock_guard _(s_mutex); + std::lock_guard _(s_mutex); s_createdRecords.emplace(aDBID); + return true; } -bool TweakDB::InternalDeleteRecord(RED4ext::TweakDBID aDBID, std::shared_ptr aLogger) +bool TweakDB::InternalDeleteRecord(RED4ext::TweakDBID aDBID, const std::shared_ptr& acpLogger) { auto* pTDB = RED4ext::TweakDB::Get(); if (!IsACreatedRecord(aDBID)) { - if (aLogger) - { - aLogger->info("Record '{}' couldn't be deleted. reason: Record not found", - GetTDBIDString(aDBID)); - } + if (acpLogger) + acpLogger->info("Record '{}' couldn't be deleted. reason: Record not found", GetTDBIDString(aDBID)); + return false; } if (!pTDB->RemoveRecord(aDBID)) { - if (aLogger) - { - aLogger->info("Record '{}' couldn't be deleted. reason: Unknown", GetTDBIDString(aDBID)); - } + if (acpLogger) + acpLogger->info("Record '{}' couldn't be deleted. reason: Unknown", GetTDBIDString(aDBID)); + return false; // shouldn't happen } @@ -725,7 +690,7 @@ bool TweakDB::InternalDeleteRecord(RED4ext::TweakDBID aDBID, std::shared_ptr _(s_mutex); + std::lock_guard _(s_mutex); s_createdRecords.erase(aDBID); return true; } @@ -738,8 +703,8 @@ bool TweakDB::RemoveFlat(RED4ext::TweakDBID aDBID) bool TweakDB::IsACreatedRecord(RED4ext::TweakDBID aDBID) { - std::lock_guard _(s_mutex); - return s_createdRecords.find(aDBID) != s_createdRecords.end(); + std::lock_guard _(s_mutex); + return s_createdRecords.contains(aDBID); } std::string TweakDB::GetTDBIDString(uint64_t aDBID) diff --git a/src/reverse/TweakDB/TweakDB.h b/src/reverse/TweakDB/TweakDB.h index eddd1ad0..6d22d2e9 100644 --- a/src/reverse/TweakDB/TweakDB.h +++ b/src/reverse/TweakDB/TweakDB.h @@ -13,70 +13,70 @@ struct TweakDB TweakDB(const TiltedPhoques::Lockable::Ref& aLua); void DebugStats(); - sol::object GetRecords(const std::string& acRecordTypeName); - sol::object GetRecordByName(const std::string& acRecordName); - sol::object GetRecord(TweakDBID aDBID); - sol::object QueryByName(const std::string& acQueryName); - sol::object Query(TweakDBID aDBID); - sol::object GetFlatByName(const std::string& acFlatName); - sol::object GetFlat(TweakDBID aDBID); + sol::object GetRecords(const std::string& acRecordTypeName) const; + sol::object GetRecordByName(const std::string& acRecordName) const; + sol::object GetRecord(TweakDBID aDBID) const; + sol::object QueryByName(const std::string& acQueryName) const; + sol::object Query(TweakDBID aDBID) const; + sol::object GetFlatByName(const std::string& acFlatName) const; + sol::object GetFlat(TweakDBID aDBID) const; bool SetFlatsByName(const std::string& acRecordName, sol::table aTable, sol::this_environment aThisEnv); bool SetFlats(TweakDBID aDBID, sol::table aTable, sol::this_environment aThisEnv); - bool SetFlatByName(const std::string& acFlatName, sol::object aObject, sol::this_environment aThisEnv); - bool SetFlat(TweakDBID aDBID, sol::object aObject, sol::this_environment aThisEnv); + bool SetFlatByName(const std::string& acFlatName, sol::object aObject, sol::this_environment aThisEnv) const; + bool SetFlat(TweakDBID aDBID, sol::object aObject, sol::this_environment aThisEnv) const; bool SetFlatByNameAutoUpdate(const std::string& acFlatName, sol::object aObject, sol::this_environment aThisEnv); bool SetFlatAutoUpdate(TweakDBID aDBID, sol::object aObject, sol::this_environment aThisEnv); bool SetTypedFlatByName(const std::string& acFlatName, sol::object aObject, const std::string& acTypeName, - sol::this_environment aThisEnv); + sol::this_environment aThisEnv) const; bool SetTypedFlat(TweakDBID aDBID, sol::object aObject, const std::string& acTypeName, - sol::this_environment aThisEnv); + sol::this_environment aThisEnv) const; bool UpdateRecordByName(const std::string& acRecordName); bool UpdateRecordByID(TweakDBID aDBID); bool UpdateRecord(sol::object aValue, sol::this_environment aThisEnv); bool CreateRecord(const std::string& acRecordName, const std::string& acRecordTypeName, - sol::this_environment aThisEnv); + sol::this_environment aThisEnv) const; bool CreateRecordToID(TweakDBID aDBID, const std::string& acRecordTypeName, - sol::this_environment aThisEnv); + sol::this_environment aThisEnv) const; bool CloneRecordByName(const std::string& acRecordName, const std::string& acClonedRecordName, - sol::this_environment aThisEnv); - bool CloneRecord(const std::string& acRecordName, TweakDBID aClonedRecordDBID, sol::this_environment aThisEnv); - bool CloneRecordToID(TweakDBID aDBID, TweakDBID aClonedRecordDBID, sol::this_environment aThisEnv); + sol::this_environment aThisEnv) const; + bool CloneRecord(const std::string& acRecordName, TweakDBID aClonedRecordDBID, sol::this_environment aThisEnv) const; + bool CloneRecordToID(TweakDBID aDBID, TweakDBID aClonedRecordDBID, sol::this_environment aThisEnv) const; bool DeleteRecord(const std::string& acRecordName, sol::this_environment aThisEnv); bool DeleteRecordByID(TweakDBID aDBID, sol::this_environment aThisEnv); protected: friend struct TweakDBEditor; bool SetOrCreateFlat(TweakDBID aDBID, sol::object aObject, const std::string& acFlatName, - const std::string& acTypeName, std::shared_ptr aLogger = nullptr); + const std::string& acTypeName, const std::shared_ptr& acpLogger = nullptr) const; static RED4ext::CStackType InternalGetFlat(RED4ext::TweakDBID aDBID); static int32_t InternalSetFlat(RED4ext::TweakDBID aDBID, const RED4ext::CStackType& acStackType); static bool InternalCreateRecord(const std::string& acRecordName, const std::string& acRecordTypeName, - const std::shared_ptr& aLogger); + const std::shared_ptr& acpLogger); static bool InternalCreateRecord(TweakDBID aDBID, const std::string& acRecordTypeName, - const std::shared_ptr& aLogger); + const std::shared_ptr& acpLogger); static bool InternalCloneRecord(const std::string& acRecordName, RED4ext::TweakDBID aClonedRecordDBID, - const std::shared_ptr& aLogger); + const std::shared_ptr& acpLogger); static bool InternalCloneRecord(TweakDBID aDBID, RED4ext::TweakDBID aClonedRecordDBID, - const std::shared_ptr& aLogger); + const std::shared_ptr& acpLogger); // Can't figure out a good name for this function. // Creates a record of the same type as 'acClonedRecord' // Creates all of its flats // Setting 'cloneValues' to false will set default values static bool InternalCloneRecord(const std::string& acRecordName, const RED4ext::gamedataTweakDBRecord* acClonedRecord, bool cloneValues, - const std::shared_ptr& aLogger); + const std::shared_ptr& acpLogger); static bool InternalCloneRecord(TweakDBID aDBID, const RED4ext::gamedataTweakDBRecord* acClonedRecord, bool cloneValues, - const std::shared_ptr& aLogger); + const std::shared_ptr& acpLogger); static bool InternalCloneFlats(RED4ext::TweakDBID aDBID, const RED4ext::gamedataTweakDBRecord* acClonedRecord, bool cloneValues, - const std::shared_ptr& aLogger); - static bool InternalDeleteRecord(RED4ext::TweakDBID aDBID, std::shared_ptr aLogger = nullptr); + const std::shared_ptr& acpLogger); + static bool InternalDeleteRecord(RED4ext::TweakDBID aDBID, const std::shared_ptr& acpLogger = nullptr); static bool RemoveFlat(RED4ext::TweakDBID aDBID); static bool IsACreatedRecord(RED4ext::TweakDBID aDBID); inline static std::string GetTDBIDString(uint64_t aDBID); -private: +private: TiltedPhoques::Lockable::Ref m_lua; static TiltedPhoques::UniquePtr s_flatPool; static std::mutex s_mutex; diff --git a/src/reverse/Type.cpp b/src/reverse/Type.cpp index 3884fee4..88ede72d 100644 --- a/src/reverse/Type.cpp +++ b/src/reverse/Type.cpp @@ -1,10 +1,10 @@ #include -#include #include "Type.h" #include "RTTIHelper.h" -#include "scripting/Scripting.h" + +#include std::string Type::Descriptor::ToString() const @@ -31,8 +31,8 @@ std::string Type::Descriptor::ToString() const } Type::Type(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass) - : m_lua(aView) - , m_pType(apClass) + : m_pType(apClass) + , m_lua(aView) { } @@ -67,11 +67,10 @@ std::string Type::GetName() const { if (m_pType) { - RED4ext::CName name; - m_pType->GetName(name); - if (!name.IsEmpty()) + const auto cName = m_pType->GetName(); + if (!cName.IsNone()) { - return name.ToString(); + return cName.ToString(); } } @@ -93,7 +92,7 @@ std::string Type::FunctionDescriptor(RED4ext::CBaseFunction* apFunc, bool aWithH for (auto i = 0u; i < apFunc->params.size; ++i) { - auto* param = apFunc->params[i]; + const auto* param = apFunc->params[i]; if (param->flags.isOut) { @@ -102,37 +101,35 @@ std::string Type::FunctionDescriptor(RED4ext::CBaseFunction* apFunc, bool aWithH hasOutParams = true; continue; } - param->type->GetName(typeName); - params.push_back(fmt::format("{}{}: {}", param->flags.isOptional ? "[opt] " : "", + typeName = param->type->GetName(); + params.emplace_back(fmt::format("{}{}: {}", param->flags.isOptional ? "[opt] " : "", param->name.ToString(), typeName.ToString())); } - for (auto i = 0u; i < params.size(); ++i) + if (!params.empty()) { - ret << params[i]; - if (i < params.size() - 1) - { - ret << ", "; - } + ret << params[0]; + for (auto i = 1u; i < params.size(); ++i) + ret << ", " << params[i]; } ret << ")"; - const bool hasReturnType = (apFunc->returnType) != nullptr && (apFunc->returnType->type) != nullptr; + const bool hasReturnType = apFunc->returnType != nullptr && apFunc->returnType->type != nullptr; params.clear(); if (hasReturnType) { - apFunc->returnType->type->GetName(typeName); - params.push_back(typeName.ToString()); + typeName = apFunc->returnType->type->GetName(); + params.emplace_back(typeName.ToString()); } if (hasOutParams) { for (auto i = 0u; i < apFunc->params.size; ++i) { - auto* param = apFunc->params[i]; + const auto* param = apFunc->params[i]; if (!param->flags.isOut) { @@ -140,24 +137,18 @@ std::string Type::FunctionDescriptor(RED4ext::CBaseFunction* apFunc, bool aWithH continue; } - param->type->GetName(typeName); - params.push_back(param->name.ToString() + std::string(": ") + typeName.ToString()); + typeName = param->type->GetName(); + params.emplace_back(fmt::format("{}: {}", param->name.ToString(), typeName.ToString())); } } - if (params.size() > 0) + if (!params.empty()) { - ret << " => ("; + ret << " => (" << params[0]; - for (auto i = 0; i < params.size(); ++i) - { - ret << params[i]; - if (i < params.size() - 1) - { - ret << ", "; - } - } + for (size_t i = 1; i < params.size(); ++i) + ret << ", " << params[i]; ret << ")"; } @@ -165,8 +156,7 @@ std::string Type::FunctionDescriptor(RED4ext::CBaseFunction* apFunc, bool aWithH if (aWithHashes) { - const std::string funcHashes = "Hash:(" + fmt::format("{:016x}", apFunc->fullName.hash) + ") / ShortName:(" + apFunc->shortName.ToString() + ") Hash:(" + fmt::format("{:016x}", apFunc->shortName.hash) + ")"; - ret << " # " << funcHashes; + ret << fmt::format(" # Hash:({:016X}) / ShortName:({}) Hash:({:016X}", apFunc->fullName.hash, apFunc->shortName.ToString(), apFunc->shortName.hash); } return ret.str(); @@ -178,9 +168,8 @@ Type::Descriptor Type::Dump(bool aWithHashes) const if (m_pType) { - RED4ext::CName name; - m_pType->GetName(name); - if (!name.IsEmpty()) + const auto name = m_pType->GetName(); + if (!name.IsNone()) { descriptor.name = name.ToString(); } @@ -189,12 +178,12 @@ Type::Descriptor Type::Dump(bool aWithHashes) const return descriptor; } -std::string Type::GameDump() +std::string Type::GameDump() const { RED4ext::CString str(""); if (m_pType) { - auto handle = GetHandle(); + const auto handle = GetHandle(); if (handle) { m_pType->ToString(handle, str); @@ -221,25 +210,20 @@ Type::Descriptor ClassType::Dump(bool aWithHashes) const std::string name = type->name.ToString(); for (auto i = 0u; i < type->funcs.size; ++i) { - auto* pFunc = type->funcs[i]; - std::string funcDesc = FunctionDescriptor(pFunc, aWithHashes); - descriptor.functions.push_back(funcDesc); + descriptor.functions.emplace_back(FunctionDescriptor(type->funcs[i], aWithHashes)); } for (auto i = 0u; i < type->staticFuncs.size; ++i) { - auto* pFunc = type->staticFuncs[i]; - std::string funcDesc = FunctionDescriptor(pFunc, aWithHashes); - descriptor.staticFunctions.push_back(funcDesc); + descriptor.staticFunctions.emplace_back(FunctionDescriptor(type->staticFuncs[i], aWithHashes)); } for (auto i = 0u; i < type->props.size; ++i) { - auto* pProperty = type->props[i]; - RED4ext::CName name; - pProperty->type->GetName(name); + const auto* cpProperty = type->props[i]; + const auto cName = cpProperty->type->GetName(); - descriptor.properties.push_back(pProperty->name.ToString() + std::string(": ") + name.ToString()); + descriptor.properties.emplace_back(fmt::format("{}: {}", cpProperty->name.ToString(), cName.ToString())); } type = type->parent && type->parent->GetType() == RED4ext::ERTTIType::Class ? type->parent : nullptr; @@ -271,17 +255,17 @@ sol::object ClassType::Index_Impl(const std::string& acName, sol::this_environme if (result != sol::nil) return result; - auto func = RTTIHelper::Get().ResolveFunction(pClass, acName, pHandle != nullptr); + const auto func = RTTIHelper::Get().ResolveFunction(pClass, acName, pHandle != nullptr); if (!func) { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); logger->warn("Warning: {} not found in {}.", acName, GetName()); return sol::nil; } - return Type::NewIndex(acName, std::move(func)); + return NewIndex(acName, func); } sol::object ClassType::NewIndex_Impl(const std::string& acName, sol::object aParam) diff --git a/src/reverse/Type.h b/src/reverse/Type.h index 0d9589e1..40371c42 100644 --- a/src/reverse/Type.h +++ b/src/reverse/Type.h @@ -13,7 +13,7 @@ struct Type }; Type(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass); - virtual ~Type(){}; + virtual ~Type() = default; RED4ext::CBaseRTTIType* GetType() const { return m_pType; } virtual RED4ext::ScriptInstance GetHandle() const { return nullptr; } @@ -27,7 +27,7 @@ struct Type std::string GetName() const; virtual Descriptor Dump(bool aWithHashes) const; - std::string GameDump(); + std::string GameDump() const; std::string FunctionDescriptor(RED4ext::CBaseFunction* apFunc, bool aWithHashes) const; @@ -45,13 +45,13 @@ struct ClassType : Type { ClassType(const TiltedPhoques::Lockable::Ref& aView, RED4ext::CBaseRTTIType* apClass); - virtual ~ClassType(){}; + ~ClassType() override = default; Descriptor Dump(bool aWithHashes) const override; sol::object Index_Impl(const std::string& acName, sol::this_environment aThisEnv) override; sol::object NewIndex_Impl(const std::string& acName, sol::object aParam) override; - RED4ext::CClass* GetClass() const { return reinterpret_cast(m_pType); }; + RED4ext::CClass* GetClass() const { return reinterpret_cast(m_pType); } }; struct UnknownType : Type diff --git a/src/reverse/WeakReference.cpp b/src/reverse/WeakReference.cpp index 9bc46405..a9385d44 100644 --- a/src/reverse/WeakReference.cpp +++ b/src/reverse/WeakReference.cpp @@ -5,14 +5,14 @@ #include "CET.h" -static RTTILocator s_sIScriptableType{RED4ext::FNV1a("IScriptable")}; +static RTTILocator s_sIScriptableType{RED4ext::FNV1a64("IScriptable")}; WeakReference::WeakReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::WeakHandle aWeakHandle) : ClassType(aView, nullptr) , m_weakHandle(std::move(aWeakHandle)) { - auto ref = m_weakHandle.Lock(); + const auto ref = m_weakHandle.Lock(); if (ref) { m_pType = ref->GetType(); @@ -21,11 +21,11 @@ WeakReference::WeakReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::WeakHandle aWeakHandle, - RED4ext::CWeakHandle* apWeakHandleType) + RED4ext::CRTTIWeakHandleType* apWeakHandleType) : ClassType(aView, nullptr) , m_weakHandle(std::move(aWeakHandle)) { - auto ref = m_weakHandle.Lock(); + const auto ref = m_weakHandle.Lock(); if (ref) { auto const cpClass = reinterpret_cast(apWeakHandleType->GetInnerType()); diff --git a/src/reverse/WeakReference.h b/src/reverse/WeakReference.h index a565163d..9925a4e5 100644 --- a/src/reverse/WeakReference.h +++ b/src/reverse/WeakReference.h @@ -8,17 +8,17 @@ struct WeakReference : ClassType RED4ext::WeakHandle aWeakHandle); WeakReference(const TiltedPhoques::Lockable::Ref& aView, RED4ext::WeakHandle aWeakHandle, - RED4ext::CWeakHandle* apWeakHandleType); - virtual ~WeakReference(); + RED4ext::CRTTIWeakHandleType* apWeakHandleType); + ~WeakReference() override; protected: - virtual RED4ext::ScriptInstance GetHandle() const override; - virtual RED4ext::ScriptInstance GetValuePtr() const override; - + RED4ext::ScriptInstance GetHandle() const override; + RED4ext::ScriptInstance GetValuePtr() const override; + private: friend struct Scripting; friend struct TweakDB; - + RED4ext::WeakHandle m_weakHandle; }; diff --git a/src/scripting/FunctionOverride.cpp b/src/scripting/FunctionOverride.cpp index eebefb1f..de2d015a 100644 --- a/src/scripting/FunctionOverride.cpp +++ b/src/scripting/FunctionOverride.cpp @@ -7,22 +7,24 @@ #include #include +namespace +{ -static FunctionOverride* s_pOverride = nullptr; +FunctionOverride* s_pOverride = nullptr; using TRunPureScriptFunction = bool (*)(RED4ext::CBaseFunction* apFunction, RED4ext::CScriptStack*, void*); using TCreateFunction = void* (*)(void* apMemoryPool, size_t aSize); using TCallScriptFunction = bool (*)(RED4ext::IFunction* apFunction, RED4ext::IScriptable* apContext, RED4ext::CStackFrame* apFrame, void* apOut, void* a4); -static TRunPureScriptFunction RealRunPureScriptFunction = nullptr; -static TCreateFunction RealCreateFunction = nullptr; -static RED4ext::REDfunc CallScriptFunction(RED4ext::Addresses::CBaseFunction_InternalExecute); +TRunPureScriptFunction RealRunPureScriptFunction = nullptr; +TCreateFunction RealCreateFunction = nullptr; +RED4ext::RelocFunc CallScriptFunction(RED4ext::Addresses::CBaseFunction_InternalExecute); -static constexpr size_t s_cMaxFunctionSize = +constexpr size_t s_cMaxFunctionSize = std::max({sizeof(RED4ext::CClassFunction), sizeof(RED4ext::CClassStaticFunction), sizeof(RED4ext::CGlobalFunction)}); -inline static size_t GetFunctionSize(RED4ext::CBaseFunction* apFunction) +size_t GetFunctionSize(RED4ext::CBaseFunction* apFunction) { if (apFunction->flags.isStatic) { @@ -34,14 +36,30 @@ inline static size_t GetFunctionSize(RED4ext::CBaseFunction* apFunction) return sizeof(RED4ext::CClassFunction); } -FunctionOverride::FunctionOverride(Scripting* apScripting, Options& aOptions) +struct OverrideCodegen : Xbyak::CodeGenerator +{ + OverrideCodegen(uintptr_t apRealFunc, uintptr_t apTrampoline) + { + sub(rsp, 56); + mov(rax, apRealFunc); + mov(ptr[rsp + 32], rax); + mov(rax, apTrampoline); + call(rax); + add(rsp, 56); + ret(); + } +}; + +} + +FunctionOverride::FunctionOverride(Scripting* apScripting) : m_pScripting(apScripting) { s_pOverride = this; m_pBuffer = m_pBufferStart = VirtualAlloc(nullptr, m_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); - Hook(aOptions); + Hook(); } FunctionOverride::~FunctionOverride() @@ -55,7 +73,7 @@ void* FunctionOverride::MakeExecutable(const uint8_t* apData, size_t aSize) { if (std::align(0x10, aSize, m_pBuffer, m_size)) { - uint8_t* result = static_cast(m_pBuffer); + auto* result = static_cast(m_pBuffer); m_pBuffer = static_cast(m_pBuffer) + aSize; m_size -= aSize; @@ -77,16 +95,17 @@ void FunctionOverride::Refresh() void FunctionOverride::Clear() { - std::unique_lock lock(s_pOverride->m_lock); + std::lock_guard lock(s_pOverride->m_lock); // Reverse order as we want to swap from most recent to oldest change for (auto& [pFunction, pContext] : m_functions) { auto* pRealFunction = pContext.Trampoline; - std::array tmpBuffer; size_t funcSize = GetFunctionSize(pRealFunction); + // TODO - undefined behaviour! + std::array tmpBuffer{}; std::memcpy(&tmpBuffer, pRealFunction, funcSize); std::memcpy(pRealFunction, pFunction, funcSize); std::memcpy(pFunction, &tmpBuffer, funcSize); @@ -95,7 +114,7 @@ void FunctionOverride::Clear() m_functions.clear(); } -void* FunctionOverride::HookCreateFunction(void* apMemoryPool, size_t aSize) +void* FunctionOverride::HookCreateFunction(void* apMemoryPool, size_t) { enum { @@ -124,33 +143,31 @@ bool FunctionOverride::HookRunPureScriptFunction(RED4ext::CClassFunction* apFunc { TiltedPhoques::StackAllocator<1 << 13> s_allocator; - auto pAllocator = TiltedPhoques::Allocator::Get(); + const auto pAllocator = TiltedPhoques::Allocator::Get(); TiltedPhoques::Allocator::Set(&s_allocator); TiltedPhoques::Vector args(0); TiltedPhoques::Vector outArgs; TiltedPhoques::Allocator::Set(pAllocator); - auto pContext = apStack->GetContext(); + const auto pContext = apStack->GetContext(); { - auto lockedState = chain.pScripting->GetState(); - auto& luaState = lockedState.Get(); + auto lockedState = chain.pScripting->GetLockedState(); + const auto& luaState = lockedState.Get(); if (!apFunction->flags.isStatic && pContext) { - const auto weak = RED4ext::WeakHandle( - *(RED4ext::WeakHandle*)&pContext->ref); - auto obj = sol::make_object(luaState, WeakReference(lockedState, weak)); - args.reserve(apFunction->params.size + 1); - args.push_back(obj); + + const auto weak = RED4ext::WeakHandle(*reinterpret_cast*>(&pContext->ref)); + args.emplace_back(make_object(luaState, WeakReference(lockedState, weak))); } else { args.reserve(apFunction->params.size); } - for (auto* p : apFunction->params) + for (const auto* p : apFunction->params) { auto* pOffset = p->valueOffset + apStack->args; @@ -158,10 +175,10 @@ bool FunctionOverride::HookRunPureScriptFunction(RED4ext::CClassFunction* apFunc arg.type = p->type; arg.value = pOffset; - args.push_back(Scripting::ToLua(lockedState, arg)); + args.emplace_back(Scripting::ToLua(lockedState, arg)); if (p->flags.isOut) - outArgs.push_back(arg); + outArgs.emplace_back(arg); } } @@ -203,22 +220,22 @@ void FunctionOverride::HandleOverridenFunction(RED4ext::IScriptable* apContext, // Save state so we can rollback to it after we popped for ourself auto* pCode = apFrame->code; - uint8_t currentParam = apFrame->currentParam; + const uint8_t currentParam = apFrame->currentParam; if (!chain.IsEmpty) { // Cheap allocation TiltedPhoques::StackAllocator<1 << 13> s_allocator; - auto pAllocator = TiltedPhoques::Allocator::Get(); + const auto pAllocator = TiltedPhoques::Allocator::Get(); TiltedPhoques::Allocator::Set(&s_allocator); TiltedPhoques::Vector args(0); TiltedPhoques::Vector outArgs; TiltedPhoques::Allocator::Set(pAllocator); { - auto lockedState = chain.pScripting->GetState(); - auto& luaState = lockedState.Get(); + auto lockedState = chain.pScripting->GetLockedState(); + const auto& luaState = lockedState.Get(); if (!apFunction->flags.isStatic) { @@ -235,12 +252,11 @@ void FunctionOverride::HandleOverridenFunction(RED4ext::IScriptable* apContext, self.value = apFrame->context; } - const auto ref = (RED4ext::WeakHandle*)&((RED4ext::IScriptable*)self.value)->ref; - const auto weak = RED4ext::WeakHandle(*ref); - auto obj = sol::make_object(luaState, WeakReference(lockedState, weak)); - args.reserve(apFunction->params.size + 1); - args.push_back(obj); + + const auto ref = reinterpret_cast*>(&static_cast(self.value)->ref); + const auto weak = RED4ext::WeakHandle(*ref); + args.emplace_back(make_object(luaState, WeakReference(lockedState, weak))); } else { @@ -248,25 +264,25 @@ void FunctionOverride::HandleOverridenFunction(RED4ext::IScriptable* apContext, } // Nasty way of popping all args - for (auto& pArg : apFunction->params) + for (const auto& pArg : apFunction->params) { auto* pType = pArg->type; - auto* pAllocator = pType->GetAllocator(); + auto* pTypeAllocator = pType->GetAllocator(); - auto* pInstance = pAllocator->AllocAligned(pType->GetSize(), pType->GetAlignment()).memory; - pType->Init(pInstance); + auto* pInstance = pTypeAllocator->AllocAligned(pType->GetSize(), pType->GetAlignment()).memory; + pType->Construct(pInstance); - bool isScriptRef = pArg->type->GetType() == RED4ext::ERTTIType::ScriptReference; + const bool isScriptRef = pArg->type->GetType() == RED4ext::ERTTIType::ScriptReference; // Exception here we need to allocate the inner object as well if (isScriptRef) { - auto* pInnerType = ((RED4ext::CRTTIScriptReferenceType*)pType)->innerType; - auto* pScriptRef = (RED4ext::ScriptRef*)pInstance; + auto* pInnerType = static_cast(pType)->innerType; + auto* pScriptRef = static_cast*>(pInstance); pScriptRef->innerType = pInnerType; pScriptRef->hash = pInnerType->GetName(); pScriptRef->ref = pInnerType->GetAllocator()->AllocAligned(pInnerType->GetSize(), pInnerType->GetAlignment()).memory; - pInnerType->Init(pScriptRef->ref); + pInnerType->Construct(pScriptRef->ref); } RED4ext::CStackType arg; @@ -276,33 +292,33 @@ void FunctionOverride::HandleOverridenFunction(RED4ext::IScriptable* apContext, apFrame->currentParam++; apFrame->data = nullptr; apFrame->dataType = nullptr; - const auto opcode = *(apFrame->code++); - RED4ext::OpcodeHandlers::Run(opcode, (RED4ext::IScriptable*)apFrame->context, apFrame, pInstance, isScriptRef ? pInstance : nullptr); + const auto opcode = *apFrame->code++; + RED4ext::OpcodeHandlers::Run(opcode, apFrame->context, apFrame, pInstance, isScriptRef ? pInstance : nullptr); - args.push_back(Scripting::ToLua(lockedState, arg)); + args.emplace_back(Scripting::ToLua(lockedState, arg)); if (pArg->flags.isOut) { // This is an original arg, pInstance contains copy if (apFrame->data) - arg.value = reinterpret_cast(apFrame->data); + arg.value = apFrame->data; - outArgs.push_back(arg); + outArgs.emplace_back(arg); } // Release inner values if (isScriptRef) { - auto* pScriptRef = (RED4ext::ScriptRef*)pInstance; - pScriptRef->innerType->Destroy(pScriptRef->ref); + auto* pScriptRef = static_cast*>(pInstance); + pScriptRef->innerType->Destruct(pScriptRef->ref); pScriptRef->innerType->GetAllocator()->Free(pScriptRef->ref); pScriptRef->ref = nullptr; } if (!pArg->flags.isOut || apFrame->data) { - pType->Destroy(pInstance); - pAllocator->Free(pInstance); + pType->Destruct(pInstance); + pTypeAllocator->Free(pInstance); } } } @@ -339,7 +355,7 @@ bool FunctionOverride::ExecuteChain(const CallChain& aChain, std::shared_lockGetState(); + auto lockedState = aChain.pScripting->GetLockedState(); for (const auto& call : aChain.Before) { @@ -347,7 +363,7 @@ bool FunctionOverride::ExecuteChain(const CallChain& aChain, std::shared_lockEnvironment["__logger"].get>(); + const auto logger = call->Environment["__logger"].get>(); logger->error(result.get().what()); } } @@ -358,15 +374,15 @@ bool FunctionOverride::ExecuteChain(const CallChain& aChain, std::shared_lockGetState(); + auto lockedState = aChain.pScripting->GetLockedState(); auto& luaState = lockedState.Get(); sol::object luaContext = pRealFunction->flags.isStatic ? sol::nil : apOrigArgs->at(0); TiltedPhoques::Vector luaArgs(apOrigArgs->begin() + (pRealFunction->flags.isStatic ? 0 : 1), apOrigArgs->end()); - auto luaWrapped = WrapNextOverride(aChain, 0, luaState, luaContext, luaArgs, pRealFunction, apContext, aLock); - auto luaResult = luaWrapped(as_args(luaArgs)); + const auto luaWrapped = WrapNextOverride(aChain, 0, luaState, luaContext, luaArgs, pRealFunction, apContext, aLock); + const auto luaResult = luaWrapped(as_args(luaArgs)); if (luaResult.valid()) { @@ -380,7 +396,7 @@ bool FunctionOverride::ExecuteChain(const CallChain& aChain, std::shared_lockempty()) { - for (auto i = 0; i < apOutArgs->size(); ++i) + for (auto i = 0; i < static_cast(apOutArgs->size()); ++i) { auto luaOutArg = luaResult.get(i + luaRetOffset); @@ -407,7 +423,7 @@ bool FunctionOverride::ExecuteChain(const CallChain& aChain, std::shared_lockGetState(); + auto lockedState = aChain.pScripting->GetLockedState(); for (const auto& call : aChain.After) { @@ -415,7 +431,7 @@ bool FunctionOverride::ExecuteChain(const CallChain& aChain, std::shared_lockEnvironment["__logger"].get>(); + const auto logger = call->Environment["__logger"].get>(); logger->error(result.get().what()); } } @@ -423,7 +439,7 @@ bool FunctionOverride::ExecuteChain(const CallChain& aChain, std::shared_lockempty()) { - auto lockedState = aChain.pScripting->GetState(); + auto lockedState = aChain.pScripting->GetLockedState(); apOrigArgs->resize(0); } @@ -438,7 +454,7 @@ sol::function FunctionOverride::WrapNextOverride(const CallChain& aChain, int aS RED4ext::CBaseFunction* apRealFunction, RED4ext::IScriptable* apRealContext, std::shared_lock& aLock) { - if (aStep == aChain.Overrides.size()) + if (aStep == static_cast(aChain.Overrides.size())) { return MakeSolFunction(aLuaState, [&](sol::variadic_args aWrapArgs, sol::this_state aState, sol::this_environment aEnv) -> sol::variadic_results { @@ -448,7 +464,7 @@ sol::function FunctionOverride::WrapNextOverride(const CallChain& aChain, int aS if (!errorMessage.empty()) { const sol::environment cEnv = aEnv; - auto logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); logger->error("Error: {}", errorMessage); aLock.unlock(); @@ -462,15 +478,15 @@ sol::function FunctionOverride::WrapNextOverride(const CallChain& aChain, int aS } return MakeSolFunction(aLuaState, - [&, aStep](sol::variadic_args aWrapArgs, sol::this_state aState, sol::this_environment aEnv) -> sol::variadic_results { - auto call = (aChain.Overrides.rbegin() + aStep)->get(); + [&, aStep](sol::variadic_args aWrapArgs, sol::this_state aState) -> sol::variadic_results { + const auto call = (aChain.Overrides.rbegin() + aStep)->get(); - for (auto i = 0; i < apRealFunction->params.size; ++i) + for (uint32_t i = 0; i < apRealFunction->params.size; ++i) { - if (i < aWrapArgs.leftover_count()) + if (static_cast(i) < aWrapArgs.leftover_count()) aLuaArgs[i] = aWrapArgs[i]; else - aLuaArgs[i] = (sol::object)sol::nil; + aLuaArgs[i] = static_cast(sol::nil); } auto next = WrapNextOverride(aChain, aStep + 1, aLuaState, aLuaContext, aLuaArgs, apRealFunction, apRealContext, aLock); @@ -481,7 +497,7 @@ sol::function FunctionOverride::WrapNextOverride(const CallChain& aChain, int aS if (!result.valid()) { const sol::environment cEnv = call->Environment; - auto logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); logger->error(result.get().what()); aLock.unlock(); @@ -491,28 +507,26 @@ sol::function FunctionOverride::WrapNextOverride(const CallChain& aChain, int aS } sol::variadic_results results; - for (const auto element : result) - results.push_back(element); + for (auto&& element : result) + results.emplace_back(element); return results; }); } -void FunctionOverride::Hook(Options& aOptions) const +void FunctionOverride::Hook() const { - auto& gameImage = aOptions.GameImage; - { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_RunPureScript); - RealRunPureScriptFunction = static_cast(func.GetAddr()); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_RunPureScript); + RealRunPureScriptFunction = reinterpret_cast(func.GetAddr()); if (!RealRunPureScriptFunction) Log::Error("Could not find pure run script function!"); else { auto* pLocation = RealRunPureScriptFunction; - if (MH_CreateHook(pLocation, &FunctionOverride::HookRunPureScriptFunction, + if (MH_CreateHook(reinterpret_cast(pLocation), reinterpret_cast(HookRunPureScriptFunction), reinterpret_cast(&RealRunPureScriptFunction)) != MH_OK || - MH_EnableHook(pLocation) != MH_OK) + MH_EnableHook(reinterpret_cast(pLocation)) != MH_OK) Log::Error("Could not hook RealRunScriptFunction function!"); else Log::Info("RealRunScriptFunction function hook complete!"); @@ -520,16 +534,16 @@ void FunctionOverride::Hook(Options& aOptions) const } { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_AllocateFunction); - RealCreateFunction = static_cast(func.GetAddr()); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_AllocateFunction); + RealCreateFunction = reinterpret_cast(func.GetAddr()); if (!RealCreateFunction) Log::Error("Could not find create function!"); else { auto* pLocation = RealCreateFunction; - if (MH_CreateHook(pLocation, &FunctionOverride::HookCreateFunction, + if (MH_CreateHook(reinterpret_cast(pLocation), reinterpret_cast(HookCreateFunction), reinterpret_cast(&RealCreateFunction)) != MH_OK || - MH_EnableHook(pLocation) != MH_OK) + MH_EnableHook(reinterpret_cast(pLocation)) != MH_OK) Log::Error("Could not hook RealCreateFunction function!"); else Log::Info("RealCreateFunction function hook complete!"); @@ -537,20 +551,6 @@ void FunctionOverride::Hook(Options& aOptions) const } } -struct OverrideCodegen : Xbyak::CodeGenerator -{ - OverrideCodegen(uintptr_t apRealFunc, uintptr_t apTrampoline) - { - sub(rsp, 56); - mov(rax, apRealFunc); - mov(ptr[rsp + 32], rax); - mov(rax, apTrampoline); - call(rax); - add(rsp, 56); - ret(); - } -}; - void FunctionOverride::Override(const std::string& acTypeName, const std::string& acFullName, sol::protected_function aFunction, sol::environment aEnvironment, bool aAbsolute, bool aAfter, bool aCollectGarbage) @@ -560,7 +560,7 @@ void FunctionOverride::Override(const std::string& acTypeName, const std::string if (!pClassType) { - auto* pNativeCName = pRtti->scriptToNative.Get(RED4ext::CName(acTypeName.c_str())); + const auto* pNativeCName = pRtti->scriptToNative.Get(RED4ext::CName(acTypeName.c_str())); if (!pNativeCName) { @@ -585,7 +585,7 @@ void FunctionOverride::Override(const std::string& acTypeName, const std::string } } - std::unique_lock lock(m_lock); + std::lock_guard lock(m_lock); CallChain* pEntry = nullptr; const auto itor = m_functions.find(pRealFunction); @@ -605,7 +605,7 @@ void FunctionOverride::Override(const std::string& acTypeName, const std::string const OverrideCodegen codegen(reinterpret_cast(pRealFunction), funcAddr); using TNativeScriptFunction = void (*)(RED4ext::IScriptable*, RED4ext::CStackFrame*, void*, int64_t); - auto* pExecutablePayload = static_cast(MakeExecutable(codegen.getCode(), codegen.getSize())); + auto* pExecutablePayload = reinterpret_cast(MakeExecutable(codegen.getCode(), codegen.getSize())); if (pRealFunction->flags.isStatic) { @@ -642,9 +642,10 @@ void FunctionOverride::Override(const std::string& acTypeName, const std::string pEntry->IsEmpty = true; // Swap the content of the real function with the one we just created - std::array tmpBuffer; - size_t funcSize = GetFunctionSize(pRealFunction); + const size_t funcSize = GetFunctionSize(pRealFunction); + // TODO - undefined behaviour! + std::array tmpBuffer{}; std::memcpy(&tmpBuffer, pRealFunction, funcSize); std::memcpy(pRealFunction, pFunc, funcSize); std::memcpy(pFunc, &tmpBuffer, funcSize); @@ -673,8 +674,7 @@ void FunctionOverride::Override(const std::string& acTypeName, const std::string } } -void FunctionOverride::CopyFunctionDescription(RED4ext::CBaseFunction* aFunc, RED4ext::CBaseFunction* aRealFunc, - bool aForceNative) +void FunctionOverride::CopyFunctionDescription(RED4ext::CBaseFunction* aFunc, RED4ext::CBaseFunction* aRealFunc, bool aForceNative) { aFunc->fullName = aRealFunc->fullName; aFunc->shortName = aRealFunc->shortName; diff --git a/src/scripting/FunctionOverride.h b/src/scripting/FunctionOverride.h index f9011053..68ab2448 100644 --- a/src/scripting/FunctionOverride.h +++ b/src/scripting/FunctionOverride.h @@ -22,27 +22,26 @@ struct FunctionOverride bool CollectGarbage; }; - FunctionOverride(Scripting* apScripting, Options& aOptions); + FunctionOverride(Scripting* apScripting); ~FunctionOverride(); void* MakeExecutable(const uint8_t* apData, size_t aSize); void Refresh(); void Clear(); - void Override(const std::string& acTypeName, const std::string& acFullName, + void Override(const std::string& acTypeName, const std::string& acFullName, sol::protected_function aFunction, sol::environment aEnvironment, bool aAbsolute, bool aAfter = false, bool aCollectGarbage = false); protected: - static void CopyFunctionDescription(RED4ext::CBaseFunction* aFunc, RED4ext::CBaseFunction* aRealFunc, - bool aForceNative); - static void HandleOverridenFunction(RED4ext::IScriptable* aContext, RED4ext::CStackFrame* aFrame, void* aOut, void* a4, RED4ext::CClassFunction* apFunction); - static bool HookRunPureScriptFunction(RED4ext::CClassFunction* apFunction, RED4ext::CScriptStack* apContext, RED4ext::CStackFrame* a3); + static void CopyFunctionDescription(RED4ext::CBaseFunction* aFunc, RED4ext::CBaseFunction* aRealFunc, bool aForceNative); + static void HandleOverridenFunction(RED4ext::IScriptable* apContext, RED4ext::CStackFrame* apFrame, void* apOut, void* a4, RED4ext::CClassFunction* apFunction); + static bool HookRunPureScriptFunction(RED4ext::CClassFunction* apFunction, RED4ext::CScriptStack* apStack, RED4ext::CStackFrame* a3); static void* HookCreateFunction(void* apMemoryPool, size_t aSize); static bool ExecuteChain(const CallChain& aChain, std::shared_lock& aLock, - RED4ext::IScriptable* apContext, TiltedPhoques::Vector* apArgs, - RED4ext::CStackType* apResult, TiltedPhoques::Vector* apOutArgs, + RED4ext::IScriptable* apContext, TiltedPhoques::Vector* apOrigArgs, + RED4ext::CStackType* apResult, TiltedPhoques::Vector* apOutArgs, RED4ext::CScriptStack* apStack, RED4ext::CStackFrame* apFrame, char* apCode, uint8_t aParam); static sol::function WrapNextOverride(const CallChain& aChain, int aStep, sol::state& aLuaState, sol::object& aLuaContext, TiltedPhoques::Vector& aLuaArgs, @@ -56,7 +55,7 @@ struct FunctionOverride kExecutableSize = 1 << 20 }; - void Hook(Options& aOptions) const; + void Hook() const; void* m_pBufferStart; void* m_pBuffer; diff --git a/src/scripting/GameDump.cpp b/src/scripting/GameDump.cpp index e1afb07d..a86b2304 100644 --- a/src/scripting/GameDump.cpp +++ b/src/scripting/GameDump.cpp @@ -2,28 +2,25 @@ #include "GameDump.h" -#include - namespace GameDump { void DumpVTablesTask::Run() { TiltedPhoques::Map vtableMap; - HMODULE ModuleBase = GetModuleHandle(nullptr); - uintptr_t begin = reinterpret_cast(ModuleBase); - const IMAGE_DOS_HEADER* dosHeader = reinterpret_cast(ModuleBase); - const IMAGE_NT_HEADERS* ntHeader = reinterpret_cast( + auto ModuleBase = GetModuleHandle(nullptr); + auto begin = reinterpret_cast(ModuleBase); + const auto* dosHeader = reinterpret_cast(ModuleBase); + const auto* ntHeader = reinterpret_cast( reinterpret_cast(dosHeader) + dosHeader->e_lfanew); uintptr_t end = begin + ntHeader->OptionalHeader.SizeOfCode + ntHeader->OptionalHeader.SizeOfInitializedData; - auto* pRttiSystem = RED4ext::CRTTISystem::Get(); + const auto* pRttiSystem = RED4ext::CRTTISystem::Get(); auto dumpClass = [begin, end](auto& aVtableMap, RED4ext::CBaseRTTIType* apType) { uintptr_t vtable = *reinterpret_cast(apType); - RED4ext::CName typeName; - apType->GetName(typeName); + const RED4ext::CName typeName = apType->GetName(); const std::string name = typeName.ToString(); if (vtable >= begin && vtable <= end) { @@ -37,11 +34,11 @@ void DumpVTablesTask::Run() // We aren't borrowing the game's allocator on purpose because some classes have Abstract // allocators and they assert - const std::unique_ptr pMemory = std::make_unique(size); + const auto pMemory = std::make_unique(size); memset(pMemory.get(), 0, size); - apType->Init(pMemory.get()); + apType->Construct(pMemory.get()); if (size >= sizeof(uintptr_t)) { diff --git a/src/scripting/GameHooks.cpp b/src/scripting/GameHooks.cpp index 16b4aba9..1dfa2953 100644 --- a/src/scripting/GameHooks.cpp +++ b/src/scripting/GameHooks.cpp @@ -1,7 +1,6 @@ #include #include "GameHooks.h" -#include "CET.h" static std::unique_ptr s_pGameMainThread; @@ -40,7 +39,7 @@ GameMainThread::~GameMainThread() bool GameMainThread::HookMainThread(void* a1, void* a2) { - auto& gmt = GameMainThread::Get(); + auto& gmt = Get(); gmt.m_taskQueue.Drain(); @@ -51,13 +50,13 @@ void GameMainThread::Hook() { if (!m_pMainThreadLocation) { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CGame_Main); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CGame_Main); m_pMainThreadLocation = func.GetAddr(); } if (m_pMainThreadLocation) { - if (MH_CreateHook(m_pMainThreadLocation, &GameMainThread::HookMainThread, reinterpret_cast(&m_pMainThreadOriginal)) != MH_OK || + if (MH_CreateHook(m_pMainThreadLocation, reinterpret_cast(&GameMainThread::HookMainThread), reinterpret_cast(&m_pMainThreadOriginal)) != MH_OK || MH_EnableHook(m_pMainThreadLocation) != MH_OK) Log::Error("Could not hook main thread function!"); else diff --git a/src/scripting/GameOptions.cpp b/src/scripting/GameOptions.cpp index 786a8fae..cfdbdb49 100644 --- a/src/scripting/GameOptions.cpp +++ b/src/scripting/GameOptions.cpp @@ -72,7 +72,7 @@ bool GameOption::Set(const std::string& value) { if (GetType() == kBoolean) { - return SetBool(stricmp(value.c_str(), "true") == 0 || stricmp(value.c_str(), "1") == 0); + return SetBool(_stricmp(value.c_str(), "true") == 0 || _stricmp(value.c_str(), "1") == 0); } if (GetType() == kInteger) { @@ -132,17 +132,16 @@ bool GameOption::Toggle() GameOption* GameOptions::Find(const std::string& category, const std::string& name) { - auto option = std::find_if( - s_gameOptions.begin(), s_gameOptions.end(), - [&category, &name](GameOption* x) - { - return stricmp(x->pCategory, category.c_str()) == 0 && stricmp(x->pName, name.c_str()) == 0; - }); + const auto option = std::ranges::find_if(s_gameOptions, + [&category, &name](const GameOption* x) + { + return _stricmp(x->pCategory, category.c_str()) == 0 && _stricmp(x->pName, name.c_str()) == 0; + }); if (option == s_gameOptions.end()) { spdlog::get("scripting")->info("Failed to find game option '{}/{}'!", category, name); - return nullptr;; + return nullptr; } return *option; @@ -153,7 +152,7 @@ void GameOptions::Print(const std::string& category, const std::string& name) auto* option = Find(category, name); if (!option) return; - + spdlog::get("scripting")->info(option->GetInfo()); } @@ -173,7 +172,7 @@ bool GameOptions::GetBool(const std::string& category, const std::string& name) return false; bool value = false; - bool result = option->GetBool(value); + const bool result = option->GetBool(value); if (!result) { spdlog::get("scripting")->info("Failed to read game option '{}/{}', not a boolean?", category, name); @@ -190,7 +189,7 @@ int GameOptions::GetInt(const std::string& category, const std::string& name) return false; int value = false; - bool result = option->GetInt(value); + const bool result = option->GetInt(value); if (!result) { spdlog::get("scripting")->info("Failed to read game option '{}/{}', not an integer/color?", category, name); @@ -207,7 +206,7 @@ float GameOptions::GetFloat(const std::string& category, const std::string& name return false; float value = false; - bool result = option->GetFloat(value); + const bool result = option->GetFloat(value); if (!result) { spdlog::get("scripting")->info("Failed to read game option '{}/{}', not a float?", category, name); @@ -222,8 +221,8 @@ void GameOptions::Set(const std::string& category, const std::string& name, cons auto* option = Find(category, name); if (!option) return; - - auto consoleLogger = spdlog::get("scripting"); + + const auto consoleLogger = spdlog::get("scripting"); if (option->Set(value)) consoleLogger->info(option->GetInfo()); else @@ -240,8 +239,8 @@ void GameOptions::SetBool(const std::string& category, const std::string& name, auto* option = Find(category, name); if (!option) return; - - auto consoleLogger = spdlog::get("scripting"); + + const auto consoleLogger = spdlog::get("scripting"); if (option->SetBool(value)) consoleLogger->info(option->GetInfo()); else @@ -258,8 +257,8 @@ void GameOptions::SetInt(const std::string& category, const std::string& name, i auto* option = Find(category, name); if (!option) return; - - auto consoleLogger = spdlog::get("scripting"); + + const auto consoleLogger = spdlog::get("scripting"); if (option->SetInt(value)) consoleLogger->info(option->GetInfo()); else @@ -276,8 +275,8 @@ void GameOptions::SetFloat(const std::string& category, const std::string& name, auto* option = Find(category, name); if (!option) return; - - auto consoleLogger = spdlog::get("scripting"); + + const auto consoleLogger = spdlog::get("scripting"); if (option->SetFloat(value)) consoleLogger->info(option->GetInfo()); else @@ -295,7 +294,7 @@ void GameOptions::Toggle(const std::string& category, const std::string& name) if (!option) return; - auto consoleLogger = spdlog::get("scripting"); + const auto consoleLogger = spdlog::get("scripting"); if (option->Toggle()) consoleLogger->info(option->GetInfo()); else @@ -309,15 +308,15 @@ void GameOptions::Toggle(const std::string& category, const std::string& name) void GameOptions::Dump() { - for (auto option : s_gameOptions) + for (const auto option : s_gameOptions) Log::Info(option->GetInfo()); - + spdlog::get("scripting")->info("Dumped {} options to cyber_engine_tweaks.log", s_gameOptions.size()); } void GameOptions::List(const std::string& category) { - auto consoleLogger = spdlog::get("scripting"); + const auto consoleLogger = spdlog::get("scripting"); int count = 0; auto iter = s_gameOptions.begin(); @@ -325,12 +324,12 @@ void GameOptions::List(const std::string& category) { iter = std::find_if( iter, s_gameOptions.end(), - [&category](GameOption* x) + [&category](const GameOption* x) { if (!category.length() || category.at(0) == '*') return true; - return stricmp(x->pCategory, category.c_str()) == 0; + return _stricmp(x->pCategory, category.c_str()) == 0; }); if (iter != s_gameOptions.end()) diff --git a/src/scripting/GameOptions.h b/src/scripting/GameOptions.h index aa2e2872..83e23ff1 100644 --- a/src/scripting/GameOptions.h +++ b/src/scripting/GameOptions.h @@ -54,7 +54,6 @@ struct GameOption RED4ext::CString String; }; - std::string GetInfo(); @@ -106,5 +105,4 @@ struct GameOptions static void List(const std::string& category); static TiltedPhoques::Vector& GetList(); - }; diff --git a/src/scripting/LuaSandbox.cpp b/src/scripting/LuaSandbox.cpp index 05616ff9..ef5d7453 100644 --- a/src/scripting/LuaSandbox.cpp +++ b/src/scripting/LuaSandbox.cpp @@ -2,12 +2,12 @@ #include "LuaSandbox.h" -#include "Scripting.h" #include "Texture.h" +#include #include -static constexpr const char* s_cGlobalObjectsWhitelist[] = +static constexpr const char* s_cVMGlobalObjectsWhitelist[] = { "_VERSION", "assert", @@ -17,7 +17,6 @@ static constexpr const char* s_cGlobalObjectsWhitelist[] = "next", "pairs", "pcall", - "print", // Required for implementing classes "rawequal", @@ -34,41 +33,21 @@ static constexpr const char* s_cGlobalObjectsWhitelist[] = "xpcall", "collectgarbage", //< Good for testing memory leaks and ref counters: `collectgarbage("collect")`, also used for forcing the release of some refs -}; -static constexpr const char* s_cGlobalTablesWhitelist[] = -{ "string", "table", "math", - "bit32" + "bit32", + "json", }; -static constexpr const char* s_cGlobalImmutablesList[] = +static constexpr const char* s_cGlobalObjectsWhitelist[] = { - "__Game", - "__Type", - "ClassReference", - "CName", - "Descriptor", - "Enum", - "EulerAngles", - "Game", - "GameOptions", - "ItemID", - "Quaternion", - "SingletonReference", - "StrongReference", - "TweakDBID", - "Unknown", - "Vector3", - "Vector4", - "WeakReference" -}; + "print", + "GetVersion", + "GetDisplayResolution", + "ModArchiveExists", -static constexpr const char* s_cGlobalExtraLibsWhitelist[] = -{ - "ImGui", "ImGuiCond", "ImGuiTreeNodeFlags", "ImGuiSelectableFlags", @@ -93,8 +72,82 @@ static constexpr const char* s_cGlobalExtraLibsWhitelist[] = "ImGuiCol", "ImGuiDir", "ImVec2", - "ImVec4", - "json" + "ImVec4" +}; + +static constexpr const char* s_cPostInitializeScriptingProtectedList[] = +{ + // initialized by Scripting + "Descriptor", + "StrongReference", + "WeakReference", + "SingletonReference", + "ClassReference", + "ResourceAsyncReference", + "Unknown", + "IsDefined", + "Enum", + "EnumInt", + "Vector3", + "ToVector3", + "Vector4", + "ToVector4", + "EulerAngles", + "ToEulerAngles", + "Quaternion", + "ToQuaternion", + "CName", + "ToCName", + "TweakDBID", + "ToTweakDBID", + "ItemID", + "ToItemID", + "CRUID", + "LocKey", + "GameOptions", +}; + +static constexpr const char* s_cPostInitializeTweakDBProtectedList[] = +{ + // initialized by Scripting + "TweakDB" +}; + +static constexpr const char* s_cPostInitializeModsProtectedList[] = +{ + // initialized by Scripting + "NewObject", + "GetSingleton", + "Override", + "ObserveBefore", + "ObserveAfter", + "Observe", + "GetMod", + "GameDump", + "Dump", + "DumpType", + "DumpAllTypeNames", + "DumpVtables", + "DumpReflection", + "Game", + "RegisterGlobalInputListener", + + // initialized by RTTIMapper + "Vector3", + "ToVector3", + "Vector4", + "ToVector4", + "EulerAngles", + "ToEulerAngles", + "Quaternion", + "ToQuaternion", + "ItemID", + "ToItemID" +}; + +static constexpr const char* s_cGlobalExtraLibsWhitelist[] = +{ + "ImGui", }; LuaSandbox::LuaSandbox(Scripting* apScripting, const VKBindings& acVKBindings) @@ -105,25 +158,28 @@ LuaSandbox::LuaSandbox(Scripting* apScripting, const VKBindings& acVKBindings) void LuaSandbox::Initialize() { - auto lock = m_pScripting->GetState(); - auto& luaView = lock.Get(); + auto lockedState = m_pScripting->GetLockedState(); + auto& luaState = lockedState.Get(); + auto globals = luaState.globals(); // initialize state + environment first - m_env = {luaView, sol::create}; + m_env = {luaState, sol::create}; // copy whitelisted things from global table - const auto cGlobals = luaView.globals(); - for (const auto* cKey : s_cGlobalObjectsWhitelist) - m_env[cKey].set(cGlobals[cKey].get()); + for (const auto* cKey : s_cVMGlobalObjectsWhitelist) + { + m_env[cKey] = DeepCopySolObject(globals[cKey].get(), luaState); + MakeSolUsertypeImmutable(m_env[cKey], luaState); + } - // copy whitelisted libs from global table - for (const auto* cKey : s_cGlobalTablesWhitelist) - m_env[cKey].set(cGlobals[cKey].get()); + // copy whitelisted from our global table + for (const auto* cKey : s_cGlobalObjectsWhitelist) + MakeSolUsertypeImmutable(m_env[cKey], luaState); // copy safe os functions { - auto os = cGlobals["os"].get(); - sol::table osCopy(luaView, sol::create); + auto os = globals["os"].get(); + sol::table osCopy(luaState, sol::create); osCopy["clock"] = os["clock"]; osCopy["date"] = os["date"]; osCopy["difftime"] = os["difftime"]; @@ -131,174 +187,250 @@ void LuaSandbox::Initialize() m_env["os"] = osCopy; } - CreateSandbox(); + CreateSandbox("", "", false, false, false, false); +} + +void LuaSandbox::PostInitializeScripting() +{ + auto lockedState = m_pScripting->GetLockedState(); + auto& luaState = lockedState.Get(); + auto globals = luaState.globals(); + + // copy whitelisted from global table + for (const auto* cKey : s_cPostInitializeScriptingProtectedList) + MakeSolUsertypeImmutable(m_env[cKey], luaState); +} + +void LuaSandbox::PostInitializeTweakDB() +{ + auto lockedState = m_pScripting->GetLockedState(); + auto& luaState = lockedState.Get(); + auto globals = luaState.globals(); + + // copy whitelisted from global table + for (const auto* cKey : s_cPostInitializeTweakDBProtectedList) + MakeSolUsertypeImmutable(m_env[cKey], luaState); } -void LuaSandbox::PostInitialize() +void LuaSandbox::PostInitializeMods() { - auto lock = m_pScripting->GetState(); - auto& luaView = lock.Get(); + auto lockedState = m_pScripting->GetLockedState(); + auto& luaState = lockedState.Get(); + auto globals = luaState.globals(); - // make shared things immutable - for (const auto* cKey : s_cGlobalImmutablesList) - MakeSolUsertypeImmutable(luaView[cKey], luaView); + // copy whitelisted from global table + for (const auto* cKey : s_cPostInitializeModsProtectedList) + MakeSolUsertypeImmutable(m_env[cKey], luaState); } void LuaSandbox::ResetState() { - for (auto& cSandbox : m_sandboxes) - CloseDBForSandbox(cSandbox); + if (m_modules.empty()) + return; + + auto lockedState = m_pScripting->GetLockedState(); + auto& luaState = lockedState.Get(); + + for (size_t i = 1; i < m_sandboxes.size(); ++i) + CloseDBForSandbox(m_sandboxes[i]); m_modules.clear(); - if (m_sandboxes.size() > 1) // first one is always present, meant for console - m_sandboxes.erase(m_sandboxes.cbegin()+1, m_sandboxes.cend()); + m_sandboxes.erase(m_sandboxes.cbegin() + 1, m_sandboxes.cend()); - auto lock = m_pScripting->GetState(); - auto& luaView = lock.Get(); - luaView.collect_garbage(); + luaState.collect_garbage(); } -size_t LuaSandbox::CreateSandbox(const std::filesystem::path& acPath, const std::string& acName, bool aEnableExtraLibs, bool aEnableDB, bool aEnableIO, bool aEnableLogger) +uint64_t LuaSandbox::CreateSandbox(const std::filesystem::path& acPath, const std::string& acName, bool aEnableExtraLibs, bool aEnableDB, bool aEnableIO, bool aEnableLogger) { - const size_t cResID = m_sandboxes.size(); + const uint64_t cResID = m_sandboxes.size(); assert(!cResID || (!acPath.empty() && !acName.empty())); - auto& res = m_sandboxes.emplace_back(m_pScripting, m_env, acPath); + + auto lockedState = m_pScripting->GetLockedState(); + const auto& luaState = lockedState.Get(); + + auto& res = m_sandboxes.emplace_back(cResID, m_pScripting, m_env, acPath); if (!acPath.empty() && !acName.empty()) { if (aEnableExtraLibs) - InitializeExtraLibsForSandbox(res); + InitializeExtraLibsForSandbox(res, luaState); if (aEnableDB) - InitializeDBForSandbox(res); + InitializeDBForSandbox(res, luaState); if (aEnableIO) - InitializeIOForSandbox(res, acName); + InitializeIOForSandbox(res, luaState, acName); if (aEnableLogger) - InitializeLoggerForSandbox(res, acName); + InitializeLoggerForSandbox(res, luaState, acName); } return cResID; } -sol::protected_function_result LuaSandbox::ExecuteFile(const std::string& acPath) +sol::protected_function_result LuaSandbox::ExecuteFile(const std::string& acPath) const { - return m_sandboxes[0].ExecuteFile(acPath); + return m_pScripting->GetLockedState().Get().script_file(acPath, m_env, sol::load_mode::text); } -sol::protected_function_result LuaSandbox::ExecuteString(const std::string& acString) +sol::protected_function_result LuaSandbox::ExecuteString(const std::string& acString) const { - return m_sandboxes[0].ExecuteString(acString); + return m_pScripting->GetLockedState().Get().script(acString, m_env, sol:: detail::default_chunk_name(), sol::load_mode::text); } -Sandbox& LuaSandbox::operator[](size_t aID) +Sandbox& LuaSandbox::operator[](uint64_t aID) { assert(aID < m_sandboxes.size()); return m_sandboxes[aID]; } -const Sandbox& LuaSandbox::operator[](size_t aID) const +const Sandbox& LuaSandbox::operator[](uint64_t aID) const { assert(aID < m_sandboxes.size()); return m_sandboxes[aID]; } -TiltedPhoques::Locked LuaSandbox::GetState() const +TiltedPhoques::Locked LuaSandbox::GetLockedState() const { - return m_pScripting->GetState(); + return m_pScripting->GetLockedState(); } -void LuaSandbox::InitializeExtraLibsForSandbox(Sandbox& aSandbox) const +void LuaSandbox::SetImGuiAvailable(bool aAvailable) { - auto& sbEnv = aSandbox.GetEnvironment(); - sol::state_view sbStateView = GetState(); - const auto cSBRootPath = aSandbox.GetRootPath(); + auto lockedState = m_pScripting->GetLockedState(); + m_imguiAvailable = aAvailable; +} - auto lock = m_pScripting->GetState(); - auto& luaView = lock.Get(); +bool LuaSandbox::GetImGuiAvailable() const +{ + auto lockedState = m_pScripting->GetLockedState(); + return m_imguiAvailable; +} + +sol::environment& LuaSandbox::GetEnvironment() +{ + return m_env; +} + +void LuaSandbox::InitializeExtraLibsForSandbox(Sandbox& aSandbox, const sol::state& acpState) const +{ + auto& sbEnv = aSandbox.GetEnvironment(); + const auto& cSBRootPath = aSandbox.GetRootPath(); + auto globals = acpState.globals(); + sol::state_view stateView = acpState; // copy extra whitelisted libs from global table - const auto cGlobals = luaView.globals(); for (const auto* cKey : s_cGlobalExtraLibsWhitelist) - sbEnv[cKey] = DeepCopySolObject(cGlobals[cKey].get(), luaView); + { + sbEnv[cKey] = DeepCopySolObject(globals[cKey].get(), acpState); + MakeSolUsertypeImmutable(sbEnv[cKey], acpState); + } - auto ImGui = sbEnv.get("ImGui"); - Texture::BindTexture(ImGui); + sol::table imgui = sbEnv["ImGui"]; - const auto cLoadTexture = [cSBRootPath, sbStateView](const std::string& acPath) -> std::tuple, sol::object> { - auto absPath = absolute(cSBRootPath / acPath).make_preferred(); + imgui["LoadTexture"] = [this, cSBRootPath, stateView](const std::string& acPath) -> std::tuple, sol::object> { + if (!GetImGuiAvailable()) + return std::make_tuple(nullptr, make_object(stateView,"Tried to use ImGui outside of allowed events!")); - const auto cRelPathStr = relative(absPath, cSBRootPath).string(); - if (!exists(absPath) || !is_regular_file(absPath) || (cRelPathStr.find("..") != std::string::npos)) - return std::make_tuple(nullptr, make_object(sbStateView, "Invalid path!")); + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(cSBRootPath); - auto texture = Texture::Load(absPath.string()); + const auto path = GetLuaPath(acPath, cSBRootPath, false); + auto texture = Texture::Load(UTF16ToUTF8(path.native())); if (!texture) - return std::make_tuple(nullptr, make_object(sbStateView, "Failed to load '" + absPath.string() + "'")); + return std::make_tuple(nullptr, make_object(stateView, fmt::format("Failed to load '{}'", acPath))); + + current_path(previousCurrentPath); return std::make_tuple(texture, sol::nil); }; - ImGui.set_function("LoadTexture", cLoadTexture); } -void LuaSandbox::InitializeDBForSandbox(Sandbox& aSandbox) const +void LuaSandbox::InitializeDBForSandbox(Sandbox& aSandbox, const sol::state& acpState) { auto& sbEnv = aSandbox.GetEnvironment(); - const auto cSBRootPath = aSandbox.GetRootPath(); - - auto lock = m_pScripting->GetState(); - auto& luaView = lock.Get(); + const auto& cSBRootPath = aSandbox.GetRootPath(); + const auto sbId = aSandbox.GetId(); + auto globals = acpState.globals(); - const auto cGlobals = luaView.globals(); - const auto cSQLite3 = cGlobals["sqlite3"].get(); - sol::table sqlite3Copy(luaView, sol::create); + const auto cSQLite3 = globals["sqlite3"].get(); + sol::table sqlite3Copy(acpState, sol::create); for (const auto& cKV : cSQLite3) { const auto cKeyStr = cKV.first.as(); if (cKeyStr.compare(0, 4, "open")) - sqlite3Copy[cKV.first] = DeepCopySolObject(cKV.second, luaView); + sqlite3Copy[cKV.first] = DeepCopySolObject(cKV.second, acpState); } + const auto dbOpen = acpState["sqlite3"]["open"].get(); + sqlite3Copy["reopen"] = [this, sbId, dbOpen]{ + auto& sandbox = m_sandboxes[sbId]; + + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(sandbox.GetRootPath()); + + CloseDBForSandbox(sandbox); + sandbox.GetEnvironment()["db"] = dbOpen(UTF16ToUTF8(GetLuaPath(L"db.sqlite3", sandbox.GetRootPath(), true).native())); + + current_path(previousCurrentPath); + }; + sbEnv["sqlite3"] = sqlite3Copy; - sbEnv["db"] = luaView["sqlite3"]["open"]((cSBRootPath / "db.sqlite3").string()); + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(cSBRootPath); + + sbEnv["db"] = dbOpen(UTF16ToUTF8(GetLuaPath(L"db.sqlite3", cSBRootPath, true).native())); + + current_path(previousCurrentPath); } -void LuaSandbox::InitializeIOForSandbox(Sandbox& aSandbox, const std::string& acName) +void LuaSandbox::InitializeIOForSandbox(Sandbox& aSandbox, const sol::state& acpState, const std::string& acName) { - sol::state_view sbStateView = GetState(); - auto sbEnv = aSandbox.GetEnvironment(); - const auto cSBRootPath = aSandbox.GetRootPath(); + auto& sbEnv = aSandbox.GetEnvironment(); + const auto& cSBRootPath = aSandbox.GetRootPath(); + auto globals = acpState.globals(); + const auto cSBEnv = sbEnv; + sol::state_view stateView = acpState; - const auto cLoadString = [sbStateView, sbEnv](const std::string& acStr, const std::string &acChunkName) -> std::tuple + const auto cLoadString = [stateView, cSBEnv](const std::string& acStr, const std::string &acChunkName) -> std::tuple { - sol::state_view sv = sbStateView; - const sol::environment cEnv = sbEnv; + if (!acStr.empty() && acStr[0] == LUA_SIGNATURE[0]) + return std::make_tuple(sol::nil, make_object(stateView, "Bytecode prohibited!")); - if (!acStr.empty() && (acStr[0] == LUA_SIGNATURE[0])) - return std::make_tuple(sol::nil, make_object(sbStateView, "Bytecode prohibited!")); - - const auto& acKey = (acChunkName.empty()) ? (acStr) : (acChunkName); - const auto cResult = sv.load(acStr, acKey, sol::load_mode::text); + sol::state_view luaView = stateView; + const auto& acKey = acChunkName.empty() ? acStr : acChunkName; + const auto cResult = luaView.load(acStr, acKey, sol::load_mode::text); if (cResult.valid()) { const auto cFunc = cResult.get(); - cEnv.set_on(cFunc); + cSBEnv.set_on(cFunc); return std::make_tuple(cFunc, sol::nil); } - return std::make_tuple(sol::nil, make_object(sv, cResult.get().what())); + return std::make_tuple(sol::nil, make_object(stateView, cResult.get().what())); }; sbEnv["loadstring"] = cLoadString; - const auto cLoadFile = [cSBRootPath, cLoadString, sbStateView](const std::string& acPath) -> std::tuple + const auto cLoadFile = [cSBRootPath, cLoadString, stateView](const std::string& acPath) -> std::tuple { - auto absPath = absolute(cSBRootPath / acPath).make_preferred(); - if (!exists(absPath) || !is_regular_file(absPath)) - absPath += ".lua"; - const auto cRelPathStr = relative(absPath, cSBRootPath).string(); - if (!exists(absPath) || !is_regular_file(absPath) || (cRelPathStr.find("..") != std::string::npos)) - return std::make_tuple(sol::nil, make_object(sbStateView, "Invalid path!")); - - std::ifstream ifs(absPath); - const std::string cScriptString((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); - return cLoadString(cScriptString, "@" + absPath.string()); + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(cSBRootPath); + + auto path = GetLuaPath(acPath, cSBRootPath, false); + + if (path.empty() || !is_regular_file(path)) + path = GetLuaPath(acPath + ".lua", cSBRootPath, false); + + if (path.empty() || !is_regular_file(path)) + { + current_path(previousCurrentPath); + + return std::make_tuple(sol::nil, make_object(stateView, "Tried to access invalid path '" + acPath + "'!")); + } + + std::ifstream ifs(path); + const std::string cScriptString((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + auto result = cLoadString(cScriptString, "@" + UTF16ToUTF8(path.native())); + + current_path(previousCurrentPath); + + return result; }; sbEnv["loadfile"] = cLoadFile; @@ -312,39 +444,46 @@ void LuaSandbox::InitializeIOForSandbox(Sandbox& aSandbox, const std::string& ac return func().get(); // is OK, dofile should throw if there is an error, we try to copy it... }; - sbEnv["require"] = [this, cLoadString, cSBRootPath, sbStateView, sbEnv](const std::string& acPath) -> std::tuple + // TODO - add _LOADED table and fill in when module loads some value, react in these functions when the key is sol::nil + sbEnv["require"] = [this, cLoadString, cSBRootPath, stateView, cSBEnv](const std::string& acPath) -> std::tuple { - auto absPath = absolute(cSBRootPath / acPath).make_preferred(); - if (!exists(absPath) || !is_regular_file(absPath)) + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(cSBRootPath); + + auto path = GetLuaPath(acPath, cSBRootPath, false); + + if (path.empty() || !is_regular_file(path)) + path = GetLuaPath(acPath + ".lua", cSBRootPath, false); + + if (path.empty() || !is_regular_file(path)) + path = GetLuaPath(acPath + "\\init.lua", cSBRootPath, false); + + if (path.empty() || !is_regular_file(path)) { - auto absPath2 = absPath; - absPath2 += ".lua"; - if (exists(absPath2) && is_regular_file(absPath2)) - absPath = absPath2; - else - { - auto absPath3 = absPath; - absPath3 /= "init.lua"; - if (!exists(absPath3) || !is_regular_file(absPath3)) - return std::make_tuple(sol::nil, make_object(sbStateView, "Invalid path!")); - absPath = absPath3; - } + current_path(previousCurrentPath); + + return std::make_tuple(sol::nil, make_object(stateView, "Tried to access invalid path '" + acPath + "'!")); } - const auto cRelPathStr = relative(absPath, cSBRootPath).string(); - if ((cRelPathStr.find("..") != std::string::npos)) - return std::make_tuple(sol::nil, make_object(sbStateView, "Invalid path!")); - const auto cKey = absPath.string(); - const auto cExistingModule = this->m_modules.find(cKey); - if (cExistingModule != this->m_modules.end()) + const auto cKey = UTF16ToUTF8((cSBRootPath / path).native()); + const auto cExistingModule = m_modules.find(cKey); + if (cExistingModule != m_modules.end()) + { + current_path(previousCurrentPath); + return std::make_tuple(cExistingModule->second, sol::nil); + } - std::ifstream ifs(absPath); - const std::string cScriptString((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); - auto res = cLoadString(cScriptString, "@" + absPath.string()); + std::ifstream ifs(path); + const std::string cScriptString((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + auto res = cLoadString(cScriptString, "@" + cKey); auto obj = std::get<0>(res); if (obj == sol::nil) + { + current_path(previousCurrentPath); + return res; + } sol::function func = std::get<0>(res); if (func != sol::nil) @@ -357,71 +496,100 @@ void LuaSandbox::InitializeIOForSandbox(Sandbox& aSandbox, const std::string& ac } catch(std::exception& e) { - return std::make_tuple(sol::nil, make_object(sbStateView, e.what())); + current_path(previousCurrentPath); + + return std::make_tuple(sol::nil, make_object(stateView, e.what())); } if (result.valid()) { - auto obj = result.get(); - this->m_modules[cKey] = obj; - return std::make_tuple(obj, sol::nil); + auto resultObj = result.get(); + m_modules[cKey] = resultObj; + + current_path(previousCurrentPath); + + return std::make_tuple(resultObj, sol::nil); } sol::error err = result; - std::shared_ptr logger = sbEnv["__logger"].get>(); - logger->error("Error: Cannot load module '{}': {}", acPath, err.what()); + auto logger = cSBEnv["__logger"].get>(); + logger->error("Error: Cannot load module '{}': {}", acPath, err.what()); + + current_path(previousCurrentPath); - return std::make_tuple(sol::nil, make_object(sbStateView, err.what())); + return std::make_tuple(sol::nil, make_object(stateView, err.what())); } + + current_path(previousCurrentPath); + return res; }; - sbEnv["dir"] = [cSBRootPath, sbStateView](const std::string& acPath) -> sol::table + sbEnv["dir"] = [cSBRootPath, stateView](const std::string& acPath) -> sol::table { - sol::state_view sv = sbStateView; + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(cSBRootPath); + + const auto path = GetLuaPath(acPath, cSBRootPath, false); + + if (path.empty() || !is_directory(path)) + { + current_path(previousCurrentPath); - auto absPath = absolute(cSBRootPath / acPath).make_preferred(); - const auto cRelPathStr = relative(absPath, cSBRootPath).string(); - if (!exists(absPath) || !is_directory(absPath) || (cRelPathStr.find("..") != std::string::npos)) return sol::nil; + } - sol::table res(sv, sol::create); + sol::table res(stateView, sol::create); int index = 1; - for (const auto& file : std::filesystem::directory_iterator(absPath)) + for (const auto& entry : std::filesystem::directory_iterator(path)) { - sol::table item(sv, sol::create); - item["name"] = relative(file.path(), absPath).string(); - item["type"] = (file.is_directory()) ? ("directory") : ("file"); + sol::table item(stateView, sol::create); + item["name"] = UTF16ToUTF8(relative(entry.path(), path).native()); + item["type"] = entry.is_directory() ? "directory" : "file"; res[index++] = item; } + + current_path(previousCurrentPath); + return res; }; - auto lock = m_pScripting->GetState(); - auto& luaView = lock.Get(); - - const auto cGlobals = luaView.globals(); // define replacements for io lib { - const auto cIO = cGlobals["io"].get(); - sol::table ioSB(luaView, sol::create); - ioSB["type"] = DeepCopySolObject(cIO["type"], luaView); - ioSB["close"] = DeepCopySolObject(cIO["close"], luaView); + const auto cIO = globals["io"].get(); + sol::table ioSB(acpState, sol::create); + ioSB["read"] = DeepCopySolObject(cIO["read"], acpState); + ioSB["write"] = DeepCopySolObject(cIO["write"], acpState); + ioSB["input"] = DeepCopySolObject(cIO["input"], acpState); + ioSB["output"] = DeepCopySolObject(cIO["output"], acpState); + ioSB["type"] = DeepCopySolObject(cIO["type"], acpState); + ioSB["close"] = DeepCopySolObject(cIO["close"], acpState); ioSB["lines"] = [cIO, cSBRootPath](const std::string& acPath) { - const auto cAbsPath = absolute(cSBRootPath / acPath).make_preferred(); - const auto cRelPathStr = relative(cAbsPath, cSBRootPath).string(); - if (cRelPathStr.find("..") == std::string::npos) - return cIO["lines"](cAbsPath.string()); - return cIO["lines"](""); // simulate invalid input even though it may be valid - we dont want mod access outside! + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(cSBRootPath); + + const auto path = GetLuaPath(acPath, cSBRootPath, false); + + auto result = cIO["lines"](path.empty() || acPath == "db.sqlite3" ? "" : UTF16ToUTF8(path.native())); + + current_path(previousCurrentPath); + + return result; + }; const auto cOpenWithMode = [cIO, cSBRootPath](const std::string& acPath, const std::string& acMode) { - const auto cAbsPath = absolute(cSBRootPath / acPath).make_preferred(); - const auto cRelPathStr = relative(cAbsPath, cSBRootPath).string(); - if (cRelPathStr.find("..") == std::string::npos && (cRelPathStr != "db.sqlite3")) - return cIO["open"](cAbsPath.string(), acMode); - return cIO["open"]("", acMode); // simulate invalid input even though it may be valid - we dont want mod access outside! + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(cSBRootPath); + + const auto path = GetLuaPath(acPath, cSBRootPath, true); + + auto result = cIO["open"](path.empty() || acPath == "db.sqlite3" ? "" : UTF16ToUTF8(path.native()), acMode); + + current_path(previousCurrentPath); + + return result; }; auto cOpenDefault = [cOpenWithMode](const std::string& acPath) { @@ -431,77 +599,87 @@ void LuaSandbox::InitializeIOForSandbox(Sandbox& aSandbox, const std::string& ac sbEnv["io"] = ioSB; } - // add in rename and remove repacements for os lib + // add in rename and remove replacements for os lib { - const auto cOS = cGlobals["os"].get(); - sol::table osSB = sbEnv["os"].get(); + const auto cOS = globals["os"].get(); + sol::table osSB = DeepCopySolObject(m_env["os"].get(), acpState); osSB["rename"] = [cOS, cSBRootPath](const std::string& acOldPath, const std::string& acNewPath) -> std::tuple { - const auto cAbsOldPath = absolute(cSBRootPath / acOldPath).make_preferred(); - const auto cRelOldPathStr = relative(cAbsOldPath, cSBRootPath).string(); - if (!exists(cAbsOldPath) || (cRelOldPathStr.find("..") != std::string::npos) || (cRelOldPathStr == "db.sqlite3")) - return std::make_tuple(sol::nil, "Argument oldpath is invalid!"); - - const auto cAbsNewPath = absolute(cSBRootPath / acNewPath).make_preferred(); - const auto cRelNewPathStr = relative(cAbsNewPath, cSBRootPath).string(); - if (cRelNewPathStr.find("..") != std::string::npos) - return std::make_tuple(sol::nil, "Argument newpath is invalid!"); - - const auto cResult = cOS["rename"](cAbsOldPath.string(), cAbsNewPath.string()); - if (cResult.valid()) - return std::make_tuple(cResult.get(), ""); - return std::make_tuple(cResult.get(0), cResult.get(1)); + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(cSBRootPath); + + const auto oldPath = GetLuaPath(acOldPath, cSBRootPath, false); + if (oldPath.empty() || acOldPath == "db.sqlite3") + { + current_path(previousCurrentPath); + + return std::make_tuple(sol::nil, "Argument oldPath is invalid! ('" + acOldPath + "')"); + } + + const auto newPath = GetLuaPath(acOldPath, cSBRootPath, true); + if (newPath.empty() || exists(newPath)|| acNewPath == "db.sqlite3") + { + current_path(previousCurrentPath); + + return std::make_tuple(sol::nil, "Argument newPath is invalid! ('" + acNewPath + "')"); + } + + const auto cResult = cOS["rename"](UTF16ToUTF8(oldPath.native()), UTF16ToUTF8(newPath.native())); + + current_path(previousCurrentPath); + + return cResult.valid() ? std::make_tuple(cResult.get(), "") : std::make_tuple(cResult.get(0), cResult.get(1)); }; osSB["remove"] = [cOS, cSBRootPath](const std::string& acPath) -> std::tuple { - const auto cAbsPath = absolute(cSBRootPath / acPath).make_preferred(); - const auto cRelPathStr = relative(cAbsPath, cSBRootPath).string(); - if (!exists(cAbsPath) || (cRelPathStr.find("..") != std::string::npos) || (cRelPathStr == "db.sqlite3")) - return std::make_tuple(sol::nil, "Argument path is invalid!"); - - const auto cResult = cOS["remove"](cAbsPath.string()); - if (cResult.valid()) - return std::make_tuple(cResult.get(), ""); - return std::make_tuple(cResult.get(0), cResult.get(1)); + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(cSBRootPath); + + const auto path = GetLuaPath(acPath, cSBRootPath, false); + + const auto cResult = cOS["remove"](UTF16ToUTF8(path.native())); + + current_path(previousCurrentPath); + + return cResult.valid() ? std::make_tuple(cResult.get(), "") : std::make_tuple(cResult.get(0), cResult.get(1)); }; + sbEnv["os"] = osSB; } // add support functions for bindings sbEnv["IsBound"] = [vkb = &m_vkBindings, name = acName](const std::string& aID) -> bool { - return vkb->IsBound(name + '.' + aID); + return vkb->IsBound({name, aID}); }; sbEnv["GetBind"] = [vkb = &m_vkBindings, name = acName](const std::string& aID) -> std::string { - return vkb->GetBindString(name + '.' + aID); + return vkb->GetBindString({name, aID}); }; } -void LuaSandbox::InitializeLoggerForSandbox(Sandbox& aSandbox, const std::string& acName) const +void LuaSandbox::InitializeLoggerForSandbox(Sandbox& aSandbox, const sol::state& acpState, const std::string& acName) const { auto& sbEnv = aSandbox.GetEnvironment(); - const auto cSBRootPath = aSandbox.GetRootPath(); + const auto& cSBRootPath = aSandbox.GetRootPath(); // initialize logger for this mod - auto logger = CreateLogger(cSBRootPath / (acName + ".log"), acName); - - auto state = m_pScripting->GetState(); + auto logger = CreateLogger(GetAbsolutePath(acName + ".log", cSBRootPath, true), acName); // assign logger to mod so it can be used from within it too - sol::table spdlog(state.Get(), sol::create); - spdlog["trace"] = [logger](const std::string& message) { logger->trace(message); }; - spdlog["debug"] = [logger](const std::string& message) { logger->debug(message); }; - spdlog["info"] = [logger](const std::string& message) { logger->info(message); }; - spdlog["warning"] = [logger](const std::string& message) { logger->warn(message); }; - spdlog["error"] = [logger](const std::string& message) { logger->error(message); }; - spdlog["critical"] = [logger](const std::string& message) { logger->critical(message); }; + sol::table spdlog(acpState, sol::create); + spdlog["trace"] = [logger](const std::string& message) { logger->trace("{}", message); }; + spdlog["debug"] = [logger](const std::string& message) { logger->debug("{}", message); }; + spdlog["info"] = [logger](const std::string& message) { logger->info("{}", message); }; + spdlog["warning"] = [logger](const std::string& message) { logger->warn("{}", message); }; + spdlog["error"] = [logger](const std::string& message) { logger->error("{}", message); }; + spdlog["critical"] = [logger](const std::string& message) { logger->critical("{}", message); }; sbEnv["spdlog"] = spdlog; // assign logger to special var so we can access it from our functions sbEnv["__logger"] = logger; } -void LuaSandbox::CloseDBForSandbox(Sandbox& aSandbox) const +void LuaSandbox::CloseDBForSandbox(const Sandbox& aSandbox) const { aSandbox.ExecuteString(R"( if type(db) == 'userdata' and tostring(db):find('^sqlite database') then diff --git a/src/scripting/LuaSandbox.h b/src/scripting/LuaSandbox.h index 32f3f2f9..59f01988 100644 --- a/src/scripting/LuaSandbox.h +++ b/src/scripting/LuaSandbox.h @@ -8,31 +8,41 @@ struct LuaSandbox ~LuaSandbox() = default; void Initialize(); - void PostInitialize(); + void PostInitializeScripting(); + void PostInitializeTweakDB(); + void PostInitializeMods(); + void ResetState(); - size_t CreateSandbox(const std::filesystem::path& acPath = "", const std::string& acName = "", bool aEnableExtraLibs = true, bool aEnableDB = true, bool aEnableIO = true, bool aEnableLogger = true); - - sol::protected_function_result ExecuteFile(const std::string& acPath); - sol::protected_function_result ExecuteString(const std::string& acString); - - Sandbox& operator[](size_t aID); - const Sandbox& operator[](size_t aID) const; + uint64_t CreateSandbox(const std::filesystem::path& acPath = "", const std::string& acName = "", bool aEnableExtraLibs = true, bool aEnableDB = true, bool aEnableIO = true, bool aEnableLogger = true); + + sol::protected_function_result ExecuteFile(const std::string& acPath) const; + sol::protected_function_result ExecuteString(const std::string& acString) const; + + Sandbox& operator[](uint64_t aID); + const Sandbox& operator[](uint64_t aID) const; + + [[nodiscard]] TiltedPhoques::Locked GetLockedState() const; + + void SetImGuiAvailable(bool aAvailable); + bool GetImGuiAvailable() const; + + sol::environment& GetEnvironment(); - [[nodiscard]] TiltedPhoques::Locked GetState() const; - private: - void InitializeExtraLibsForSandbox(Sandbox& aSandbox) const; - void InitializeDBForSandbox(Sandbox& aSandbox) const; - void InitializeIOForSandbox(Sandbox& aSandbox, const std::string& acName); - void InitializeLoggerForSandbox(Sandbox& aSandbox, const std::string& acName) const; + void InitializeExtraLibsForSandbox(Sandbox& aSandbox, const sol::state& acpState) const; + void InitializeDBForSandbox(Sandbox& aSandbox, const sol::state& acpState); + void InitializeIOForSandbox(Sandbox& aSandbox, const sol::state& acpState, const std::string& acName); + void InitializeLoggerForSandbox(Sandbox& aSandbox, const sol::state& acpState, const std::string& acName) const; - void CloseDBForSandbox(Sandbox& aSandbox) const; + void CloseDBForSandbox(const Sandbox& aSandbox) const; Scripting* m_pScripting; const VKBindings& m_vkBindings; - sol::environment m_env{ }; + sol::environment m_env{}; TiltedPhoques::Vector m_sandboxes{}; - TiltedPhoques::Map m_modules{ }; + TiltedPhoques::Map m_modules{}; + + bool m_imguiAvailable{ false }; }; diff --git a/src/scripting/LuaVM.cpp b/src/scripting/LuaVM.cpp index 4968cdf7..d3f50ff8 100644 --- a/src/scripting/LuaVM.cpp +++ b/src/scripting/LuaVM.cpp @@ -1,15 +1,25 @@ #include #include "LuaVM.h" -#include "CET.h" -#include "overlay/Overlay.h" -const TiltedPhoques::Vector& LuaVM::GetBinds() const +#include + +const VKBind* LuaVM::GetBind(const VKModBind& acModBind) const +{ + return m_scripting.GetBind(acModBind); +} + +const TiltedPhoques::Vector* LuaVM::GetBinds(const std::string& acModName) const { - return m_scripting.GetBinds(); + return m_scripting.GetBinds(acModName); } -bool LuaVM::ExecuteLua(const std::string& acCommand) +const TiltedPhoques::Map>>& LuaVM::GetAllBinds() const +{ + return m_scripting.GetAllBinds(); +} + +bool LuaVM::ExecuteLua(const std::string& acCommand) const { if (!m_initialized) { @@ -23,17 +33,24 @@ bool LuaVM::ExecuteLua(const std::string& acCommand) void LuaVM::Update(float aDeltaTime) { if (!m_initialized) + return; + + CET::Get().GetBindings().Update(); + + if (m_reload) { - if (m_logCount.load(std::memory_order_relaxed) > 0) - PostInitializeMods(); + m_scripting.ReloadAllMods(); - return; + m_reload = false; } m_scripting.TriggerOnUpdate(aDeltaTime); + + if (!m_d3d12.IsImGuiPresentDraw()) + m_d3d12.PrepareUpdate(); } -void LuaVM::Draw() +void LuaVM::Draw() const { if (!m_initialized || m_drawBlocked) return; @@ -44,16 +61,7 @@ void LuaVM::Draw() void LuaVM::ReloadAllMods() { if (m_initialized) - { - m_scripting.ReloadAllMods(); - m_scripting.TriggerOnTweak(); - m_scripting.TriggerOnInit(); - - if (CET::Get().GetOverlay().IsEnabled()) - m_scripting.TriggerOnOverlayOpen(); - - spdlog::get("scripting")->info("LuaVM: Reloaded all mods!"); - } + m_reload = true; } void LuaVM::OnOverlayOpen() const @@ -71,9 +79,7 @@ void LuaVM::OnOverlayClose() const void LuaVM::Initialize() { if (!IsInitialized()) - { m_scripting.Initialize(); - } } bool LuaVM::IsInitialized() const @@ -88,7 +94,7 @@ void LuaVM::BlockDraw(bool aBlockDraw) void LuaVM::RemoveTDBIDDerivedFrom(uint64_t aDBID) { - std::lock_guard _{ m_tdbidLock }; + std::lock_guard _{ m_tdbidLock }; const auto it = m_tdbidDerivedLookup.find(aDBID); if (it != m_tdbidDerivedLookup.end()) @@ -104,20 +110,20 @@ void LuaVM::RemoveTDBIDDerivedFrom(uint64_t aDBID) bool LuaVM::GetTDBIDDerivedFrom(uint64_t aDBID, TiltedPhoques::Vector& aDerivedList) { - std::shared_lock _{ m_tdbidLock }; + std::lock_guard _{ m_tdbidLock }; const auto it = m_tdbidDerivedLookup.find(aDBID & 0xFFFFFFFFFF); if (it == m_tdbidDerivedLookup.end()) return false; aDerivedList.reserve(it->second.size()); - std::copy(it->second.begin(), it->second.end(), std::back_inserter(aDerivedList)); + std::ranges::copy(it->second, std::back_inserter(aDerivedList)); return true; } uint64_t LuaVM::GetTDBIDBase(uint64_t aDBID) { - std::shared_lock _{ m_tdbidLock }; + std::lock_guard _{ m_tdbidLock }; const auto it = m_tdbidLookup.find(aDBID & 0xFFFFFFFFFF); if (it == m_tdbidLookup.end()) @@ -127,7 +133,7 @@ uint64_t LuaVM::GetTDBIDBase(uint64_t aDBID) TDBIDLookupEntry LuaVM::GetTDBIDLookupEntry(uint64_t aDBID) { - std::shared_lock _{ m_tdbidLock }; + std::lock_guard _{ m_tdbidLock }; const auto it = m_tdbidLookup.find(aDBID & 0xFFFFFFFFFF); if (it == m_tdbidLookup.end()) @@ -136,7 +142,7 @@ TDBIDLookupEntry LuaVM::GetTDBIDLookupEntry(uint64_t aDBID) return it->second; } -std::string LuaVM::GetTDBDIDDebugString(TDBID aDBID) +std::string LuaVM::GetTDBDIDDebugString(TDBID aDBID) const { RED4ext::TweakDBID internal(aDBID.value); return internal.HasTDBOffset() @@ -144,13 +150,13 @@ std::string LuaVM::GetTDBDIDDebugString(TDBID aDBID) : fmt::format("", internal.name.hash, internal.name.length); } -std::string LuaVM::GetTDBIDString(uint64_t aDBID) +std::string LuaVM::GetTDBIDString(uint64_t aDBID, bool aOnlyRegistered) { - std::shared_lock _{ m_tdbidLock }; + std::lock_guard _{ m_tdbidLock }; auto it = m_tdbidLookup.find(aDBID & 0xFFFFFFFFFF); if (it == m_tdbidLookup.end()) - return GetTDBDIDDebugString(TDBID{ aDBID }); + return aOnlyRegistered ? "" : GetTDBDIDDebugString(TDBID{ aDBID }); std::string string = it->second.name; uint64_t base = it->second.base; @@ -169,12 +175,12 @@ std::string LuaVM::GetTDBIDString(uint64_t aDBID) return string; } -void LuaVM::RegisterTDBIDString(uint64_t aValue, uint64_t aBase, const std::string& aName) +void LuaVM::RegisterTDBIDString(uint64_t aValue, uint64_t aBase, const std::string& acString) { if (aValue == 0) return; - std::lock_guard _{ m_tdbidLock }; + std::lock_guard _{ m_tdbidLock }; - m_tdbidLookup[aValue] = { aBase, aName }; + m_tdbidLookup[aValue] = { aBase, acString }; if (aBase != 0) m_tdbidDerivedLookup[aBase].insert(aValue); } @@ -186,7 +192,7 @@ void LuaVM::PostInitializeScripting() void LuaVM::PostInitializeTweakDB() { - m_scripting.TriggerOnTweak(); + m_scripting.PostInitializeTweakDB(); } void LuaVM::PostInitializeMods() @@ -194,9 +200,6 @@ void LuaVM::PostInitializeMods() assert(!m_initialized); m_scripting.PostInitializeMods(); - m_scripting.TriggerOnInit(); - if (CET::Get().GetOverlay().IsEnabled()) - m_scripting.TriggerOnOverlayOpen(); spdlog::get("scripting")->info("LuaVM: initialization finished!"); diff --git a/src/scripting/LuaVM.h b/src/scripting/LuaVM.h index 055df033..ae21b4fc 100644 --- a/src/scripting/LuaVM.h +++ b/src/scripting/LuaVM.h @@ -10,9 +10,11 @@ struct UnknownString; using TSetMousePosition = BOOL(void*, HWND, long, long); using TTDBIDCtorDerive = TDBID*(const TDBID*, TDBID*, const char*); using TRunningStateRun = bool(uintptr_t, uintptr_t); +using TShutdownStateRun = bool(uintptr_t, uintptr_t); using TSetLoadingState = uintptr_t(uintptr_t, int); using TTweakDBLoad = uint64_t(uintptr_t, uintptr_t); using TTranslateBytecode = bool(uintptr_t, uintptr_t); +using TPlayerSpawned = uint64_t(uint64_t, uint64_t, uint64_t, uint64_t); struct TDBIDLookupEntry { @@ -23,15 +25,17 @@ struct TDBIDLookupEntry struct Image; struct LuaVM { - LuaVM(Paths& aPaths, VKBindings& aBindings, D3D12& aD3D12, Options& aOptions); - ~LuaVM(); + LuaVM(const Paths& aPaths, VKBindings& aBindings, D3D12& aD3D12); + ~LuaVM() = default; + + [[nodiscard]] const VKBind* GetBind(const VKModBind& acModBind) const; + [[nodiscard]] const TiltedPhoques::Vector* GetBinds(const std::string& acModName) const; + [[nodiscard]] const TiltedPhoques::Map>>& GetAllBinds() const; + + bool ExecuteLua(const std::string& acCommand) const; - const TiltedPhoques::Vector& GetBinds() const; - - bool ExecuteLua(const std::string& acCommand); - void Update(float aDeltaTime); - void Draw(); + void Draw() const; void ReloadAllMods(); void OnOverlayOpen() const; @@ -40,7 +44,7 @@ struct LuaVM void Initialize(); bool IsInitialized() const; - + void BlockDraw(bool aBlockDraw); // Used by TweakDB when you delete a custom record @@ -48,8 +52,8 @@ struct LuaVM bool GetTDBIDDerivedFrom(uint64_t aDBID, TiltedPhoques::Vector& aDerivedList); uint64_t GetTDBIDBase(uint64_t aDBID); TDBIDLookupEntry GetTDBIDLookupEntry(uint64_t aDBID); - std::string GetTDBDIDDebugString(TDBID aDBID); - std::string GetTDBIDString(uint64_t aDBID); + std::string GetTDBDIDDebugString(TDBID aDBID) const; + std::string GetTDBIDString(uint64_t aDBID, bool aOnlyRegistered = false); void RegisterTDBIDString(uint64_t aValue, uint64_t aBase, const std::string& acString); @@ -58,43 +62,44 @@ struct LuaVM void PostInitializeMods(); protected: - - void Hook(Options& aOptions); + + void Hook(); static void HookLog(RED4ext::IScriptable*, RED4ext::CStackFrame* apStack, void*, void*); static void HookLogChannel(RED4ext::IScriptable*, RED4ext::CStackFrame* apStack, void*, void*); static void HookTDBIDToStringDEBUG(RED4ext::IScriptable*, RED4ext::CStackFrame* apStack, void* apResult, void*); static TDBID* HookTDBIDCtorDerive(TDBID* apBase, TDBID* apThis, const char* acpName); static bool HookRunningStateRun(uintptr_t aThis, uintptr_t aApp); + static bool HookShutdownStateRun(uintptr_t aThis, uintptr_t aApp); static uintptr_t HookSetLoadingState(uintptr_t aThis, int aState); static uint64_t HookTweakDBLoad(uintptr_t aThis, uintptr_t aParam); static bool HookTranslateBytecode(uintptr_t aBinder, uintptr_t aData); + static uint64_t HookPlayerSpawned(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4); private: - - std::shared_mutex m_tdbidLock{ }; + + std::mutex m_tdbidLock{ }; TiltedPhoques::Map m_tdbidLookup{ }; // Used by TweakDB to get the flats associated with a record TiltedPhoques::Map> m_tdbidDerivedLookup{ }; - + RED4ext::OpcodeHandlers::Handler_t m_realLog{ nullptr }; RED4ext::OpcodeHandlers::Handler_t m_realLogChannel{nullptr}; RED4ext::OpcodeHandlers::Handler_t m_realTDBIDToStringDEBUG{nullptr}; TTDBIDCtorDerive* m_realTDBIDCtorDerive{ nullptr }; TRunningStateRun* m_realRunningStateRun{ nullptr }; + TShutdownStateRun* m_realShutdownStateRun{ nullptr }; TSetLoadingState* m_realSetLoadingState{ nullptr }; TTweakDBLoad* m_realTweakDBLoad{ nullptr }; TTranslateBytecode* m_realTranslateBytecode{ nullptr }; - - std::chrono::time_point m_lastframe; - - std::atomic m_logCount{ 0 }; + TPlayerSpawned* m_realPlayerSpawned{ nullptr }; Scripting m_scripting; bool m_initialized{ false }; bool m_drawBlocked{ false }; - + bool m_reload{ false }; + D3D12& m_d3d12; - size_t m_connectUpdate; + std::chrono::time_point m_lastframe{ std::chrono::high_resolution_clock::now() }; }; diff --git a/src/scripting/LuaVM_Hooks.cpp b/src/scripting/LuaVM_Hooks.cpp index da8c41c6..5fc017ca 100644 --- a/src/scripting/LuaVM_Hooks.cpp +++ b/src/scripting/LuaVM_Hooks.cpp @@ -1,10 +1,192 @@ #include -#include "CET.h" #include "LuaVM.h" -#include +#include +#include + #include +#include + +namespace +{ + +LuaVM* s_vm{ nullptr }; + +using TOodleLZ_Decompress = size_t(*)(char *in, int insz, char *out, int outsz, int wantsFuzzSafety, int b, int c, void *d, void *e, void *f, void *g, void *workBuffer, size_t workBufferSize, int j); + +struct Header +{ + uint32_t m_magic = 0; // a hash of all types currently supported + uint32_t m_version = 0; // 1 + uint32_t m_recordsCount = 0; + uint32_t m_flatsCount = 0; + uint32_t m_queriesCount = 0; +}; + +int32_t ReadCompressedInt(std::istream& aFile) +{ + int32_t uncompressed = 0; + + int8_t byte; + aFile.read(reinterpret_cast(&byte), 1); + uncompressed |= byte & 0x3F; + if (byte & 0x40) + { + aFile.read(reinterpret_cast(&byte), 1); + uncompressed |= (byte & 0x7F) << 6; + if (byte & 0x80) + { + aFile.read(reinterpret_cast(&byte), 1); + uncompressed |= (byte & 0x7F) << 13; + if (byte & 0x80) + { + aFile.read(reinterpret_cast(&byte), 1); + uncompressed |= (byte & 0x7F) << 20; + if (byte & 0x80) + { + aFile.read(reinterpret_cast(&byte), 1); + uncompressed |= byte << 27; + } + } + } + } + + return uncompressed; +} + +void ReadTDBIDNameArray(std::istream& aFile, uint32_t aCount, TiltedPhoques::Map& aOutMap) +{ + for (uint32_t i = 0; i < aCount; ++i) + { + const int32_t length = ReadCompressedInt(aFile); + std::string str; + str.resize(length); + aFile.read(str.data(), length); + aOutMap.try_emplace(TweakDBID(str).value, TDBIDLookupEntry{0, str}); + } +} + +bool InitializeTweakDBMetadata(TiltedPhoques::Map& lookup) +{ + auto hOodleHandle = GetModuleHandle(TEXT("oo2ext_7_win64.dll")); + if (hOodleHandle == nullptr) + { + Log::Error("CDPRTweakDBMetadata::Initalize() - Could not get Oodle access"); + return false; + } + + auto OodleLZ_Decompress = reinterpret_cast(GetProcAddress(hOodleHandle, "OodleLZ_Decompress")); + if (OodleLZ_Decompress == nullptr) + { + Log::Error("CDPRTweakDBMetadata::Initalize() - Could not get OodleLZ_Decompress"); + return false; + } + + const auto tdbstrPathRAW = GetAbsolutePath(L"tweakdb.str", CET::Get().GetPaths().TweakDB(), false, true); + if (!tdbstrPathRAW.empty()) + { + try + { + std::ifstream file(tdbstrPathRAW, std::ios::binary); + file.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit); + Header header; + file.read(reinterpret_cast(&header), sizeof(Header)); + assert(header.m_version == 1); + ReadTDBIDNameArray(file, header.m_recordsCount, lookup); + ReadTDBIDNameArray(file, header.m_flatsCount, lookup); + ReadTDBIDNameArray(file, header.m_queriesCount, lookup); + } + // this is easier for now + catch (std::exception&) + { + Log::Error("CDPRTweakDBMetadata::Initalize() - Failed to load tweakdb.str!"); + return false; + } + } + else + { + const auto tdbstrPath = GetAbsolutePath(L"tweakdbstr.kark", CET::Get().GetPaths().TweakDB(), false, true); + if (tdbstrPath.empty()) + { + Log::Error("CDPRTweakDBMetadata::Initalize() - Missing tweakdbstr.kark!"); + return false; + } + + try + { + constexpr size_t headerSize = 8; + auto encodedBytes = ReadWholeBinaryFile(tdbstrPath); + + std::string decodedBytes; + decodedBytes.resize(reinterpret_cast(encodedBytes.data())[1]); + + char workingMemory[0x80000]; + auto size = OodleLZ_Decompress(encodedBytes.data() + headerSize, static_cast(encodedBytes.size() - headerSize), decodedBytes.data(), + static_cast(decodedBytes.size()), 1, 1, 0, nullptr, nullptr, nullptr, nullptr, workingMemory, std::size(workingMemory), 3); + + assert(size == decodedBytes.size()); + if (size != decodedBytes.size()) + { + Log::Error("CDPRTweakDBMetadata::Initalize() - Failed to decompress tweakdbstr.kark!"); + return false; + } + + struct inline_buffer: std::streambuf { + inline_buffer(char* base, std::ptrdiff_t n) { + this->setg(base, base, base + n); + } + } tdbstrDecodedBuffer(decodedBytes.data(), decodedBytes.size()); + std::istream tdbstrDecodedFile(&tdbstrDecodedBuffer); + tdbstrDecodedFile.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit); + + Header header; + tdbstrDecodedFile.read(reinterpret_cast(&header), sizeof(Header)); + assert(header.m_version == 1); + ReadTDBIDNameArray(tdbstrDecodedFile, header.m_recordsCount, lookup); + ReadTDBIDNameArray(tdbstrDecodedFile, header.m_flatsCount, lookup); + ReadTDBIDNameArray(tdbstrDecodedFile, header.m_queriesCount, lookup); + + Log::Info("CDPRTweakDBMetadata::Initalize() - Primary TweakDB initialization successful!"); + } + // this is easier for now + catch (std::exception&) + { + Log::Error("CDPRTweakDBMetadata::Initalize() - Failed to load tweakdbstr.kark!"); + return false; + } + } + + // check if we have a tweakdb that was changed by REDmod + const auto moddedTweakDbFilePath = GetAbsolutePath(L"tweakdb.str", CET::Get().GetPaths().R6CacheModdedRoot(), false, true); + if (moddedTweakDbFilePath.empty()) + return true; + + try + { + std::ifstream file(moddedTweakDbFilePath, std::ios::binary); + file.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit); + + Header header; + file.read(reinterpret_cast(&header), sizeof(Header)); + assert(header.m_version == 1); + ReadTDBIDNameArray(file, header.m_recordsCount, lookup); + ReadTDBIDNameArray(file, header.m_flatsCount, lookup); + ReadTDBIDNameArray(file, header.m_queriesCount, lookup); + Log::Info("CDPRTweakDBMetadata::Initalize() - REDMod TweakDB initialization successful!"); + } + // this is easier for now + // do not modify the return state since this isn't the main TweakDB + catch (std::exception&) + { + Log::Warn("CDPRTweakDBMetadata::Initalize() - Failed to load REDMod TweakDB. Modded entries may not " + "be shown in the TweakDB Editor."); + } + + return true; +} + +} void LuaVM::HookLog(RED4ext::IScriptable*, RED4ext::CStackFrame* apStack, void*, void*) { @@ -12,34 +194,33 @@ void LuaVM::HookLog(RED4ext::IScriptable*, RED4ext::CStackFrame* apStack, void*, RED4ext::CString text{}; - apStack->data = 0; - apStack->dataType = 0; + apStack->data = nullptr; + apStack->dataType = nullptr; RED4ext::ScriptRef ref; ref.innerType = s_stringLocator; ref.ref = &text; - ref.innerType->GetName(ref.hash); + ref.hash = ref.innerType->GetName(); apStack->currentParam++; - const auto opcode = *(apStack->code++); + const auto opcode = *apStack->code++; RED4ext::OpcodeHandlers::Run(opcode, apStack->context, apStack, &ref, &ref); apStack->code++; // skip ParamEnd - auto& console = CET::Get().GetOverlay().GetConsole(); - if (console.GameLogEnabled()) - spdlog::get("scripting")->info(ref.ref->c_str()); + spdlog::get("gamelog")->info("{}", ref.ref->c_str()); } void LuaVM::HookLogChannel(RED4ext::IScriptable*, RED4ext::CStackFrame* apStack, void*, void*) { static RTTILocator s_stringLocator("String"); static RED4ext::CName s_debugChannel("DEBUG"); + static RED4ext::CName s_assertionChannel("ASSERT"); RED4ext::CName channel; - apStack->data = 0; - apStack->dataType = 0; - uint8_t opcode = *(apStack->code++); + apStack->data = nullptr; + apStack->dataType = nullptr; + uint8_t opcode = *apStack->code++; apStack->currentParam++; RED4ext::OpcodeHandlers::Run(opcode, apStack->context, apStack, &channel, nullptr); @@ -48,266 +229,286 @@ void LuaVM::HookLogChannel(RED4ext::IScriptable*, RED4ext::CStackFrame* apStack, RED4ext::ScriptRef ref; ref.innerType = s_stringLocator; ref.ref = &text; - ref.innerType->GetName(ref.hash); + ref.hash = ref.innerType->GetName(); apStack->currentParam++; apStack->data = nullptr; apStack->dataType = nullptr; - opcode = *(apStack->code++); + opcode = *apStack->code++; RED4ext::OpcodeHandlers::Run(opcode, apStack->context, apStack, &ref, &ref); apStack->code++; // skip ParamEnd - auto& console = CET::Get().GetOverlay().GetConsole(); - if (console.GameLogEnabled() || channel == s_debugChannel) + std::string_view channelSV = channel.ToString(); + if (channelSV.empty()) + spdlog::get("gamelog")->info("[?{:X}] {}", channel.hash, ref.ref->c_str()); + else { - auto consoleLogger = spdlog::get("scripting"); - - std::string_view textSV = ref.ref->c_str(); if (channel == s_debugChannel) - { - consoleLogger->info(textSV); - } + spdlog::get("gamelog")->debug("[{}] {}", channelSV, ref.ref->c_str()); + else if (channel == s_assertionChannel) + spdlog::get("gamelog")->warn("[{}] {}", channelSV, ref.ref->c_str()); else - { - std::string_view channelSV = channel.ToString(); - if (channelSV.empty()) - consoleLogger->info("[?{0:x}] {}", channel.hash, textSV); - else - consoleLogger->info("[{}] {}", channelSV, textSV); - } + spdlog::get("gamelog")->info("[{}] {}", channelSV, ref.ref->c_str()); } - - CET::Get().GetVM().m_logCount.fetch_add(1); } -LuaVM::LuaVM(Paths& aPaths, VKBindings& aBindings, D3D12& aD3D12, Options& aOptions) - : m_scripting(aPaths, aBindings, aD3D12, aOptions) +LuaVM::LuaVM(const Paths& aPaths, VKBindings& aBindings, D3D12& aD3D12) + : m_scripting(aPaths, aBindings, aD3D12) , m_d3d12(aD3D12) - , m_lastframe(std::chrono::high_resolution_clock::now()) { - Hook(aOptions); + Hook(); + + // launch initialization of TweakDBID lookup and resource list on separate thread to not hog the game + // resource list is currently only used by TweakDB Editor widget, it checks for initialization of this + // before trying to access it or TweakDBID lookup + // TweakDBID lookup is on top locked for the duration of the initialization so accessing it should not + // be possible until it finishes + std::thread([this] + { + { + std::lock_guard _{ m_tdbidLock }; + InitializeTweakDBMetadata(m_tdbidLookup); + } - m_connectUpdate = aD3D12.OnUpdate.Connect([this]() { Draw(); }); -} + ResourcesList::Get()->Initialize(); + }).detach(); -LuaVM::~LuaVM() -{ - m_d3d12.OnUpdate.Disconnect(m_connectUpdate); + aBindings.SetVM(this); } TDBID* LuaVM::HookTDBIDCtorDerive(TDBID* apBase, TDBID* apThis, const char* acpName) { - auto& luavm = CET::Get().GetVM(); - auto result = luavm.m_realTDBIDCtorDerive(apBase, apThis, acpName); - luavm.RegisterTDBIDString(apThis->value, apBase->value, std::string(acpName)); + const auto result = s_vm->m_realTDBIDCtorDerive(apBase, apThis, acpName); + s_vm->RegisterTDBIDString(apThis->value, apBase->value, std::string(acpName)); return result; } void LuaVM::HookTDBIDToStringDEBUG(RED4ext::IScriptable*, RED4ext::CStackFrame* apStack, void* apResult, void*) { TDBID tdbid; - apStack->data = 0; - apStack->dataType = 0; + apStack->data = nullptr; + apStack->dataType = nullptr; apStack->currentParam++; - uint8_t opcode = *(apStack->code++); + const uint8_t opcode = *apStack->code++; RED4ext::OpcodeHandlers::Run(opcode, apStack->context, apStack, &tdbid, nullptr); apStack->code++; // skip ParamEnd if (apResult) { - std::string name = CET::Get().GetVM().GetTDBIDString(tdbid.value); - RED4ext::CString s(name.c_str()); + const std::string name = s_vm->GetTDBIDString(tdbid.value); + const RED4ext::CString s(name.c_str()); *static_cast(apResult) = s; } } bool LuaVM::HookRunningStateRun(uintptr_t aThis, uintptr_t aApp) { - auto& luavm = CET::Get().GetVM(); - const auto cNow = std::chrono::high_resolution_clock::now(); - const auto cDelta = cNow - luavm.m_lastframe; - auto cSeconds = std::chrono::duration_cast>(cDelta); + const auto cDelta = cNow - s_vm->m_lastframe; + const auto cSeconds = std::chrono::duration_cast>(cDelta); - luavm.Update(cSeconds.count()); + s_vm->Update(cSeconds.count()); - luavm.m_lastframe = cNow; + s_vm->m_lastframe = cNow; - return luavm.m_realRunningStateRun(aThis, aApp); + return s_vm->m_realRunningStateRun(aThis, aApp); } -uintptr_t LuaVM::HookSetLoadingState(uintptr_t aThis, int aState) +bool LuaVM::HookShutdownStateRun(uintptr_t aThis, uintptr_t aApp) { - auto& luavm = CET::Get().GetVM(); + s_vm->m_scripting.UnloadAllMods(); + return s_vm->m_realShutdownStateRun(aThis, aApp); +} + +uintptr_t LuaVM::HookSetLoadingState(uintptr_t aThis, int aState) +{ static std::once_flag s_initBarrier; if (aState == 2) { - std::call_once(s_initBarrier, [&luavm]() + std::call_once(s_initBarrier, [] { - luavm.PostInitializeMods(); + s_vm->PostInitializeMods(); }); } - return luavm.m_realSetLoadingState(aThis, aState); + return s_vm->m_realSetLoadingState(aThis, aState); } bool LuaVM::HookTranslateBytecode(uintptr_t aBinder, uintptr_t aData) { - auto& luavm = CET::Get().GetVM(); - - const auto ret = luavm.m_realTranslateBytecode(aBinder, aData); + const auto ret = s_vm->m_realTranslateBytecode(aBinder, aData); if (ret) { - luavm.PostInitializeScripting(); + s_vm->PostInitializeScripting(); } return ret; } -uint64_t LuaVM::HookTweakDBLoad(uintptr_t aThis, uintptr_t aParam) +uint64_t LuaVM::HookPlayerSpawned(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4) { - auto& luavm = CET::Get().GetVM(); + const auto ret = s_vm->m_realPlayerSpawned(a1, a2, a3, a4); - const auto ret = luavm.m_realTweakDBLoad(aThis, aParam); + if (!s_vm->m_initialized) + s_vm->PostInitializeMods(); - luavm.PostInitializeTweakDB(); + return ret; +} + +uint64_t LuaVM::HookTweakDBLoad(uintptr_t aThis, uintptr_t aParam) +{ + const auto ret = s_vm->m_realTweakDBLoad(aThis, aParam); + + s_vm->PostInitializeTweakDB(); return ret; } -void LuaVM::Hook(Options& aOptions) +void LuaVM::Hook() { + s_vm = this; + + { + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_Log); + uint8_t* pLocation = func.GetAddr(); + + if (pLocation) + { + if (MH_CreateHook(pLocation, reinterpret_cast(&HookLog), reinterpret_cast(&m_realLog)) != MH_OK || + MH_EnableHook(pLocation) != MH_OK) + Log::Error("Could not hook CScript::Log function!"); + else + Log::Info("CScript::Log function hook complete!"); + } + } + { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_Log); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_LogChannel); uint8_t* pLocation = func.GetAddr(); if (pLocation) { - if (MH_CreateHook(pLocation, &HookLog, reinterpret_cast(&m_realLog)) != MH_OK || + if (MH_CreateHook(pLocation, reinterpret_cast(&HookLogChannel), reinterpret_cast(&m_realLogChannel)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - Log::Error("Could not hook Log function!"); + Log::Error("Could not hook CScript::LogChannel function!"); else - Log::Info("Log function hook complete!"); + Log::Info("CScript::LogChannel function hook complete!"); } } { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_LogChannel); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_TDBIDConstructorDerive); uint8_t* pLocation = func.GetAddr(); if (pLocation) { - if (MH_CreateHook(pLocation, &HookLogChannel, reinterpret_cast(&m_realLogChannel)) != MH_OK || + if (MH_CreateHook(pLocation, reinterpret_cast(&HookTDBIDCtorDerive), reinterpret_cast(&m_realTDBIDCtorDerive)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - Log::Error("Could not hook LogChannel function!"); + Log::Error("Could not hook CScript::TDBIDConstructorDerive function!"); else - Log::Info("LogChannel function hook complete!"); + Log::Info("CScript::TDBIDConstructorDerive function hook complete!"); } } { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_TDBIDConstructorDerive); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_ProcessRunningState); uint8_t* pLocation = func.GetAddr(); if (pLocation) { - if (MH_CreateHook(pLocation, &HookTDBIDCtorDerive, reinterpret_cast(&m_realTDBIDCtorDerive)) != - MH_OK || + if (MH_CreateHook(pLocation, reinterpret_cast(&HookRunningStateRun), reinterpret_cast(&m_realRunningStateRun)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - Log::Error("Could not hook TDBID::ctor[Derive] function!"); + Log::Error("Could not hook CScript::ProcessRunningState function!"); else - Log::Info("TDBID::ctor[Derive] function hook complete!"); + { + Log::Info("CScript::ProcessRunningState function hook complete!"); + } + } + } + + { + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_ProcessShutdownState); + uint8_t* pLocation = func.GetAddr(); + + if (pLocation) + { + if (MH_CreateHook(pLocation, reinterpret_cast(&HookShutdownStateRun), reinterpret_cast(&m_realShutdownStateRun)) != MH_OK || + MH_EnableHook(pLocation) != MH_OK) + Log::Error("Could not hook CScript::ProcessShutdownState function!"); + else + { + Log::Info("CScript::ProcessShutdownState function hook complete!"); + } } } { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_ProcessRunningState); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_ToStringDEBUG); uint8_t* pLocation = func.GetAddr(); if (pLocation) { - if (MH_CreateHook(pLocation, &HookRunningStateRun, reinterpret_cast(&m_realRunningStateRun)) != - MH_OK || + if (MH_CreateHook(pLocation, reinterpret_cast(HookTDBIDToStringDEBUG), reinterpret_cast(&m_realTDBIDToStringDEBUG)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - Log::Error("Could not hook RunningState::Run function!"); + Log::Error("Could not hook CScript::ToStringDEBUG function!"); else { - Log::Info("RunningState::Run function hook complete!"); + Log::Info("CScript::ToStringDEBUG function hook complete!"); } } } { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_TranslateBytecode); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_TranslateBytecode); uint8_t* pLocation = func.GetAddr(); if (pLocation) { - if (MH_CreateHook(pLocation, &HookTranslateBytecode, reinterpret_cast(&m_realTranslateBytecode)) != MH_OK || + if (MH_CreateHook(pLocation, reinterpret_cast(HookTranslateBytecode), reinterpret_cast(&m_realTranslateBytecode)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - Log::Error("Could not hook ScriptBinder::TranslateBytecode function!"); + Log::Error("Could not hook CScript::TranslateBytecode function!"); else { - Log::Info("ScriptBinder::TranslateBytecode function hook complete!"); + Log::Info("CScript::TranslateBytecode function hook complete!"); } } } { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_ToStringDEBUG); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_TweakDBLoad); uint8_t* pLocation = func.GetAddr(); if (pLocation) { - if (MH_CreateHook(pLocation, &HookTDBIDToStringDEBUG, - reinterpret_cast(&m_realTDBIDToStringDEBUG)) != - MH_OK || + if (MH_CreateHook(pLocation, reinterpret_cast(HookTweakDBLoad), reinterpret_cast(&m_realTweakDBLoad)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - Log::Error("Could not hook RunningState::Run function!"); + Log::Error("Could not hook CScript::TweakDBLoad function!"); else { - Log::Info("RunningState::Run function hook complete!"); + Log::Info("CScript::TweakDBLoad function hook complete!"); } } } { - RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::CScript_TweakDBLoad); + const RED4ext::RelocPtr func(CyberEngineTweaks::Addresses::PlayerSystem_OnPlayerSpawned); uint8_t* pLocation = func.GetAddr(); if (pLocation) { - if (MH_CreateHook(pLocation, &HookTweakDBLoad, reinterpret_cast(&m_realTweakDBLoad)) != MH_OK || + if (MH_CreateHook(pLocation, reinterpret_cast(HookPlayerSpawned), reinterpret_cast(&m_realPlayerSpawned)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - Log::Error("Could not hook TweakDB::Load function!"); + Log::Error("Could not hook PlayerSystem::OnPlayerSpawned function!"); else { - Log::Info("TweakDB::Load function hook complete!"); + Log::Info("PlayerSystem::OnPlayerSpawned function hook complete!"); } } } - // Disable SetLoadingState hook temporarily and get back to log count workaround - // as it introduces major breaking change for onInit handler. - //{ - // const mem::pattern cPattern("48 89 5C 24 18 89 54 24 10 57 48 83 EC 20 48 8B D9 C7"); - // const mem::default_scanner cScanner(cPattern); - // uint8_t* pLocation = cScanner(gameImage.TextRegion).as(); - // - // if (pLocation) - // { - // if (MH_CreateHook(pLocation, &HookSetLoadingState, reinterpret_cast(&m_realSetLoadingState)) != MH_OK - // || MH_EnableHook(pLocation) != MH_OK) - // Log::Error("Could not hook SetLoadingState function!"); - // else - // { - // Log::Info("SetLoadingState function hook complete!"); - // } - // } - //} } diff --git a/src/scripting/Sandbox.cpp b/src/scripting/Sandbox.cpp index cdac9b34..8307b918 100644 --- a/src/scripting/Sandbox.cpp +++ b/src/scripting/Sandbox.cpp @@ -3,32 +3,27 @@ #include "Sandbox.h" #include "Scripting.h" -#include - -Sandbox::Sandbox(Scripting* apScripting, sol::environment aBaseEnvironment, const std::filesystem::path& acRootPath) - : m_pScripting(apScripting) - , m_env(apScripting->GetState().Get(), sol::create) +Sandbox::Sandbox(uint64_t aId, Scripting* apScripting, sol::environment aBaseEnvironment, const std::filesystem::path& acRootPath) + : m_id(aId) + , m_pScripting(apScripting) + , m_env(apScripting->GetLockedState().Get(), sol::create, aBaseEnvironment) , m_path(acRootPath) { - // copy base environment, do not set it as fallback, as it may cause globals to bleed into other things! - sol::state_view sv = apScripting->GetState().Get(); - for (const auto& cKV : aBaseEnvironment) - m_env[cKV.first] = DeepCopySolObject(cKV.second, sv); - - // set global fallback table for the environment - sol::table metatable(sv, sol::create); - metatable[sol::meta_function::index] = sv.get(apScripting->GetGlobalName()); - m_env[sol::metatable_key] = metatable; } sol::protected_function_result Sandbox::ExecuteFile(const std::string& acPath) const { - return m_pScripting->GetState().Get().script_file(acPath, m_env, sol::load_mode::text); + return m_pScripting->GetLockedState().Get().script_file(acPath, m_env, sol::load_mode::text); } sol::protected_function_result Sandbox::ExecuteString(const std::string& acString) const { - return m_pScripting->GetState().Get().script(acString, m_env, sol:: detail::default_chunk_name(), sol::load_mode::text); + return m_pScripting->GetLockedState().Get().script(acString, m_env, sol:: detail::default_chunk_name(), sol::load_mode::text); +} + +uint64_t Sandbox::GetId() const +{ + return m_id; } sol::environment& Sandbox::GetEnvironment() diff --git a/src/scripting/Sandbox.h b/src/scripting/Sandbox.h index aca0f46c..c31d44c6 100644 --- a/src/scripting/Sandbox.h +++ b/src/scripting/Sandbox.h @@ -4,17 +4,19 @@ struct Scripting; struct Sandbox { - Sandbox(Scripting* apScripting, sol::environment aBaseEnvironment, const std::filesystem::path& acRootPath); + Sandbox(uint64_t aId, Scripting* apScripting, sol::environment aBaseEnvironment, const std::filesystem::path& acRootPath); ~Sandbox() = default; - + sol::protected_function_result ExecuteFile(const std::string& acPath) const; sol::protected_function_result ExecuteString(const std::string& acString) const; + uint64_t GetId() const; sol::environment& GetEnvironment(); const std::filesystem::path& GetRootPath() const; - + private: + uint64_t m_id{0}; Scripting* m_pScripting; sol::environment m_env; - std::filesystem::path m_path{ }; + std::filesystem::path m_path{}; }; \ No newline at end of file diff --git a/src/scripting/ScriptContext.cpp b/src/scripting/ScriptContext.cpp index be252046..c34a2b3c 100644 --- a/src/scripting/ScriptContext.cpp +++ b/src/scripting/ScriptContext.cpp @@ -3,10 +3,13 @@ #include "ScriptContext.h" #include +#include +namespace +{ // TODO: proper exception handling for Lua funcs! template -static sol::protected_function_result TryLuaFunction(std::shared_ptr aLogger, sol::function aFunc, Args... aArgs) +sol::protected_function_result TryLuaFunction(const std::shared_ptr& acpLogger, const sol::function& aFunc, Args... aArgs) { sol::protected_function_result result{ }; if (aFunc) @@ -17,23 +20,25 @@ static sol::protected_function_result TryLuaFunction(std::shared_ptrerror(e.what()); + acpLogger->error(e.what()); } if (!result.valid()) { const sol::error cError = result; - aLogger->error(cError.what()); + acpLogger->error(cError.what()); } } return result; } +} + ScriptContext::ScriptContext(LuaSandbox& aLuaSandbox, const std::filesystem::path& acPath, const std::string& acName) : m_sandbox(aLuaSandbox) , m_sandboxID(aLuaSandbox.CreateSandbox(acPath, acName)) , m_name(acName) { - auto state = aLuaSandbox.GetState(); + auto lockedState = aLuaSandbox.GetLockedState(); auto& sb = m_sandbox[m_sandboxID]; auto& env = sb.GetEnvironment(); @@ -41,10 +46,12 @@ ScriptContext::ScriptContext(LuaSandbox& aLuaSandbox, const std::filesystem::pat env["registerForEvent"] = [this](const std::string& acName, sol::function aCallback) { - if(acName == "onInit") - m_onInit = aCallback; + if(acName == "onHook") + m_onHook = aCallback; else if(acName == "onTweak") m_onTweak = aCallback; + else if(acName == "onInit") + m_onInit = aCallback; else if(acName == "onShutdown") m_onShutdown = aCallback; else if(acName == "onUpdate") @@ -59,65 +66,119 @@ ScriptContext::ScriptContext(LuaSandbox& aLuaSandbox, const std::filesystem::pat m_logger->error("Tried to register an unknown event '{}'!", acName); }; - env["registerHotkey"] = [this, &aLuaSandbox](const std::string& acID, const std::string& acDescription, sol::function aCallback) - { - if (acID.empty() || - (std::ranges::find_if(acID, [](char c){ return !(isalpha(c) || isdigit(c) || c == '_'); }) != acID.cend())) + auto wrapHandler = [&aLuaSandbox, loggerRef = m_logger](sol::function aCallback, const bool acIsHotkey) -> std::variant, std::function> { + if (acIsHotkey) { - m_logger->error("Tried to register a hotkey with an incorrect ID format '{}'! ID needs to be alphanumeric without any whitespace or special characters (exception being '_' which is allowed in ID)!", acID); - return; - } + return [&aLuaSandbox, loggerRef, aCallback]{ + auto lockedState = aLuaSandbox.GetLockedState(); - if (acDescription.empty()) - { - m_logger->error("Tried to register a hotkey with an empty description! (ID of hotkey handler: {})", acID); - return; + TryLuaFunction(loggerRef, aCallback); + }; } - auto loggerRef = m_logger; - std::string vkBindID = m_name + '.' + acID; - VKBind vkBind = {vkBindID, acDescription, [&aLuaSandbox, loggerRef, aCallback]() + return [&aLuaSandbox, loggerRef, aCallback](bool isDown){ + auto lockedState = aLuaSandbox.GetLockedState(); + + TryLuaFunction(loggerRef, aCallback, isDown); + }; + }; + + auto wrapDescription = [&aLuaSandbox, loggerRef = m_logger, sandboxId = m_sandboxID](const std::variant& acDescription) -> std::variant> { + if (std::holds_alternative(acDescription)) { - auto state = aLuaSandbox.GetState(); - TryLuaFunction(loggerRef, aCallback); - }}; + auto callback = std::get(acDescription); + if (callback != sol::nil) + { + return [&aLuaSandbox, loggerRef, callback]{ + auto lockedState = aLuaSandbox.GetLockedState(); + + aLuaSandbox.SetImGuiAvailable(true); + + const auto previousStyle = ImGui::GetStyle(); + + TryLuaFunction(loggerRef, callback); + + ImGui::GetStyle() = previousStyle; - m_vkBindInfos.emplace_back(VKBindInfo{vkBind}); + aLuaSandbox.SetImGuiAvailable(false); + }; + } + loggerRef->warn("Tried to register empty tooltip for handler!]"); + return ""; + } + return std::get(acDescription); }; - - env["registerInput"] = [this, &aLuaSandbox](const std::string& acID, const std::string& acDescription, sol::function aCallback) { + + auto registerBinding = [this, &wrapHandler, &wrapDescription](const std::string& acID, const std::string& acDisplayName, const std::variant& acDescription, sol::function aCallback, const bool acIsHotkey){ + const auto inputTypeStr = acIsHotkey ? "hotkey" : "input"; + if (acID.empty() || - (std::ranges::find_if(acID, [](char c) { return !(isalpha(c) || isdigit(c) || c == '_'); }) != acID.cend())) + std::ranges::find_if(acID, [](char c){ return !(isalnum(c) || c == '_' || c == '.'); }) != acID.cend()) { - m_logger->error( - "Tried to register input with an incorrect ID format '{}'! ID needs to be alphanumeric without any " - "whitespace or special characters (exception being '_' which is allowed in ID)!", + m_logger->error("Tried to register a {} with an incorrect ID format '{}'! ID needs to be alphanumeric without any whitespace or special characters (exceptions being '_' and '.' which are allowed in ID)!", + inputTypeStr, acID); return; } - if (acDescription.empty()) + if (acDisplayName.empty()) { - m_logger->error("Tried to register an input with an empty description! (ID of input handler: {})", acID); + m_logger->error("Tried to register a {} with an empty display name! [ID of this handler: {}]", + inputTypeStr, + acID); return; } - auto loggerRef = m_logger; - std::string vkBindID = m_name + '.' + acID; - VKBind vkBind = {vkBindID, acDescription, [&aLuaSandbox, loggerRef, aCallback](bool isDown) + const auto bindIt = std::find(m_vkBinds.cbegin(), m_vkBinds.cend(), acID); + if (bindIt != m_vkBinds.cend()) { - auto state = aLuaSandbox.GetState(); - TryLuaFunction(loggerRef, aCallback, isDown); - }}; + m_logger->error("Tried to register a {} with same ID as some other! [ID of this handler: {}, Display name of this handler: '{}', Display name of other handler: {}]", + inputTypeStr, + acID, + acDisplayName, + bindIt->DisplayName); + return; + } - m_vkBindInfos.emplace_back(VKBindInfo{vkBind}); + m_vkBinds.emplace_back(acID, acDisplayName, wrapDescription(acDescription), wrapHandler(aCallback, acIsHotkey)); }; + env["registerHotkey"] = sol::overload( + [®isterBinding](const std::string& acID, const std::string& acDisplayName, sol::function acDescriptionCallback, sol::function aCallback) + { + registerBinding(acID, acDisplayName, acDescriptionCallback, aCallback, true); + }, + [®isterBinding](const std::string& acID, const std::string& acDisplayName, const std::string& acDescription, sol::function aCallback) + { + registerBinding(acID, acDisplayName, acDescription, aCallback, true); + }, + [®isterBinding](const std::string& acID, const std::string& acDescription, sol::function aCallback) { + registerBinding(acID, acDescription, "", aCallback, true); + }); + + env["registerInput"] = sol::overload( + [®isterBinding](const std::string& acID, const std::string& acDisplayName, sol::function acDescriptionCallback, sol::function aCallback) + { + registerBinding(acID, acDisplayName, acDescriptionCallback, aCallback, false); + }, + [®isterBinding](const std::string& acID, const std::string& acDisplayName, const std::string& acDescription, sol::function aCallback) { + registerBinding(acID, acDisplayName, acDescription, aCallback, false); + }, + [registerBinding](const std::string& acID, const std::string& acDescription, sol::function aCallback) { + registerBinding(acID, acDescription, "", aCallback, false); + }); + // TODO: proper exception handling! try { - const auto path = acPath / "init.lua"; - const auto result = sb.ExecuteFile(path.string()); + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(sb.GetRootPath()); + + const auto path = GetLuaPath(L"init.lua", acPath, false); + + const auto result = sb.ExecuteFile(UTF16ToUTF8(path.native())); + + current_path(previousCurrentPath); if (result.valid()) { @@ -134,9 +195,14 @@ ScriptContext::ScriptContext(LuaSandbox& aLuaSandbox, const std::filesystem::pat { m_logger->error(e.what()); } + + env["registerForEvent"] = sol::nil; + env["registerHotkey"] = sol::nil; + env["registerInput"] = sol::nil; } -ScriptContext::ScriptContext(ScriptContext&& other) noexcept : ScriptContext(other) +ScriptContext::ScriptContext(ScriptContext&& other) noexcept +: ScriptContext(other) { other.m_initialized = false; } @@ -154,49 +220,76 @@ bool ScriptContext::IsValid() const return m_initialized; } -const TiltedPhoques::Vector& ScriptContext::GetBinds() const +const VKBind* ScriptContext::GetBind(const std::string& acId) const +{ + const auto it = std::find(m_vkBinds.begin(), m_vkBinds.end(), acId); + + if (it != m_vkBinds.cend()) + { + return &(*it); + } + + return nullptr; +} + +const TiltedPhoques::Vector& ScriptContext::GetBinds() const +{ + return m_vkBinds; +} + +void ScriptContext::TriggerOnHook() const { - return m_vkBindInfos; + auto lockedState = m_sandbox.GetLockedState(); + + TryLuaFunction(m_logger, m_onHook); } void ScriptContext::TriggerOnTweak() const { - auto state = m_sandbox.GetState(); + auto lockedState = m_sandbox.GetLockedState(); TryLuaFunction(m_logger, m_onTweak); } void ScriptContext::TriggerOnInit() const { - auto state = m_sandbox.GetState(); + auto lockedState = m_sandbox.GetLockedState(); TryLuaFunction(m_logger, m_onInit); } void ScriptContext::TriggerOnUpdate(float aDeltaTime) const { - auto state = m_sandbox.GetState(); + auto lockedState = m_sandbox.GetLockedState(); TryLuaFunction(m_logger, m_onUpdate, aDeltaTime); } void ScriptContext::TriggerOnDraw() const { - auto state = m_sandbox.GetState(); + auto lockedState = m_sandbox.GetLockedState(); + + m_sandbox.SetImGuiAvailable(true); + + const auto previousStyle = ImGui::GetStyle(); TryLuaFunction(m_logger, m_onDraw); + + ImGui::GetStyle() = previousStyle; + + m_sandbox.SetImGuiAvailable(false); } - + void ScriptContext::TriggerOnOverlayOpen() const { - auto state = m_sandbox.GetState(); + auto lockedState = m_sandbox.GetLockedState(); TryLuaFunction(m_logger, m_onOverlayOpen); } void ScriptContext::TriggerOnOverlayClose() const { - auto state = m_sandbox.GetState(); + auto lockedState = m_sandbox.GetLockedState(); TryLuaFunction(m_logger, m_onOverlayClose); } @@ -208,7 +301,7 @@ sol::object ScriptContext::GetRootObject() const void ScriptContext::TriggerOnShutdown() const { - auto state = m_sandbox.GetState(); + auto lockedState = m_sandbox.GetLockedState(); TryLuaFunction(m_logger, m_onShutdown); } diff --git a/src/scripting/ScriptContext.h b/src/scripting/ScriptContext.h index 83d3192e..75079f5d 100644 --- a/src/scripting/ScriptContext.h +++ b/src/scripting/ScriptContext.h @@ -10,27 +10,30 @@ struct ScriptContext [[nodiscard]] bool IsValid() const; - const TiltedPhoques::Vector& GetBinds() const; - + [[nodiscard]] const VKBind* GetBind(const std::string& acId) const; + [[nodiscard]] const TiltedPhoques::Vector& GetBinds() const; + + void TriggerOnHook() const; void TriggerOnTweak() const; void TriggerOnInit() const; void TriggerOnUpdate(float aDeltaTime) const; void TriggerOnDraw() const; - + void TriggerOnOverlayOpen() const; void TriggerOnOverlayClose() const; - sol::object GetRootObject() const; + [[nodiscard]] sol::object GetRootObject() const; private: void TriggerOnShutdown() const; ScriptContext(const ScriptContext&) = default; - + LuaSandbox& m_sandbox; - size_t m_sandboxID; + uint64_t m_sandboxID; sol::object m_object{ }; + sol::function m_onHook{ }; sol::function m_onTweak{ }; sol::function m_onInit{ }; sol::function m_onShutdown{ }; @@ -38,7 +41,7 @@ struct ScriptContext sol::function m_onDraw{ }; sol::function m_onOverlayOpen{ }; sol::function m_onOverlayClose{ }; - TiltedPhoques::Vector m_vkBindInfos{ }; + TiltedPhoques::Vector m_vkBinds{ }; std::string m_name{ }; std::shared_ptr m_logger{ nullptr }; bool m_initialized{ false }; diff --git a/src/scripting/ScriptStore.cpp b/src/scripting/ScriptStore.cpp index 13b0a15c..e84c7150 100644 --- a/src/scripting/ScriptStore.cpp +++ b/src/scripting/ScriptStore.cpp @@ -2,6 +2,10 @@ #include "ScriptStore.h" +#include + +#include "Utils.h" + ScriptStore::ScriptStore(LuaSandbox& aLuaSandbox, const Paths& aPaths, VKBindings& aBindings) : m_sandbox(aLuaSandbox) , m_paths(aPaths) @@ -9,13 +13,19 @@ ScriptStore::ScriptStore(LuaSandbox& aLuaSandbox, const Paths& aPaths, VKBinding { } -void ScriptStore::LoadAll() +void ScriptStore::DiscardAll() { - m_vkBindInfos.clear(); + m_vkBinds.clear(); m_contexts.clear(); m_sandbox.ResetState(); + m_bindings.Load(); +} + +void ScriptStore::LoadAll() +{ + DiscardAll(); - auto consoleLogger = spdlog::get("scripting"); + const auto consoleLogger = spdlog::get("scripting"); const auto& cModsRoot = m_paths.ModsRoot(); for (const auto& file : std::filesystem::directory_iterator(cModsRoot)) @@ -23,98 +33,119 @@ void ScriptStore::LoadAll() if (!file.is_directory()) continue; - auto fPath = file.path(); + const auto name = UTF16ToUTF8(file.path().filename().native()); + const auto pathStr = UTF16ToUTF8((cModsRoot / file.path()).native()); - try - { - if (is_symlink(fPath)) - fPath = read_symlink(fPath); - else if (is_symlink(fPath / "init.lua")) - fPath = read_symlink(fPath / "init.lua").parent_path(); - } - catch (std::exception& e) + if (file.path().native().starts_with(L"cet\\")) { + consoleLogger->warn("Ignoring mod using reserved name! ('{}')", pathStr); + continue; } - fPath = absolute(fPath); - auto fPathStr = fPath.string(); - - if (!exists(fPath / "init.lua")) + const auto path = GetAbsolutePath(file.path(), cModsRoot, false); + if (path.empty()) { - consoleLogger->warn("Ignoring directory that does not contain init.lua! ('{}')", fPathStr); + consoleLogger->error("Tried to access invalid directory! ('{}')", pathStr); continue; } - auto name = file.path().filename().string(); - if (name.find('.') != std::string::npos) + if (!exists(path / L"init.lua")) { - consoleLogger->info("Ignoring directory containing '.', as this is reserved character! ('{}')", fPathStr); + consoleLogger->warn("Ignoring mod which does not contain init.lua! ('{}')", pathStr); continue; } - auto ctx = ScriptContext{m_sandbox, fPath, name}; + auto ctx = ScriptContext{m_sandbox, path, name}; if (ctx.IsValid()) { - auto& ctxBinds = ctx.GetBinds(); - m_vkBindInfos.insert(m_vkBindInfos.cend(), ctxBinds.cbegin(), ctxBinds.cend()); m_contexts.emplace(name, std::move(ctx)); - consoleLogger->info("Mod {} loaded! ('{}')", name, fPathStr); + consoleLogger->info("Mod {} loaded! ('{}')", name, pathStr); } else - { - consoleLogger->error("Mod {} failed to load! ('{}')", name, fPathStr); - } + consoleLogger->error("Mod {} failed to load! ('{}')", name, pathStr); + } + + for (auto& contextIt : m_contexts) + m_vkBinds.insert({contextIt.first, contextIt.second.GetBinds()}); + + m_bindings.InitializeMods(m_vkBinds); +} + +const VKBind* ScriptStore::GetBind(const VKModBind& acModBind) const +{ + if (acModBind == Bindings::GetOverlayToggleModBind()) + return &Bindings::GetOverlayToggleBind(); + + const auto it = m_contexts.find(acModBind.ModName); + if (it != m_contexts.cend()) + return it->second.GetBind(acModBind.ID); + + return nullptr; +} + +const TiltedPhoques::Vector* ScriptStore::GetBinds(const std::string& acModName) const +{ + const auto it = m_contexts.find(acModName); + if (it != m_contexts.cend()) + { + return &it->second.GetBinds(); } - m_bindings.InitializeMods(m_vkBindInfos); + return nullptr; +} + +const TiltedPhoques::Map>>& ScriptStore::GetAllBinds() const +{ + return m_vkBinds; } -const TiltedPhoques::Vector& ScriptStore::GetBinds() const +void ScriptStore::TriggerOnHook() const { - return m_vkBindInfos; + for (const auto& mod : m_contexts | std::views::values) + mod.TriggerOnHook(); } void ScriptStore::TriggerOnTweak() const { - for (const auto& kvp : m_contexts) - kvp.second.TriggerOnTweak(); + for (const auto& mod : m_contexts | std::views::values) + mod.TriggerOnTweak(); } void ScriptStore::TriggerOnInit() const { - for (const auto& kvp : m_contexts) - kvp.second.TriggerOnInit(); + for (const auto& mod : m_contexts | std::views::values) + mod.TriggerOnInit(); } void ScriptStore::TriggerOnUpdate(float aDeltaTime) const { - for (const auto& kvp : m_contexts) - kvp.second.TriggerOnUpdate(aDeltaTime); + for (const auto& mod : m_contexts | std::views::values) + mod.TriggerOnUpdate(aDeltaTime); } void ScriptStore::TriggerOnDraw() const { - for (const auto& kvp : m_contexts) - kvp.second.TriggerOnDraw(); + for (const auto& mod : m_contexts | std::views::values) + mod.TriggerOnDraw(); } void ScriptStore::TriggerOnOverlayOpen() const { - for (const auto& kvp : m_contexts) - kvp.second.TriggerOnOverlayOpen(); + for (const auto& mod : m_contexts | std::views::values) + mod.TriggerOnOverlayOpen(); } void ScriptStore::TriggerOnOverlayClose() const { - for (const auto& kvp : m_contexts) - kvp.second.TriggerOnOverlayClose(); + for (const auto& mod : m_contexts | std::views::values) + mod.TriggerOnOverlayClose(); } sol::object ScriptStore::GetMod(const std::string& acName) const { - const auto itor = m_contexts.find(acName); - if (itor != std::end(m_contexts)) - return itor->second.GetRootObject(); + const auto it = m_contexts.find(acName); + if (it != m_contexts.cend()) + return it->second.GetRootObject(); return sol::nil; } diff --git a/src/scripting/ScriptStore.h b/src/scripting/ScriptStore.h index 85659a34..f548f3bb 100644 --- a/src/scripting/ScriptStore.h +++ b/src/scripting/ScriptStore.h @@ -7,24 +7,28 @@ struct ScriptStore ScriptStore(LuaSandbox& aLuaSandbox, const Paths& aPaths, VKBindings& aBindings); ~ScriptStore() = default; + void DiscardAll(); void LoadAll(); - const TiltedPhoques::Vector& GetBinds() const; - + [[nodiscard]] const VKBind* GetBind(const VKModBind& acModBind) const; + [[nodiscard]] const TiltedPhoques::Vector* GetBinds(const std::string& acModName) const; + [[nodiscard]] const TiltedPhoques::Map>>& GetAllBinds() const; + + void TriggerOnHook() const; void TriggerOnTweak() const; void TriggerOnInit() const; void TriggerOnUpdate(float aDeltaTime) const; void TriggerOnDraw() const; - + void TriggerOnOverlayOpen() const; void TriggerOnOverlayClose() const; - sol::object GetMod(const std::string& acName) const; + [[nodiscard]] sol::object GetMod(const std::string& acName) const; private: - + TiltedPhoques::Map m_contexts{ }; - TiltedPhoques::Vector m_vkBindInfos{ }; + TiltedPhoques::Map>> m_vkBinds{ }; LuaSandbox& m_sandbox; const Paths& m_paths; VKBindings& m_bindings; diff --git a/src/scripting/Scripting.cpp b/src/scripting/Scripting.cpp index 9d04ce8d..cb20f56b 100644 --- a/src/scripting/Scripting.cpp +++ b/src/scripting/Scripting.cpp @@ -6,45 +6,40 @@ #include "GameOptions.h" #include "Texture.h" -#include +#include #include - -#include - #include -#include #include #include #include #include -#include -#include #include -#include #include #include +#include +#include +#include -#include "Utils.h" - -#ifndef NDEBUG +#ifdef CET_DEBUG #include "GameHooks.h" #include "GameDump.h" #include #endif -static constexpr const bool s_cThrowLuaErrors = true; +static constexpr bool s_cThrowLuaErrors = true; -static RTTILocator s_stringType{RED4ext::FNV1a("String")}; +static RTTILocator s_stringType{RED4ext::FNV1a64("String")}; -Scripting::Scripting(const Paths& aPaths, VKBindings& aBindings, D3D12& aD3D12, Options& aOptions) +Scripting::Scripting(const Paths& aPaths, VKBindings& aBindings, D3D12& aD3D12) : m_sandbox(this, aBindings) + , m_mapper(m_lua.AsRef(), m_sandbox) , m_store(m_sandbox, aPaths, aBindings) - , m_override(this, aOptions) + , m_override(this) , m_paths(aPaths) , m_d3d12(aD3D12) - , m_mapper(m_lua.AsRef(), m_global) { CreateLogger(aPaths.CETRoot() / "scripting.log", "scripting"); + CreateLogger(aPaths.CETRoot() / "gamelog.log", "gamelog"); } void Scripting::Initialize() @@ -60,9 +55,47 @@ void Scripting::Initialize() // as this could get overriden by LUA_PATH environment variable luaVm["package"]["path"] = "./?.lua"; - sol_ImGui::InitBindings(luaVm); + // execute autoexec.lua inside our default script directory + const auto previousCurrentPath = std::filesystem::current_path(); + current_path(m_paths.CETRoot() / "scripts"); + luaVm.script("json = require 'json/json'", sol:: detail::default_chunk_name(), sol::load_mode::text); + current_path(previousCurrentPath); + + // initialize sandbox + m_sandbox.Initialize(); + + auto& globals = m_sandbox.GetEnvironment(); - luaVm["print"] = [](sol::variadic_args aArgs, sol::this_state aState) + // load in imgui bindings + sol_ImGui::InitBindings(luaVm, globals); + sol::table imgui = globals["ImGui"]; + Texture::BindTexture(imgui); + for (auto [key, value] : imgui) + { + if (value.get_type() != sol::type::function) + continue; + + sol::function original = value; + imgui[key] = make_object(luaVm, [this, original](sol::variadic_args aVariadicArgs, sol::this_environment aThisEnv) -> sol::variadic_results { + const sol::environment cEnv = aThisEnv; + const auto logger = cEnv["__logger"].get>(); + if (!m_sandbox.GetImGuiAvailable()) + { + logger->error("Tried to call ImGui from invalid event!"); + throw "Tried to call ImGui from invalid event!"; + } + + return original(as_args(aVariadicArgs)); + }); + } + + // setup logger for console sandbox + auto& consoleSB = m_sandbox[0]; + auto& consoleSBEnv = consoleSB.GetEnvironment(); + consoleSBEnv["__logger"] = spdlog::get("scripting"); + + // load in game bindings + globals["print"] = [](sol::variadic_args aArgs, sol::this_state aState) { std::ostringstream oss; sol::state_view s(aState); @@ -79,24 +112,20 @@ void Scripting::Initialize() spdlog::get("scripting")->info(oss.str()); }; - // global fallback table for all environments - sol::table luaGlobal = sol::table(luaVm, sol::create); - luaVm[m_global] = luaGlobal; - - luaGlobal["GetVersion"] = []() -> std::string + globals["GetVersion"] = []() -> std::string { return CET_BUILD_COMMIT; }; - luaGlobal["GetDisplayResolution"] = [this]() -> std::tuple + globals["GetDisplayResolution"] = [this]() -> std::tuple { const auto resolution = m_d3d12.GetResolution(); return {static_cast(resolution.cx), static_cast(resolution.cy)}; }; - luaGlobal["ModArchiveExists"] = [this](const std::string& acArchiveName) -> bool + globals["ModArchiveExists"] = [this](const std::string& acArchiveName) -> bool { - auto resourceDepot = RED4ext::ResourceDepot::Get(); + const auto resourceDepot = RED4ext::ResourceDepot::Get(); for (const auto& archiveGroup : resourceDepot->groups) { @@ -114,28 +143,6 @@ void Scripting::Initialize() return false; }; - // fake game object to prevent errors from older autoexec.lua - luaVm["Game"] = sol::table(luaVm, sol::create); - luaGlobal["Game"] = luaVm["Game"]; - - // execute autoexec.lua inside our default script directory - current_path(m_paths.CETRoot() / "scripts"); - if (std::filesystem::exists("autoexec.lua")) - luaVm.do_file("autoexec.lua"); - else - spdlog::get("scripting")->warn("WARNING: missing CET autoexec.lua!"); - - // initialize sandbox - m_sandbox.Initialize(); - - // setup logger for console sandbox - auto& consoleSB = m_sandbox[0]; - auto& consoleSBEnv = consoleSB.GetEnvironment(); - consoleSBEnv["__logger"] = spdlog::get("scripting"); - - // set current path for following scripts to our ModsPath - current_path(m_paths.ModsRoot()); - // load mods m_store.LoadAll(); } @@ -144,17 +151,15 @@ void Scripting::PostInitializeScripting() { auto lua = m_lua.Lock(); auto& luaVm = lua.Get(); + auto& globals = m_sandbox.GetEnvironment(); if (luaVm["__Game"] != sol::nil) { m_mapper.Refresh(); m_override.Refresh(); - m_sandbox.PostInitialize(); return; } - sol::table luaGlobal = luaVm[m_global]; - luaVm.new_usertype("__Game", sol::meta_function::construct, sol::no_constructor, sol::meta_function::index, &Scripting::Index); @@ -170,55 +175,55 @@ void Scripting::PostInitializeScripting() sol::meta_function::index, &ClassType::Index, sol::meta_function::new_index, &ClassType::NewIndex); - luaVm.new_usertype("Descriptor", + globals.new_usertype("Descriptor", sol::meta_function::to_string, &Type::Descriptor::ToString); - luaVm.new_usertype("StrongReference", + globals.new_usertype("StrongReference", sol::meta_function::construct, sol::no_constructor, sol::base_classes, sol::bases(), sol::meta_function::index, &StrongReference::Index, sol::meta_function::new_index, &StrongReference::NewIndex); - luaVm.new_usertype("WeakReference", + globals.new_usertype("WeakReference", sol::meta_function::construct, sol::no_constructor, sol::base_classes, sol::bases(), sol::meta_function::index, &WeakReference::Index, sol::meta_function::new_index, &WeakReference::NewIndex); - luaVm.new_usertype("SingletonReference", + globals.new_usertype("SingletonReference", sol::meta_function::construct, sol::no_constructor, sol::base_classes, sol::bases(), sol::meta_function::index, &SingletonReference::Index, sol::meta_function::new_index, &SingletonReference::NewIndex); - luaVm.new_usertype("ClassReference", + globals.new_usertype("ClassReference", sol::meta_function::construct, sol::no_constructor, sol::base_classes, sol::bases(), sol::meta_function::index, &ClassReference::Index, sol::meta_function::new_index, &ClassReference::NewIndex); - luaVm.new_usertype("ResourceAsyncReference", + globals.new_usertype("ResourceAsyncReference", sol::meta_function::construct, sol::no_constructor, sol::base_classes, sol::bases(), sol::meta_function::index, &ResourceAsyncReference::Index, sol::meta_function::new_index, &ResourceAsyncReference::NewIndex, - "hash", sol::property(&ResourceAsyncReference::GetLuaHash)); + "hash", property(&ResourceAsyncReference::GetLuaHash)); - luaVm.new_usertype("Unknown", + globals.new_usertype("Unknown", sol::meta_function::construct, sol::no_constructor, sol::base_classes, sol::bases(), sol::meta_function::index, &UnknownType::Index, sol::meta_function::new_index, &UnknownType::NewIndex); - luaGlobal["IsDefined"] = sol::overload( + globals["IsDefined"] = sol::overload( // Check if weak reference is still valid - [](WeakReference& aRef) -> bool + [](const WeakReference& aRef) -> bool { return !aRef.m_weakHandle.Expired(); }, // To make it callable for strong reference // although it's always valid unless it's null - [](StrongReference& aRef) -> bool + [](const StrongReference&) -> bool { return true; }, @@ -228,18 +233,16 @@ void Scripting::PostInitializeScripting() return false; }); - luaVm.new_usertype("Enum", + globals.new_usertype("Enum", sol::constructors(), sol::meta_function::to_string, &Enum::ToString, sol::meta_function::equal_to, &Enum::operator==, "value", sol::property(&Enum::GetValueName, &Enum::SetValueByName)); - luaGlobal["Enum"] = luaVm["Enum"]; - - luaGlobal["EnumInt"] = [this](Enum& aEnum) -> sol::object + globals["EnumInt"] = [this](Enum& aEnum) -> sol::object { - static RTTILocator s_uint64Type{RED4ext::FNV1a("Uint64")}; + static RTTILocator s_uint64Type{RED4ext::FNV1a64("Uint64")}; auto lockedState = m_lua.Lock(); @@ -250,7 +253,7 @@ void Scripting::PostInitializeScripting() return Converter::ToLua(stackType, lockedState); }; - luaVm.new_usertype("Vector3", + globals.new_usertype("Vector3", sol::constructors(), sol::meta_function::to_string, &Vector3::ToString, sol::meta_function::equal_to, &Vector3::operator==, @@ -258,8 +261,7 @@ void Scripting::PostInitializeScripting() "y", &Vector3::y, "z", &Vector3::z); - luaGlobal["Vector3"] = luaVm["Vector3"]; - luaGlobal["ToVector3"] = [](sol::table table) -> Vector3 + globals["ToVector3"] = [](sol::table table) -> Vector3 { return Vector3 { @@ -278,9 +280,9 @@ void Scripting::PostInitializeScripting() "y", &Vector4::y, "z", &Vector4::z, "w", &Vector4::w); + globals["Vector4"] = luaVm["Vector4"]; - luaGlobal["Vector4"] = luaVm["Vector4"]; - luaGlobal["ToVector4"] = [](sol::table table) -> Vector4 + globals["ToVector4"] = [](sol::table table) -> Vector4 { return Vector4 { @@ -299,9 +301,9 @@ void Scripting::PostInitializeScripting() "roll", &EulerAngles::roll, "pitch", &EulerAngles::pitch, "yaw", &EulerAngles::yaw); + globals["EulerAngles"] = luaVm["EulerAngles"]; - luaGlobal["EulerAngles"] = luaVm["EulerAngles"]; - luaGlobal["ToEulerAngles"] = [](sol::table table) -> EulerAngles + globals["ToEulerAngles"] = [](sol::table table) -> EulerAngles { return EulerAngles { @@ -320,9 +322,9 @@ void Scripting::PostInitializeScripting() "j", &Quaternion::j, "k", &Quaternion::k, "r", &Quaternion::r); + globals["Quaternion"] = luaVm["Quaternion"]; - luaGlobal["Quaternion"] = luaVm["Quaternion"]; - luaGlobal["ToQuaternion"] = [](sol::table table) -> Quaternion + globals["ToQuaternion"] = [](sol::table table) -> Quaternion { return Quaternion { @@ -333,7 +335,7 @@ void Scripting::PostInitializeScripting() }; }; - luaVm.new_usertype("CName", + globals.new_usertype("CName", sol::constructors(), sol::call_constructor, sol::constructors(), @@ -344,8 +346,7 @@ void Scripting::PostInitializeScripting() "value", sol::property(&CName::AsString), "add", &CName::Add); - luaGlobal["CName"] = luaVm["CName"]; - luaGlobal["ToCName"] = [](sol::table table) -> CName + globals["ToCName"] = [](sol::table table) -> CName { return CName { @@ -354,7 +355,7 @@ void Scripting::PostInitializeScripting() }; }; - luaVm.new_usertype("TweakDBID", + globals.new_usertype("TweakDBID", sol::constructors(), sol::call_constructor, sol::constructors(), @@ -363,11 +364,17 @@ void Scripting::PostInitializeScripting() sol::meta_function::addition, &TweakDBID::operator+, sol::meta_function::concatenation, &TweakDBID::operator+, "hash", &TweakDBID::name_hash, - "length", &TweakDBID::name_length); + "length", &TweakDBID::name_length, + "value", sol::property(&TweakDBID::AsString)); - luaGlobal["TweakDBID"] = luaVm["TweakDBID"]; - luaGlobal["ToTweakDBID"] = [](sol::table table) -> TweakDBID + globals["ToTweakDBID"] = [](sol::table table) -> TweakDBID { + // try with name first + const auto name = table["name"].get_or(""); + if (!name.empty()) + return TweakDBID(name); + + // if conversion from name failed, look for old hash + length return TweakDBID { table["hash"].get_or(0), @@ -386,9 +393,9 @@ void Scripting::PostInitializeScripting() "rng_seed", &ItemID::rng_seed, "unknown", &ItemID::unknown, "maybe_type", &ItemID::maybe_type); + globals["ItemID"] = luaVm["ItemID"]; - luaGlobal["ItemID"] = luaVm["ItemID"]; - luaGlobal["ToItemID"] = [](sol::table table) -> ItemID + globals["ToItemID"] = [](sol::table table) -> ItemID { return ItemID { @@ -399,16 +406,14 @@ void Scripting::PostInitializeScripting() }; }; - luaVm.new_usertype("CRUID", + globals.new_usertype("CRUID", sol::constructors(), sol::call_constructor, sol::constructors(), sol::meta_function::to_string, &CRUID::ToString, sol::meta_function::equal_to, &CRUID::operator==, "hash", &CRUID::hash); - luaGlobal["CRUID"] = luaVm["CRUID"]; - - luaVm.new_usertype("LocKey", + globals.new_usertype("LocKey", sol::constructors(), sol::meta_function::to_string, &gamedataLocKeyWrapper::ToString, sol::meta_function::equal_to, &gamedataLocKeyWrapper::operator==, @@ -424,7 +429,7 @@ void Scripting::PostInitializeScripting() } else if (IsLuaCData(aValue)) { - std::string str = lua["tostring"](aValue); + const std::string str = lua["tostring"](aValue); result.hash = std::stoull(str); } else if (aValue.get_type() == sol::type::string) @@ -437,13 +442,11 @@ void Scripting::PostInitializeScripting() }), "hash", sol::property([](gamedataLocKeyWrapper& aThis, sol::this_state aState) -> sol::object { sol::state_view lua(aState); - auto converted = lua.script(fmt::format("return {}ull", aThis.hash)); + const auto converted = lua.script(fmt::format("return {}ull", aThis.hash)); return converted.get(); })); - luaGlobal["LocKey"] = luaVm["LocKey"]; - - luaVm.new_usertype("GameOptions", + globals.new_usertype("GameOptions", sol::meta_function::construct, sol::no_constructor, "Print", &GameOptions::Print, "Get", &GameOptions::Get, @@ -458,44 +461,54 @@ void Scripting::PostInitializeScripting() "Dump", &GameOptions::Dump, "List", &GameOptions::List); - luaGlobal["GameOptions"] = luaVm["GameOptions"]; + m_sandbox.PostInitializeScripting(); + + TriggerOnHook(); +} + +void Scripting::PostInitializeTweakDB() +{ + auto lua = m_lua.Lock(); + auto& luaVm = lua.Get(); + auto& globals = m_sandbox.GetEnvironment(); luaVm.new_usertype("__TweakDB", sol::meta_function::construct, sol::no_constructor, "DebugStats", &TweakDB::DebugStats, "GetRecords", &TweakDB::GetRecords, - "GetRecord", sol::overload(&TweakDB::GetRecordByName, &TweakDB::GetRecord), - "Query", sol::overload(&TweakDB::QueryByName, &TweakDB::Query), - "GetFlat", sol::overload(&TweakDB::GetFlatByName, &TweakDB::GetFlat), - "SetFlats", sol::overload(&TweakDB::SetFlatsByName, &TweakDB::SetFlats), - "SetFlat", sol::overload(&TweakDB::SetFlatByNameAutoUpdate, &TweakDB::SetFlatAutoUpdate, &TweakDB::SetTypedFlat, &TweakDB::SetTypedFlatByName), - "SetFlatNoUpdate", sol::overload(&TweakDB::SetFlatByName, &TweakDB::SetFlat), - "Update", sol::overload(&TweakDB::UpdateRecordByName, &TweakDB::UpdateRecordByID, &TweakDB::UpdateRecord), - "CreateRecord", sol::overload(&TweakDB::CreateRecordToID, &TweakDB::CreateRecord), - "CloneRecord", sol::overload(&TweakDB::CloneRecordByName, &TweakDB::CloneRecordToID, &TweakDB::CloneRecord), - "DeleteRecord", sol::overload(&TweakDB::DeleteRecordByID, &TweakDB::DeleteRecord)); - - luaGlobal["TweakDB"] = TweakDB(m_lua.AsRef()); - - m_sandbox.PostInitialize(); + "GetRecord", overload(&TweakDB::GetRecordByName, &TweakDB::GetRecord), + "Query", overload(&TweakDB::QueryByName, &TweakDB::Query), + "GetFlat", overload(&TweakDB::GetFlatByName, &TweakDB::GetFlat), + "SetFlats", overload(&TweakDB::SetFlatsByName, &TweakDB::SetFlats), + "SetFlat", overload(&TweakDB::SetFlatByNameAutoUpdate, &TweakDB::SetFlatAutoUpdate, &TweakDB::SetTypedFlat, &TweakDB::SetTypedFlatByName), + "SetFlatNoUpdate", overload(&TweakDB::SetFlatByName, &TweakDB::SetFlat), + "Update", overload(&TweakDB::UpdateRecordByName, &TweakDB::UpdateRecordByID, &TweakDB::UpdateRecord), + "CreateRecord", overload(&TweakDB::CreateRecordToID, &TweakDB::CreateRecord), + "CloneRecord", overload(&TweakDB::CloneRecordByName, &TweakDB::CloneRecordToID, &TweakDB::CloneRecord), + "DeleteRecord", overload(&TweakDB::DeleteRecordByID, &TweakDB::DeleteRecord)); + + globals["TweakDB"] = TweakDB(m_lua.AsRef()); + + m_sandbox.PostInitializeTweakDB(); + + TriggerOnTweak(); } void Scripting::PostInitializeMods() { auto lua = m_lua.Lock(); auto& luaVm = lua.Get(); + auto& globals = m_sandbox.GetEnvironment(); - sol::table luaGlobal = luaVm[m_global]; - - luaGlobal["NewObject"] = [this](const std::string& acName, sol::this_environment aEnv) -> sol::object + globals["NewObject"] = [this](const std::string& acName, sol::this_environment aEnv) -> sol::object { auto* pRtti = RED4ext::CRTTISystem::Get(); - auto* pType = pRtti->GetType(RED4ext::FNV1a(acName.c_str())); + auto* pType = pRtti->GetType(RED4ext::FNV1a64(acName.c_str())); if (!pType) { const sol::environment cEnv = aEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); logger->info("Type '{}' not found.", acName); return sol::nil; } @@ -505,48 +518,47 @@ void Scripting::PostInitializeMods() return RTTIHelper::Get().NewHandle(pType, sol::nullopt); }; - luaGlobal["GetSingleton"] = [this](const std::string& acName, sol::this_environment aThisEnv) + globals["GetSingleton"] = [this](const std::string& acName, sol::this_environment aThisEnv) { return this->GetSingletonHandle(acName, aThisEnv); }; - luaGlobal["Override"] = [this](const std::string& acTypeName, const std::string& acFullName, + globals["Override"] = [this](const std::string& acTypeName, const std::string& acFullName, sol::protected_function aFunction, sol::this_environment aThisEnv) -> void { m_override.Override(acTypeName, acFullName, aFunction, aThisEnv, true); }; - luaGlobal["ObserveBefore"] = [this](const std::string& acTypeName, const std::string& acFullName, + globals["ObserveBefore"] = [this](const std::string& acTypeName, const std::string& acFullName, sol::protected_function aFunction, sol::this_environment aThisEnv) -> void { m_override.Override(acTypeName, acFullName, aFunction, aThisEnv, false, false); }; - luaGlobal["ObserveAfter"] = [this](const std::string& acTypeName, const std::string& acFullName, + globals["ObserveAfter"] = [this](const std::string& acTypeName, const std::string& acFullName, sol::protected_function aFunction, sol::this_environment aThisEnv) -> void { m_override.Override(acTypeName, acFullName, aFunction, aThisEnv, false, true); }; - luaGlobal["Observe"] = luaGlobal["ObserveBefore"]; + globals["Observe"] = globals["ObserveBefore"]; - // Doesn't require RTTI, but still shouldn't be used before onInit as there is no guarantee that the required mod will be loaded before - luaGlobal["GetMod"] = [this](const std::string& acName) -> sol::object + globals["GetMod"] = [this](const std::string& acName) -> sol::object { return GetMod(acName); }; - luaGlobal["GameDump"] = [this](Type* apType) + globals["GameDump"] = [this](const Type* acpType) { - return apType ? apType->GameDump() : "Null"; + return acpType ? acpType->GameDump() : "Null"; }; - luaGlobal["Dump"] = [this](Type* apType, bool aDetailed) + globals["Dump"] = [this](const Type* acpType, bool aDetailed) { - return apType != nullptr ? apType->Dump(aDetailed) : Type::Descriptor{}; + return acpType != nullptr ? acpType->Dump(aDetailed) : Type::Descriptor{}; }; - luaGlobal["DumpType"] = [this](const std::string& acName, bool aDetailed) + globals["DumpType"] = [this](const std::string& acName, bool aDetailed) { auto* pRtti = RED4ext::CRTTISystem::Get(); - auto* pType = pRtti->GetClass(RED4ext::FNV1a(acName.c_str())); + auto* pType = pRtti->GetClass(RED4ext::FNV1a64(acName.c_str())); if (!pType || pType->GetType() == RED4ext::ERTTIType::Simple) return Type::Descriptor(); @@ -554,23 +566,23 @@ void Scripting::PostInitializeMods() return type.Dump(aDetailed); }; - luaGlobal["DumpAllTypeNames"] = [this](sol::this_environment aThisEnv) + globals["DumpAllTypeNames"] = [this](sol::this_environment aThisEnv) { - auto* pRtti = RED4ext::CRTTISystem::Get(); + const auto* pRtti = RED4ext::CRTTISystem::Get(); uint32_t count = 0; - pRtti->types.for_each([&count](RED4ext::CName name, RED4ext::CBaseRTTIType*& type) + pRtti->types.for_each([&count](RED4ext::CName name, RED4ext::CBaseRTTIType*&) { Log::Info(name.ToString()); count++; }); const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); logger->info("Dumped {} types", count); }; -#ifndef NDEBUG - luaGlobal["DumpVtables"] = [this]() +#ifdef CET_DEBUG + globals["DumpVtables"] = [this] { // Hacky RTTI dump, this should technically only dump IScriptable instances and RTTI types as they are guaranteed to have a vtable // but there can occasionally be Class types that are not IScriptable derived that still have a vtable @@ -579,43 +591,63 @@ void Scripting::PostInitializeMods() // error when there are classes that instantiate a parent class but don't actually have a subclass instance GameMainThread::Get().AddTask(&GameDump::DumpVTablesTask::Run); }; - luaGlobal["DumpReflection"] = [this](bool aVerbose, bool aExtendedPath, bool aPropertyHolders) + globals["DumpReflection"] = [this](bool aVerbose, bool aExtendedPath, bool aPropertyHolders) { RED4ext::GameReflection::Dump(m_paths.CETRoot() / "dumps", aVerbose, aExtendedPath, aPropertyHolders); }; #endif - luaVm["Game"] = this; - luaGlobal["Game"] = luaVm["Game"]; + globals["Game"] = this; RTTIExtender::Initialize(); m_mapper.Register(); - m_sandbox.PostInitialize(); RegisterOverrides(); + + m_sandbox.PostInitializeMods(); + + TriggerOnInit(); + if (CET::Get().GetOverlay().IsEnabled()) + TriggerOnOverlayOpen(); } void Scripting::RegisterOverrides() { auto lua = m_lua.Lock(); auto& luaVm = lua.Get(); + auto& globals = m_sandbox.GetEnvironment(); - luaVm["RegisterGlobalInputListener"] = [](WeakReference& aSelf, sol::this_environment aThisEnv) { - sol::protected_function unregisterInputListener = aSelf.Index("UnregisterInputListener", aThisEnv); - sol::protected_function registerInputListener = aSelf.Index("RegisterInputListener", aThisEnv); + globals["RegisterGlobalInputListener"] = [](WeakReference& aSelf, sol::this_environment aThisEnv) { + const sol::protected_function unregisterInputListener = aSelf.Index("UnregisterInputListener", aThisEnv); + const sol::protected_function registerInputListener = aSelf.Index("RegisterInputListener", aThisEnv); unregisterInputListener(aSelf, aSelf); registerInputListener(aSelf, aSelf); }; - m_override.Override("PlayerPuppet", "GracePeriodAfterSpawn", luaVm["RegisterGlobalInputListener"], sol::nil, false, false, true); + m_override.Override("PlayerPuppet", "GracePeriodAfterSpawn", globals["RegisterGlobalInputListener"], sol::nil, false, false, true); m_override.Override("PlayerPuppet", "OnDetach", sol::nil, sol::nil, false, false, true); m_override.Override("QuestTrackerGameController", "OnUninitialize", sol::nil, sol::nil, false, false, true); } -const TiltedPhoques::Vector& Scripting::GetBinds() const +const VKBind* Scripting::GetBind(const VKModBind& acModBind) const { - return m_store.GetBinds(); + return m_store.GetBind(acModBind); +} + +const TiltedPhoques::Vector* Scripting::GetBinds(const std::string& acModName) const +{ + return m_store.GetBinds(acModName); +} + +const TiltedPhoques::Map>>& Scripting::GetAllBinds() const +{ + return m_store.GetAllBinds(); +} + +void Scripting::TriggerOnHook() const +{ + m_store.TriggerOnHook(); } void Scripting::TriggerOnTweak() const @@ -653,22 +685,36 @@ sol::object Scripting::GetMod(const std::string& acName) const return m_store.GetMod(acName); } -void Scripting::ReloadAllMods() +void Scripting::UnloadAllMods() { m_override.Clear(); RegisterOverrides(); - current_path(m_paths.ModsRoot()); + m_store.DiscardAll(); +} + +void Scripting::ReloadAllMods() +{ + UnloadAllMods(); + m_store.LoadAll(); + + TriggerOnHook(); + TriggerOnTweak(); + TriggerOnInit(); + + if (CET::Get().GetOverlay().IsEnabled()) + TriggerOnOverlayOpen(); + + spdlog::get("scripting")->info("LuaVM: Reloaded all mods!"); } -bool Scripting::ExecuteLua(const std::string& acCommand) +bool Scripting::ExecuteLua(const std::string& acCommand) const { // TODO: proper exception handling! try { - auto lock = std::scoped_lock{m_vmLock}; - - const auto cResult = m_sandbox.ExecuteString(acCommand); + auto lockedState = m_lua.Lock(); + const auto cResult = m_sandbox[0].ExecuteString(acCommand); if (cResult.valid()) return true; const sol::error cError = cResult; @@ -681,7 +727,7 @@ bool Scripting::ExecuteLua(const std::string& acCommand) return false; } -void Scripting::CollectGarbage() +void Scripting::CollectGarbage() const { auto lockedState = m_lua.Lock(); auto& luaState = lockedState.Get(); @@ -689,14 +735,14 @@ void Scripting::CollectGarbage() luaState.collect_garbage(); } -Scripting::LockedState Scripting::GetState() const noexcept +LuaSandbox& Scripting::GetSandbox() { - return m_lua.Lock(); + return m_sandbox; } -std::string Scripting::GetGlobalName() const noexcept +Scripting::LockedState Scripting::GetLockedState() const noexcept { - return m_global; + return m_lua.Lock(); } sol::object Scripting::Index(const std::string& acName, sol::this_state aState, sol::this_environment aEnv) @@ -706,7 +752,7 @@ sol::object Scripting::Index(const std::string& acName, sol::this_state aState, return itor->second; } - auto func = RTTIHelper::Get().ResolveFunction(acName); + const auto func = RTTIHelper::Get().ResolveFunction(acName); if (!func) { @@ -719,7 +765,7 @@ sol::object Scripting::Index(const std::string& acName, sol::this_state aState, else { const sol::environment cEnv = aEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + auto logger = cEnv["__logger"].get>(); logger->error("Error: {}", errorMessage); } @@ -727,31 +773,31 @@ sol::object Scripting::Index(const std::string& acName, sol::this_state aState, } auto& property = m_properties[acName]; - property = std::move(func); + property = func; return property; } sol::object Scripting::GetSingletonHandle(const std::string& acName, sol::this_environment aThisEnv) { - auto locked = m_lua.Lock(); - auto& lua = locked.Get(); + auto lockedState = m_lua.Lock(); + const auto& luaState = lockedState.Get(); - auto itor = m_singletons.find(acName); + const auto itor = m_singletons.find(acName); if (itor != std::end(m_singletons)) - return make_object(lua, itor->second); + return make_object(luaState, itor->second); auto* pRtti = RED4ext::CRTTISystem::Get(); - auto* pType = pRtti->GetClass(RED4ext::FNV1a(acName.c_str())); + auto* pType = pRtti->GetClass(RED4ext::FNV1a64(acName.c_str())); if (!pType) { const sol::environment cEnv = aThisEnv; - std::shared_ptr logger = cEnv["__logger"].get>(); + const auto logger = cEnv["__logger"].get>(); logger->info("Type '{}' not found.", acName); return sol::nil; } - auto result = m_singletons.emplace(std::make_pair(acName, SingletonReference{m_lua.AsRef(), pType})); - return make_object(lua, result.first->second); + const auto result = m_singletons.emplace(std::make_pair(acName, SingletonReference{m_lua.AsRef(), pType})); + return make_object(luaState, result.first->second); } size_t Scripting::Size(RED4ext::CBaseRTTIType* apRttiType) @@ -770,7 +816,7 @@ sol::object Scripting::ToLua(LockedState& aState, RED4ext::CStackType& aResult) { auto* pType = aResult.type; - auto& state = aState.Get(); + const auto& state = aState.Get(); if (pType == nullptr) return sol::nil; @@ -780,17 +826,17 @@ sol::object Scripting::ToLua(LockedState& aState, RED4ext::CStackType& aResult) { const auto handle = *static_cast*>(aResult.value); if (handle) - return make_object(state, StrongReference(aState, handle, static_cast(pType))); + return make_object(state, StrongReference(aState, handle, static_cast(pType))); } else if (pType->GetType() == RED4ext::ERTTIType::WeakHandle) { const auto handle = *static_cast*>(aResult.value); if (!handle.Expired()) - return make_object(state, WeakReference(aState, handle, static_cast(pType))); + return make_object(state, WeakReference(aState, handle, static_cast(pType))); } else if (pType->GetType() == RED4ext::ERTTIType::Array) { - auto* pArrayType = static_cast(pType); + const auto* pArrayType = static_cast(pType); const uint32_t cLength = pArrayType->GetLength(aResult.value); sol::table result(state, sol::create); for (auto i = 0u; i < cLength; ++i) @@ -812,7 +858,7 @@ sol::object Scripting::ToLua(LockedState& aState, RED4ext::CStackType& aResult) } else if (pType->GetType() == RED4ext::ERTTIType::ScriptReference) { - auto* pInstance = static_cast*>(aResult.value); + const auto* pInstance = static_cast*>(aResult.value); RED4ext::CStackType innerStack; innerStack.value = pInstance->ref; innerStack.type = pInstance->innerType; @@ -828,8 +874,7 @@ sol::object Scripting::ToLua(LockedState& aState, RED4ext::CStackType& aResult) return sol::nil; } -RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType* apRttiType, - TiltedPhoques::Allocator* apAllocator) +RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType* apRttiType, TiltedPhoques::Allocator* apAllocator) { RED4ext::CStackType result; @@ -845,7 +890,7 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType if (hasData) { sol::state_view v(aObject.lua_state()); - str = v["tostring"](aObject); + str = v["tostring"](aObject).get(); } result.value = apAllocator->New(str.c_str()); } @@ -853,8 +898,8 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType { if (aObject.is()) { - auto* pSubType = static_cast(apRttiType)->parent; - auto* pType = static_cast(aObject.as()->m_pType); + const auto* pSubType = static_cast(apRttiType)->parent; + const auto* pType = static_cast(aObject.as()->m_pType); if (pType && pType->IsA(pSubType)) { result.value = apAllocator->New>( @@ -863,8 +908,8 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType } else if (aObject.is()) { - auto* pSubType = static_cast(apRttiType)->parent; - auto* pType = static_cast(aObject.as()->m_pType); + const auto* pSubType = static_cast(apRttiType)->parent; + const auto* pType = static_cast(aObject.as()->m_pType); if (pType && pType->IsA(pSubType)) { result.value = apAllocator->New>( @@ -880,8 +925,8 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType { if (aObject.is()) { - auto* pSubType = static_cast(apRttiType)->parent; - auto* pType = static_cast(aObject.as()->m_pType); + const auto* pSubType = static_cast(apRttiType)->parent; + const auto* pType = static_cast(aObject.as()->m_pType); if (pType && pType->IsA(pSubType)) { result.value = apAllocator->New>( @@ -890,8 +935,8 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType } else if (aObject.is()) // Handle Implicit Cast { - auto* pSubType = static_cast(apRttiType)->parent; - auto* pType = static_cast(aObject.as()->m_pType); + const auto* pSubType = static_cast(apRttiType)->parent; + const auto* pType = static_cast(aObject.as()->m_pType); if (pType && pType->IsA(pSubType)) { result.value = apAllocator->New>( @@ -907,13 +952,13 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType { if (!hasData || aObject.get_type() == sol::type::table) { - auto* pArrayType = static_cast(apRttiType); + auto* pArrayType = static_cast(apRttiType); auto pMemory = static_cast*>(apAllocator->Allocate(apRttiType->GetSize())); - apRttiType->Init(pMemory); + apRttiType->Construct(pMemory); if (hasData) { - auto tbl = aObject.as(); + const auto tbl = aObject.as(); if (tbl.size() > 0) { @@ -921,15 +966,15 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType const auto shouldDestroy = pArrayInnerType->GetType() != RED4ext::ERTTIType::Class; // Copy elements from the table into the array - pArrayType->Resize(pMemory, tbl.size()); + pArrayType->Resize(pMemory, static_cast(tbl.size())); for (uint32_t i = 1; i <= tbl.size(); ++i) { - RED4ext::CStackType type = ToRED(tbl.get(i), pArrayInnerType, apAllocator); + const RED4ext::CStackType type = ToRED(tbl.get(i), pArrayInnerType, apAllocator); // Break on first incompatible element if (!type.value) { - pArrayType->Destroy(pMemory); + pArrayType->Destruct(pMemory); pMemory = nullptr; break; } @@ -938,7 +983,7 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType pArrayInnerType->Assign(pElement, type.value); if (shouldDestroy) - pArrayInnerType->Destroy(type.value); + pArrayInnerType->Destruct(type.value); } } } @@ -949,14 +994,14 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType else if (apRttiType->GetType() == RED4ext::ERTTIType::ScriptReference) { auto* pInnerType = static_cast(apRttiType)->innerType; - RED4ext::CStackType innerValue = ToRED(aObject, pInnerType, apAllocator); + const RED4ext::CStackType innerValue = ToRED(aObject, pInnerType, apAllocator); if (innerValue.value) { auto* pScriptRef = apAllocator->New>(); pScriptRef->innerType = innerValue.type; pScriptRef->ref = innerValue.value; - apRttiType->GetName(pScriptRef->hash); + pScriptRef->hash = apRttiType->GetName(); result.value = pScriptRef; } } @@ -985,7 +1030,7 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::CBaseRTTIType else if (IsLuaCData(aObject)) { sol::state_view v(aObject.lua_state()); - std::string str = v["tostring"](aObject); + const std::string str = v["tostring"](aObject); hash = std::stoull(str); } @@ -1011,12 +1056,12 @@ void Scripting::ToRED(sol::object aObject, RED4ext::CStackType& aRet) { static thread_local TiltedPhoques::ScratchAllocator s_scratchMemory(1 << 13); - auto result = ToRED(aObject, aRet.type, &s_scratchMemory); + const auto result = ToRED(aObject, aRet.type, &s_scratchMemory); aRet.type->Assign(aRet.value, result.value); if (aRet.type->GetType() != RED4ext::ERTTIType::Class) - aRet.type->Destroy(result.value); + aRet.type->Destruct(result.value); s_scratchMemory.Reset(); } diff --git a/src/scripting/Scripting.h b/src/scripting/Scripting.h index a5f6b9b8..5c3ef91d 100644 --- a/src/scripting/Scripting.h +++ b/src/scripting/Scripting.h @@ -11,15 +11,19 @@ struct Scripting { using LockedState = TiltedPhoques::Locked; - Scripting(const Paths& aPaths, VKBindings& aBindings, D3D12& aD3D12, Options& aOptions); + Scripting(const Paths& aPaths, VKBindings& aBindings, D3D12& aD3D12); ~Scripting() = default; void Initialize(); void PostInitializeScripting(); + void PostInitializeTweakDB(); void PostInitializeMods(); - const TiltedPhoques::Vector& GetBinds() const; + [[nodiscard]] const VKBind* GetBind(const VKModBind& acModBind) const; + [[nodiscard]] const TiltedPhoques::Vector* GetBinds(const std::string& acModName) const; + [[nodiscard]] const TiltedPhoques::Map>>& GetAllBinds() const; + void TriggerOnHook() const; void TriggerOnTweak() const; void TriggerOnInit() const; void TriggerOnUpdate(float aDeltaTime) const; @@ -28,12 +32,13 @@ struct Scripting void TriggerOnOverlayClose() const; sol::object GetMod(const std::string& acName) const; + void UnloadAllMods(); void ReloadAllMods(); - bool ExecuteLua(const std::string& acCommand); - void CollectGarbage(); + bool ExecuteLua(const std::string& acCommand) const; + void CollectGarbage() const; - LockedState GetState() const noexcept; - std::string GetGlobalName() const noexcept; + LuaSandbox& GetSandbox(); + LockedState GetLockedState() const noexcept; static size_t Size(RED4ext::CBaseRTTIType* apRttiType); static sol::object ToLua(LockedState& aState, RED4ext::CStackType& aResult); @@ -51,12 +56,10 @@ struct Scripting TiltedPhoques::Lockable m_lua; TiltedPhoques::Map m_properties{ }; TiltedPhoques::Map m_singletons{ }; - std::string m_global{ "Global" }; - RTTIMapper m_mapper; LuaSandbox m_sandbox; + RTTIMapper m_mapper; ScriptStore m_store; FunctionOverride m_override; const Paths& m_paths; D3D12& m_d3d12; - mutable std::recursive_mutex m_vmLock; }; diff --git a/src/scripting/Texture.cpp b/src/scripting/Texture.cpp index 84494628..7c7311e4 100644 --- a/src/scripting/Texture.cpp +++ b/src/scripting/Texture.cpp @@ -8,9 +8,7 @@ void Texture::BindTexture(sol::table& aTable) { - sol::state_view vm(aTable.lua_state()); - - vm.new_usertype("ImguiTexture", sol::no_constructor, + aTable.new_usertype("ImguiTexture", sol::no_constructor, "size", sol::property(&Texture::GetSize), "Release", &Texture::Release); @@ -24,7 +22,6 @@ void Texture::BindTexture(sol::table& aTable) [](const Texture& acTexture, ImVec2 aSize) { ImGuiImage(acTexture, aSize); }, [](const Texture& acTexture ){ ImGuiImage(acTexture); } )); - } std::shared_ptr Texture::Load(const std::string& acPath) @@ -49,8 +46,7 @@ std::shared_ptr Texture::Load(const std::string& acPath) return {}; // Create texture resource - D3D12_HEAP_PROPERTIES props; - memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); + D3D12_HEAP_PROPERTIES props = {}; props.Type = D3D12_HEAP_TYPE_DEFAULT; props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; @@ -69,13 +65,11 @@ std::shared_ptr Texture::Load(const std::string& acPath) desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; desc.Flags = D3D12_RESOURCE_FLAG_NONE; - ID3D12Resource* pTexture = NULL; - d3d_device->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, - IID_PPV_ARGS(&pTexture)); + Microsoft::WRL::ComPtr pTexture = nullptr; + d3d_device->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pTexture)); // Create a temporary upload resource to move the data in - UINT uploadPitch = - (image_width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); + UINT uploadPitch = (image_width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); UINT uploadSize = image_height * uploadPitch; desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; desc.Alignment = 0; @@ -93,23 +87,23 @@ std::shared_ptr Texture::Load(const std::string& acPath) props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - ID3D12Resource* uploadBuffer = NULL; + Microsoft::WRL::ComPtr uploadBuffer = nullptr; HRESULT hr = d3d_device->CreateCommittedResource( - &props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&uploadBuffer)); + &props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer)); IM_ASSERT(SUCCEEDED(hr)); // Write pixels into the upload resource - void* mapped = NULL; + void* mapped = nullptr; D3D12_RANGE range = {0, uploadSize}; hr = uploadBuffer->Map(0, &range, &mapped); IM_ASSERT(SUCCEEDED(hr)); for (int y = 0; y < image_height; y++) - memcpy((void*)((uintptr_t)mapped + y * uploadPitch), image_data + y * image_width * 4, image_width * 4); + memcpy(static_cast(mapped) + y * uploadPitch, image_data + y * image_width * 4, image_width * 4); uploadBuffer->Unmap(0, &range); // Copy the upload resource content into the real resource D3D12_TEXTURE_COPY_LOCATION srcLocation = {}; - srcLocation.pResource = uploadBuffer; + srcLocation.pResource = uploadBuffer.Get(); srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM; srcLocation.PlacedFootprint.Footprint.Width = image_width; @@ -118,52 +112,53 @@ std::shared_ptr Texture::Load(const std::string& acPath) srcLocation.PlacedFootprint.Footprint.RowPitch = uploadPitch; D3D12_TEXTURE_COPY_LOCATION dstLocation = {}; - dstLocation.pResource = pTexture; + dstLocation.pResource = pTexture.Get(); dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; dstLocation.SubresourceIndex = 0; D3D12_RESOURCE_BARRIER barrier = {}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = pTexture; + barrier.Transition.pResource = pTexture.Get(); barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; // Create a temporary command queue to do the copy with - ID3D12Fence* fence = NULL; + Microsoft::WRL::ComPtr fence = nullptr; hr = d3d_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); IM_ASSERT(SUCCEEDED(hr)); - HANDLE event = CreateEvent(0, 0, 0, 0); - IM_ASSERT(event != NULL); + HANDLE event = CreateEvent(nullptr, 0, 0, nullptr); + IM_ASSERT(event != nullptr); D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.NodeMask = 1; - ID3D12CommandQueue* cmdQueue = NULL; + Microsoft::WRL::ComPtr cmdQueue = nullptr; hr = d3d_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue)); IM_ASSERT(SUCCEEDED(hr)); - ID3D12CommandAllocator* cmdAlloc = NULL; + Microsoft::WRL::ComPtr cmdAlloc = nullptr; hr = d3d_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc)); IM_ASSERT(SUCCEEDED(hr)); - ID3D12GraphicsCommandList* cmdList = NULL; - hr = d3d_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, NULL, IID_PPV_ARGS(&cmdList)); + Microsoft::WRL::ComPtr cmdList = nullptr; + hr = d3d_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc.Get(), nullptr, IID_PPV_ARGS(&cmdList)); IM_ASSERT(SUCCEEDED(hr)); - cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, NULL); + cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr); cmdList->ResourceBarrier(1, &barrier); hr = cmdList->Close(); IM_ASSERT(SUCCEEDED(hr)); // Execute the copy - cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList); - hr = cmdQueue->Signal(fence, 1); + ID3D12CommandList* commandLists[] = { cmdList.Get() }; + cmdQueue->ExecuteCommandLists(1, commandLists); + hr = cmdQueue->Signal(fence.Get(), 1); IM_ASSERT(SUCCEEDED(hr)); // Wait for everything to complete @@ -186,11 +181,11 @@ std::shared_ptr Texture::Load(const std::string& acPath) srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - d3d_device->CreateShaderResourceView(pTexture, &srvDesc, srvCpuHandle); + d3d_device->CreateShaderResourceView(pTexture.Get(), &srvDesc, srvCpuHandle); // Return results auto texture = std::make_shared(); - texture->m_size = ImVec2(image_width, image_height); + texture->m_size = ImVec2(static_cast(image_width), static_cast(image_height)); texture->m_texture = pTexture; texture->m_handle = srvGpuHandle; @@ -215,5 +210,5 @@ ImVec2 Texture::GetSize() const void Texture::Release() { - m_texture.Release(); + m_texture = nullptr; } diff --git a/src/scripting/Texture.h b/src/scripting/Texture.h index e925ea54..5d1fe0ff 100644 --- a/src/scripting/Texture.h +++ b/src/scripting/Texture.h @@ -17,7 +17,7 @@ struct Texture private: - CComPtr m_texture; + Microsoft::WRL::ComPtr m_texture; D3D12_GPU_DESCRIPTOR_HANDLE m_handle; ImVec2 m_size{}; }; diff --git a/src/sol_imgui/sol_imgui.h b/src/sol_imgui/sol_imgui.h index dc6b5c01..df64ceca 100644 --- a/src/sol_imgui/sol_imgui.h +++ b/src/sol_imgui/sol_imgui.h @@ -4,7 +4,7 @@ namespace sol_ImGui { // Windows inline bool Begin(const std::string& name) { return ImGui::Begin(name.c_str()); } - inline bool Begin(const std::string& name, int flags) { return ImGui::Begin(name.c_str(), NULL, static_cast(flags)); } + inline bool Begin(const std::string& name, int flags) { return ImGui::Begin(name.c_str(), nullptr, flags); } inline std::tuple Begin(const std::string& name, bool open) { if (!open) return std::make_tuple(false, false); @@ -14,7 +14,7 @@ namespace sol_ImGui inline std::tuple Begin(const std::string& name, bool open, int flags) { if (!open) return std::make_tuple(false, false); - const bool shouldDraw = ImGui::Begin(name.c_str(), &open, static_cast(flags)); + const bool shouldDraw = ImGui::Begin(name.c_str(), &open, flags); return std::make_tuple(open, open && shouldDraw); } inline void End() { ImGui::End(); } @@ -24,16 +24,16 @@ namespace sol_ImGui inline bool BeginChild(const std::string& name, float sizeX) { return ImGui::BeginChild(name.c_str(), { sizeX, 0 }); } inline bool BeginChild(const std::string& name, float sizeX, float sizeY) { return ImGui::BeginChild(name.c_str(), { sizeX, sizeY }); } inline bool BeginChild(const std::string& name, float sizeX, float sizeY, bool border) { return ImGui::BeginChild(name.c_str(), { sizeX, sizeY }, border); } - inline bool BeginChild(const std::string& name, float sizeX, float sizeY, bool border, int flags) { return ImGui::BeginChild(name.c_str(), { sizeX, sizeY }, border, static_cast(flags)); } + inline bool BeginChild(const std::string& name, float sizeX, float sizeY, bool border, int flags) { return ImGui::BeginChild(name.c_str(), { sizeX, sizeY }, border, flags); } inline void EndChild() { ImGui::EndChild(); } // Windows Utilities inline bool IsWindowAppearing() { return ImGui::IsWindowAppearing(); } inline bool IsWindowCollapsed() { return ImGui::IsWindowCollapsed(); } inline bool IsWindowFocused() { return ImGui::IsWindowFocused(); } - inline bool IsWindowFocused(int flags) { return ImGui::IsWindowFocused(static_cast(flags)); } + inline bool IsWindowFocused(int flags) { return ImGui::IsWindowFocused(flags); } inline bool IsWindowHovered() { return ImGui::IsWindowHovered(); } - inline bool IsWindowHovered(int flags) { return ImGui::IsWindowHovered(static_cast(flags)); } + inline bool IsWindowHovered(int flags) { return ImGui::IsWindowHovered(flags); } inline ImDrawList* GetWindowDrawList() { return ImGui::GetWindowDrawList(); } inline std::tuple GetWindowPos() { const auto vec2{ ImGui::GetWindowPos() }; return std::make_tuple(vec2.x, vec2.y); } inline std::tuple GetWindowSize() { const auto vec2{ ImGui::GetWindowSize() }; return std::make_tuple(vec2.x, vec2.y); } @@ -42,30 +42,30 @@ namespace sol_ImGui // Prefer using SetNext... inline void SetNextWindowPos(float posX, float posY) { ImGui::SetNextWindowPos({ posX, posY }); } - inline void SetNextWindowPos(float posX, float posY, int cond) { ImGui::SetNextWindowPos({ posX, posY }, static_cast(cond)); } - inline void SetNextWindowPos(float posX, float posY, int cond, float pivotX, float pivotY) { ImGui::SetNextWindowPos({ posX, posY }, static_cast(cond), { pivotX, pivotY }); } + inline void SetNextWindowPos(float posX, float posY, int cond) { ImGui::SetNextWindowPos({ posX, posY }, cond); } + inline void SetNextWindowPos(float posX, float posY, int cond, float pivotX, float pivotY) { ImGui::SetNextWindowPos({ posX, posY }, cond, { pivotX, pivotY }); } inline void SetNextWindowSize(float sizeX, float sizeY) { ImGui::SetNextWindowSize({ sizeX, sizeY }); } - inline void SetNextWindowSize(float sizeX, float sizeY, int cond) { ImGui::SetNextWindowSize({ sizeX, sizeY }, static_cast(cond)); } + inline void SetNextWindowSize(float sizeX, float sizeY, int cond) { ImGui::SetNextWindowSize({ sizeX, sizeY }, cond); } inline void SetNextWindowSizeConstraints(float minX, float minY, float maxX, float maxY) { ImGui::SetNextWindowSizeConstraints({ minX, minY }, { maxX, maxY }); } inline void SetNextWindowContentSize(float sizeX, float sizeY) { ImGui::SetNextWindowContentSize({ sizeX, sizeY }); } inline void SetNextWindowCollapsed(bool collapsed) { ImGui::SetNextWindowCollapsed(collapsed); } - inline void SetNextWindowCollapsed(bool collapsed, int cond) { ImGui::SetNextWindowCollapsed(collapsed, static_cast(cond)); } + inline void SetNextWindowCollapsed(bool collapsed, int cond) { ImGui::SetNextWindowCollapsed(collapsed, cond); } inline void SetNextWindowFocus() { ImGui::SetNextWindowFocus(); } inline void SetNextWindowBgAlpha(float alpha) { ImGui::SetNextWindowBgAlpha(alpha); } inline void SetWindowPos(float posX, float posY) { ImGui::SetWindowPos({ posX, posY }); } - inline void SetWindowPos(float posX, float posY, int cond) { ImGui::SetWindowPos({ posX, posY }, static_cast(cond)); } + inline void SetWindowPos(float posX, float posY, int cond) { ImGui::SetWindowPos({ posX, posY }, cond); } inline void SetWindowSize(float sizeX, float sizeY) { ImGui::SetWindowSize({ sizeX, sizeY }); } - inline void SetWindowSize(float sizeX, float sizeY, int cond) { ImGui::SetWindowSize({ sizeX, sizeY }, static_cast(cond)); } + inline void SetWindowSize(float sizeX, float sizeY, int cond) { ImGui::SetWindowSize({ sizeX, sizeY }, cond); } inline void SetWindowCollapsed(bool collapsed) { ImGui::SetWindowCollapsed(collapsed); } - inline void SetWindowCollapsed(bool collapsed, int cond) { ImGui::SetWindowCollapsed(collapsed, static_cast(cond)); } + inline void SetWindowCollapsed(bool collapsed, int cond) { ImGui::SetWindowCollapsed(collapsed, cond); } inline void SetWindowFocus() { ImGui::SetWindowFocus(); } inline void SetWindowFontScale(float scale) { ImGui::SetWindowFontScale(scale); } inline void SetWindowPos(const std::string& name, float posX, float posY) { ImGui::SetWindowPos(name.c_str(), { posX, posY }); } - inline void SetWindowPos(const std::string& name, float posX, float posY, int cond) { ImGui::SetWindowPos(name.c_str(), { posX, posY }, static_cast(cond)); } + inline void SetWindowPos(const std::string& name, float posX, float posY, int cond) { ImGui::SetWindowPos(name.c_str(), { posX, posY }, cond); } inline void SetWindowSize(const std::string& name, float sizeX, float sizeY) { ImGui::SetWindowSize(name.c_str(), { sizeX, sizeY }); } - inline void SetWindowSize(const std::string& name, float sizeX, float sizeY, int cond) { ImGui::SetWindowSize(name.c_str(), { sizeX, sizeY }, static_cast(cond)); } + inline void SetWindowSize(const std::string& name, float sizeX, float sizeY, int cond) { ImGui::SetWindowSize(name.c_str(), { sizeX, sizeY }, cond); } inline void SetWindowCollapsed(const std::string& name, bool collapsed) { ImGui::SetWindowCollapsed(name.c_str(), collapsed); } - inline void SetWindowCollapsed(const std::string& name, bool collapsed, int cond) { ImGui::SetWindowCollapsed(name.c_str(), collapsed, static_cast(cond)); } + inline void SetWindowCollapsed(const std::string& name, bool collapsed, int cond) { ImGui::SetWindowCollapsed(name.c_str(), collapsed, cond); } inline void SetWindowFocus(const std::string& name) { ImGui::SetWindowFocus(name.c_str()); } // Content Region @@ -96,23 +96,23 @@ namespace sol_ImGui inline void PushFont(ImFont* pFont) { ImGui::PushFont(pFont); } inline void PopFont() { ImGui::PopFont(); } #endif // SOL_IMGUI_ENABLE_FONT_MANIPULATORS - inline void PushStyleColor(int idx, int col) { ImGui::PushStyleColor(static_cast(idx), ImU32(col)); } - inline void PushStyleColor(int idx, float colR, float colG, float colB, float colA) { ImGui::PushStyleColor(static_cast(idx), { colR, colG, colB, colA }); } + inline void PushStyleColor(int idx, int col) { ImGui::PushStyleColor(idx, static_cast(col)); } + inline void PushStyleColor(int idx, float colR, float colG, float colB, float colA) { ImGui::PushStyleColor(idx, { colR, colG, colB, colA }); } inline void PopStyleColor() { ImGui::PopStyleColor(); } inline void PopStyleColor(int count) { ImGui::PopStyleColor(count); } - inline void PushStyleVar(int idx, float val) { ImGui::PushStyleVar(static_cast(idx), val); } - inline void PushStyleVar(int idx, float valX, float valY) { ImGui::PushStyleVar(static_cast(idx), { valX, valY }); } + inline void PushStyleVar(int idx, float val) { ImGui::PushStyleVar(idx, val); } + inline void PushStyleVar(int idx, float valX, float valY) { ImGui::PushStyleVar(idx, { valX, valY }); } inline void PopStyleVar() { ImGui::PopStyleVar(); } inline void PopStyleVar(int count) { ImGui::PopStyleVar(count); } - inline std::tuple GetStyleColorVec4(int idx) { const auto col{ ImGui::GetStyleColorVec4(static_cast(idx)) }; return std::make_tuple(col.x, col.y, col.z, col.w); } + inline std::tuple GetStyleColorVec4(int idx) { const auto col{ ImGui::GetStyleColorVec4(idx) }; return std::make_tuple(col.x, col.y, col.z, col.w); } #ifdef SOL_IMGUI_ENABLE_FONT_MANIPULATORS inline ImFont* GetFont() { return ImGui::GetFont(); } #endif // SOL_IMGUI_ENABLE_FONT_MANIPULATORS inline float GetFontSize() { return ImGui::GetFontSize(); } inline std::tuple GetFontTexUvWhitePixel() { const auto vec2{ ImGui::GetFontTexUvWhitePixel() }; return std::make_tuple(vec2.x, vec2.y); } - inline int GetColorU32(int idx, float alphaMul) { return ImGui::GetColorU32(static_cast(idx), alphaMul); } + inline int GetColorU32(int idx, float alphaMul) { return ImGui::GetColorU32(idx, alphaMul); } inline int GetColorU32(float colR, float colG, float colB, float colA) { return ImGui::GetColorU32({ colR, colG, colB, colA }); } - inline int GetColorU32(int col) { return ImGui::GetColorU32(ImU32(col)); } + inline int GetColorU32(int col) { return ImGui::GetColorU32(static_cast(col)); } // Parameters stacks (current window) inline void PushItemWidth(float itemWidth) { ImGui::PushItemWidth(itemWidth); } @@ -165,11 +165,11 @@ namespace sol_ImGui // Widgets: Text inline void TextUnformatted(const std::string& text) { ImGui::TextUnformatted(text.c_str()); } inline void Text(const std::string& text) { ImGui::TextUnformatted(text.c_str()); } // TODO - make this proper call to ImGui::Text, allowing real formatting! - inline void TextColored(float colR, float colG, float colB, float colA, const std::string& text) { ImGui::TextColored({ colR, colG, colB, colA }, text.c_str()); } - inline void TextDisabled(const std::string& text) { ImGui::TextDisabled(text.c_str()); } - inline void TextWrapped(const std::string text) { ImGui::TextWrapped(text.c_str()); } - inline void LabelText(const std::string& label, const std::string& text) { ImGui::LabelText(label.c_str(), text.c_str()); } - inline void BulletText(const std::string& text) { ImGui::BulletText(text.c_str()); } + inline void TextColored(float colR, float colG, float colB, float colA, const std::string& text) { ImGui::TextColored({ colR, colG, colB, colA }, "%s", text.c_str()); } + inline void TextDisabled(const std::string& text) { ImGui::TextDisabled("%s", text.c_str()); } + inline void TextWrapped(const std::string& text) { ImGui::TextWrapped("%s", text.c_str()); } + inline void LabelText(const std::string& label, const std::string& text) { ImGui::LabelText(label.c_str(), "%s", text.c_str()); } + inline void BulletText(const std::string& text) { ImGui::BulletText("%s", text.c_str()); } // Widgets: Main inline bool Button(const std::string& label) { return ImGui::Button(label.c_str()); } @@ -196,7 +196,7 @@ namespace sol_ImGui // Widgets: Combo Box inline bool BeginCombo(const std::string& label, const std::string& previewValue) { return ImGui::BeginCombo(label.c_str(), previewValue.c_str()); } - inline bool BeginCombo(const std::string& label, const std::string& previewValue, int flags) { return ImGui::BeginCombo(label.c_str(), previewValue.c_str(), static_cast(flags)); } + inline bool BeginCombo(const std::string& label, const std::string& previewValue, int flags) { return ImGui::BeginCombo(label.c_str(), previewValue.c_str(), flags); } inline void EndCombo() { ImGui::EndCombo(); } inline std::tuple Combo(const std::string& label, int currentItem, const sol::table& items, int itemsCount) { @@ -246,12 +246,12 @@ namespace sol_ImGui inline std::tuple DragFloat(const std::string& label, float v, float v_speed, float v_min) { bool used = ImGui::DragFloat(label.c_str(), &v, v_speed, v_min); return std::make_tuple(v, used); } inline std::tuple DragFloat(const std::string& label, float v, float v_speed, float v_min, float v_max) { bool used = ImGui::DragFloat(label.c_str(), &v, v_speed, v_min, v_max); return std::make_tuple(v, used); } inline std::tuple DragFloat(const std::string& label, float v, float v_speed, float v_min, float v_max, const std::string& format) { bool used = ImGui::DragFloat(label.c_str(), &v, v_speed, v_min, v_max, format.c_str()); return std::make_tuple(v, used); } - inline std::tuple DragFloat(const std::string& label, float v, float v_speed, float v_min, float v_max, const std::string& format, int flags) { bool used = ImGui::DragFloat(label.c_str(), &v, v_speed, v_min, v_max, format.c_str(), static_cast(flags)); return std::make_tuple(v, used); } + inline std::tuple DragFloat(const std::string& label, float v, float v_speed, float v_min, float v_max, const std::string& format, int flags) { bool used = ImGui::DragFloat(label.c_str(), &v, v_speed, v_min, v_max, format.c_str(), flags); return std::make_tuple(v, used); } inline std::tuple>, bool> DragFloat2(const std::string& label, const sol::table& v) { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; + float value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::DragFloat2(label.c_str(), value); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ @@ -264,7 +264,7 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; + float value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::DragFloat2(label.c_str(), value, v_speed); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ @@ -277,7 +277,7 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; + float value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::DragFloat2(label.c_str(), value, v_speed, v_min); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ @@ -290,7 +290,7 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; + float value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::DragFloat2(label.c_str(), value, v_speed, v_min, v_max); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ @@ -303,7 +303,7 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; + float value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::DragFloat2(label.c_str(), value, v_speed, v_min, v_max, format.c_str()); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ @@ -316,8 +316,8 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; - bool used = ImGui::DragFloat2(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), static_cast(flags)); + float value[2] = { static_cast(v1), static_cast(v2) }; + bool used = ImGui::DragFloat2(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), flags); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1] @@ -330,7 +330,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::DragFloat3(label.c_str(), value); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ @@ -344,7 +344,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::DragFloat3(label.c_str(), value, v_speed); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ @@ -358,7 +358,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::DragFloat3(label.c_str(), value, v_speed, v_min); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ @@ -372,7 +372,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::DragFloat3(label.c_str(), value, v_speed, v_min, v_max); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ @@ -386,7 +386,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::DragFloat3(label.c_str(), value, v_speed, v_min, v_max, format.c_str()); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ @@ -400,8 +400,8 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; - bool used = ImGui::DragFloat3(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), static_cast(flags)); + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; + bool used = ImGui::DragFloat3(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), flags); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[2] @@ -415,7 +415,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::DragFloat4(label.c_str(), value); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ @@ -430,7 +430,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::DragFloat4(label.c_str(), value, v_speed); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ @@ -445,7 +445,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::DragFloat4(label.c_str(), value, v_speed, v_min); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ @@ -460,7 +460,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::DragFloat4(label.c_str(), value, v_speed, v_min, v_max); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ @@ -475,7 +475,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::DragFloat4(label.c_str(), value, v_speed, v_min, v_max, format.c_str()); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ @@ -490,8 +490,8 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; - bool used = ImGui::DragFloat4(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), static_cast(flags)); + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; + bool used = ImGui::DragFloat4(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), flags); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[2], value[3] @@ -505,12 +505,12 @@ namespace sol_ImGui inline std::tuple DragInt(const std::string& label, int v, float v_speed, int v_min) { bool used = ImGui::DragInt(label.c_str(), &v, v_speed, v_min); return std::make_tuple(v, used); } inline std::tuple DragInt(const std::string& label, int v, float v_speed, int v_min, int v_max) { bool used = ImGui::DragInt(label.c_str(), &v, v_speed, v_min, v_max); return std::make_tuple(v, used); } inline std::tuple DragInt(const std::string& label, int v, float v_speed, int v_min, int v_max, const std::string& format) { bool used = ImGui::DragInt(label.c_str(), &v, v_speed, v_min, v_max, format.c_str()); return std::make_tuple(v, used); } - inline std::tuple DragInt(const std::string& label, int v, float v_speed, int v_min, int v_max, const std::string& format, int flags) { bool used = ImGui::DragInt(label.c_str(), &v, v_speed, v_min, v_max, format.c_str(), static_cast(flags)); return std::make_tuple(v, used); } + inline std::tuple DragInt(const std::string& label, int v, float v_speed, int v_min, int v_max, const std::string& format, int flags) { bool used = ImGui::DragInt(label.c_str(), &v, v_speed, v_min, v_max, format.c_str(), flags); return std::make_tuple(v, used); } inline std::tuple>, bool> DragInt2(const std::string& label, const sol::table& v) { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - int value[2] = { int(v1), int(v2) }; + int value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::DragInt2(label.c_str(), value); sol::as_table_t int2 = sol::as_table(TiltedPhoques::Vector{ @@ -523,7 +523,7 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - int value[2] = { int(v1), int(v2) }; + int value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::DragInt2(label.c_str(), value, v_speed); sol::as_table_t int2 = sol::as_table(TiltedPhoques::Vector{ @@ -536,7 +536,7 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - int value[2] = { int(v1), int(v2) }; + int value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::DragInt2(label.c_str(), value, v_speed, v_min); sol::as_table_t int2 = sol::as_table(TiltedPhoques::Vector{ @@ -549,7 +549,7 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - int value[2] = { int(v1), int(v2) }; + int value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::DragInt2(label.c_str(), value, v_speed, v_min, v_max); sol::as_table_t int2 = sol::as_table(TiltedPhoques::Vector{ @@ -562,7 +562,7 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - int value[2] = { int(v1), int(v2) }; + int value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::DragInt2(label.c_str(), value, v_speed, v_min, v_max, format.c_str()); sol::as_table_t int2 = sol::as_table(TiltedPhoques::Vector{ @@ -575,8 +575,8 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - int value[2] = { int(v1), int(v2) }; - bool used = ImGui::DragInt2(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), static_cast(flags)); + int value[2] = { static_cast(v1), static_cast(v2) }; + bool used = ImGui::DragInt2(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), flags); sol::as_table_t int2 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1] @@ -589,7 +589,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - int value[3] = { int(v1), int(v2), int(v3) }; + int value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::DragInt3(label.c_str(), value); sol::as_table_t int3 = sol::as_table(TiltedPhoques::Vector{ @@ -603,7 +603,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - int value[3] = { int(v1), int(v2), int(v3) }; + int value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::DragInt3(label.c_str(), value, v_speed); sol::as_table_t int3 = sol::as_table(TiltedPhoques::Vector{ @@ -617,7 +617,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - int value[3] = { int(v1), int(v2), int(v3) }; + int value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::DragInt3(label.c_str(), value, v_speed, v_min); sol::as_table_t int3 = sol::as_table(TiltedPhoques::Vector{ @@ -631,7 +631,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - int value[3] = { int(v1), int(v2), int(v3) }; + int value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::DragInt3(label.c_str(), value, v_speed, v_min, v_max); sol::as_table_t int3 = sol::as_table(TiltedPhoques::Vector{ @@ -645,7 +645,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - int value[3] = { int(v1), int(v2), int(v3) }; + int value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::DragInt3(label.c_str(), value, v_speed, v_min, v_max, format.c_str()); sol::as_table_t int3 = sol::as_table(TiltedPhoques::Vector{ @@ -659,8 +659,8 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - int value[3] = { int(v1), int(v2), int(v3) }; - bool used = ImGui::DragInt3(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), static_cast(flags)); + int value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; + bool used = ImGui::DragInt3(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), flags); sol::as_table_t int3 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[2] @@ -674,7 +674,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - int value[4] = { int(v1), int(v2), int(v3), int(v4) }; + int value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::DragInt4(label.c_str(), value); sol::as_table_t int4 = sol::as_table(TiltedPhoques::Vector{ @@ -689,7 +689,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - int value[4] = { int(v1), int(v2), int(v3), int(v4) }; + int value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::DragInt4(label.c_str(), value, v_speed); sol::as_table_t int4 = sol::as_table(TiltedPhoques::Vector{ @@ -704,7 +704,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - int value[4] = { int(v1), int(v2), int(v3), int(v4) }; + int value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::DragInt4(label.c_str(), value, v_speed, v_min); sol::as_table_t int4 = sol::as_table(TiltedPhoques::Vector{ @@ -719,7 +719,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - int value[4] = { int(v1), int(v2), int(v3), int(v4) }; + int value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::DragInt4(label.c_str(), value, v_speed, v_min, v_max); sol::as_table_t int4 = sol::as_table(TiltedPhoques::Vector{ @@ -734,7 +734,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - int value[4] = { int(v1), int(v2), int(v3), int(v4) }; + int value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::DragInt4(label.c_str(), value, v_speed, v_min, v_max, format.c_str()); sol::as_table_t int4 = sol::as_table(TiltedPhoques::Vector{ @@ -749,8 +749,8 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - int value[4] = { int(v1), int(v2), int(v3), int(v4) }; - bool used = ImGui::DragInt4(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), static_cast(flags)); + int value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; + bool used = ImGui::DragInt4(label.c_str(), value, v_speed, v_min, v_max, format.c_str(), flags); sol::as_table_t int4 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[2], value[3] @@ -765,12 +765,12 @@ namespace sol_ImGui // Widgets: Sliders inline std::tuple SliderFloat(const std::string& label, float v, float v_min, float v_max) { bool used = ImGui::SliderFloat(label.c_str(), &v, v_min, v_max); return std::make_tuple(v, used); } inline std::tuple SliderFloat(const std::string& label, float v, float v_min, float v_max, const std::string& format) { bool used = ImGui::SliderFloat(label.c_str(), &v, v_min, v_max, format.c_str()); return std::make_tuple(v, used); } - inline std::tuple SliderFloat(const std::string& label, float v, float v_min, float v_max, const std::string& format, int flags) { bool used = ImGui::SliderFloat(label.c_str(), &v, v_min, v_max, format.c_str(), static_cast(flags)); return std::make_tuple(v, used); } + inline std::tuple SliderFloat(const std::string& label, float v, float v_min, float v_max, const std::string& format, int flags) { bool used = ImGui::SliderFloat(label.c_str(), &v, v_min, v_max, format.c_str(), flags); return std::make_tuple(v, used); } inline std::tuple>, bool> SliderFloat2(const std::string& label, const sol::table& v, float v_min, float v_max) { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; + float value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::SliderFloat2(label.c_str(), value, v_min, v_max); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ @@ -783,7 +783,7 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; + float value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::SliderFloat2(label.c_str(), value, v_min, v_max, format.c_str()); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ @@ -796,8 +796,8 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; - bool used = ImGui::SliderFloat2(label.c_str(), value, v_min, v_max, format.c_str(), static_cast(flags)); + float value[2] = { static_cast(v1), static_cast(v2) }; + bool used = ImGui::SliderFloat2(label.c_str(), value, v_min, v_max, format.c_str(), flags); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1] @@ -810,7 +810,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::SliderFloat3(label.c_str(), value, v_min, v_max); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ @@ -824,7 +824,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::SliderFloat3(label.c_str(), value, v_min, v_max, format.c_str()); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ @@ -838,8 +838,8 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; - bool used = ImGui::SliderFloat3(label.c_str(), value, v_min, v_max, format.c_str(), static_cast(flags)); + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; + bool used = ImGui::SliderFloat3(label.c_str(), value, v_min, v_max, format.c_str(), flags); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[3] @@ -853,7 +853,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::SliderFloat4(label.c_str(), value, v_min, v_max); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ @@ -868,7 +868,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::SliderFloat4(label.c_str(), value, v_min, v_max, format.c_str()); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ @@ -883,8 +883,8 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; - bool used = ImGui::SliderFloat4(label.c_str(), value, v_min, v_max, format.c_str(), static_cast(flags)); + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; + bool used = ImGui::SliderFloat4(label.c_str(), value, v_min, v_max, format.c_str(), flags); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[2], value[3] @@ -896,15 +896,15 @@ namespace sol_ImGui inline std::tuple SliderAngle(const std::string& label, float v_rad, float v_degrees_min) { bool used = ImGui::SliderAngle(label.c_str(), &v_rad, v_degrees_min); return std::make_tuple(v_rad, used); } inline std::tuple SliderAngle(const std::string& label, float v_rad, float v_degrees_min, float v_degrees_max) { bool used = ImGui::SliderAngle(label.c_str(), &v_rad, v_degrees_min, v_degrees_max); return std::make_tuple(v_rad, used); } inline std::tuple SliderAngle(const std::string& label, float v_rad, float v_degrees_min, float v_degrees_max, const std::string& format) { bool used = ImGui::SliderAngle(label.c_str(), &v_rad, v_degrees_min, v_degrees_max, format.c_str()); return std::make_tuple(v_rad, used); } - inline std::tuple SliderAngle(const std::string& label, float v_rad, float v_degrees_min, float v_degrees_max, const std::string& format, int flags) { bool used = ImGui::SliderAngle(label.c_str(), &v_rad, v_degrees_min, v_degrees_max, format.c_str(), static_cast(flags)); return std::make_tuple(v_rad, used); } + inline std::tuple SliderAngle(const std::string& label, float v_rad, float v_degrees_min, float v_degrees_max, const std::string& format, int flags) { bool used = ImGui::SliderAngle(label.c_str(), &v_rad, v_degrees_min, v_degrees_max, format.c_str(), flags); return std::make_tuple(v_rad, used); } inline std::tuple SliderInt(const std::string& label, int v, int v_min, int v_max) { bool used = ImGui::SliderInt(label.c_str(), &v, v_min, v_max); return std::make_tuple(v, used); } inline std::tuple SliderInt(const std::string& label, int v, int v_min, int v_max, const std::string& format) { bool used = ImGui::SliderInt(label.c_str(), &v, v_min, v_max, format.c_str()); return std::make_tuple(v, used); } - inline std::tuple SliderInt(const std::string& label, int v, int v_min, int v_max, const std::string& format, int flags) { bool used = ImGui::SliderInt(label.c_str(), &v, v_min, v_max, format.c_str(), static_cast(flags)); return std::make_tuple(v, used); } + inline std::tuple SliderInt(const std::string& label, int v, int v_min, int v_max, const std::string& format, int flags) { bool used = ImGui::SliderInt(label.c_str(), &v, v_min, v_max, format.c_str(), flags); return std::make_tuple(v, used); } inline std::tuple>, bool> SliderInt2(const std::string& label, const sol::table& v, int v_min, int v_max) { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - int value[2] = { int(v1), int(v2) }; + int value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::SliderInt2(label.c_str(), value, v_min, v_max); sol::as_table_t int2 = sol::as_table(TiltedPhoques::Vector{ @@ -917,7 +917,7 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - int value[2] = { int(v1), int(v2) }; + int value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::SliderInt2(label.c_str(), value, v_min, v_max, format.c_str()); sol::as_table_t int2 = sol::as_table(TiltedPhoques::Vector{ @@ -930,8 +930,8 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - int value[2] = { int(v1), int(v2) }; - bool used = ImGui::SliderInt2(label.c_str(), value, v_min, v_max, format.c_str(), static_cast(flags)); + int value[2] = { static_cast(v1), static_cast(v2) }; + bool used = ImGui::SliderInt2(label.c_str(), value, v_min, v_max, format.c_str(), flags); sol::as_table_t int2 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1] @@ -944,7 +944,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - int value[3] = { int(v1), int(v2), int(v3) }; + int value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::SliderInt3(label.c_str(), value, v_min, v_max); sol::as_table_t int3 = sol::as_table(TiltedPhoques::Vector{ @@ -958,7 +958,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - int value[3] = { int(v1), int(v2), int(v3) }; + int value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::SliderInt3(label.c_str(), value, v_min, v_max, format.c_str()); sol::as_table_t int3 = sol::as_table(TiltedPhoques::Vector{ @@ -972,8 +972,8 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - int value[3] = { int(v1), int(v2), int(v3) }; - bool used = ImGui::SliderInt3(label.c_str(), value, v_min, v_max, format.c_str(), static_cast(flags)); + int value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; + bool used = ImGui::SliderInt3(label.c_str(), value, v_min, v_max, format.c_str(), flags); sol::as_table_t int3 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[2] @@ -987,7 +987,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - int value[4] = { int(v1), int(v2), int(v3), int(v4) }; + int value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::SliderInt4(label.c_str(), value, v_min, v_max); sol::as_table_t int4 = sol::as_table(TiltedPhoques::Vector{ @@ -1002,7 +1002,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - int value[4] = { int(v1), int(v2), int(v3), int(v4) }; + int value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::SliderInt4(label.c_str(), value, v_min, v_max, format.c_str()); sol::as_table_t int4 = sol::as_table(TiltedPhoques::Vector{ @@ -1017,8 +1017,8 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - int value[4] = { int(v1), int(v2), int(v3), int(v4) }; - bool used = ImGui::SliderInt4(label.c_str(), value, v_min, v_max, format.c_str(), static_cast(flags)); + int value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; + bool used = ImGui::SliderInt4(label.c_str(), value, v_min, v_max, format.c_str(), flags); sol::as_table_t int4 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[2], value[3] @@ -1030,30 +1030,30 @@ namespace sol_ImGui inline void SliderScalarN() { /* TODO: SliderScalarN(...) ==> UNSUPPORTED */ } inline std::tuple VSliderFloat(const std::string& label, float sizeX, float sizeY, float v, float v_min, float v_max) { bool used = ImGui::VSliderFloat(label.c_str(), { sizeX, sizeY }, &v, v_min, v_max); return std::make_tuple(v, used); } inline std::tuple VSliderFloat(const std::string& label, float sizeX, float sizeY, float v, float v_min, float v_max, const std::string& format) { bool used = ImGui::VSliderFloat(label.c_str(), { sizeX, sizeY }, &v, v_min, v_max, format.c_str()); return std::make_tuple(v, used); } - inline std::tuple VSliderFloat(const std::string& label, float sizeX, float sizeY, float v, float v_min, float v_max, const std::string& format, int flags) { bool used = ImGui::VSliderFloat(label.c_str(), { sizeX, sizeY }, &v, v_min, v_max, format.c_str(), static_cast(flags)); return std::make_tuple(v, used); } + inline std::tuple VSliderFloat(const std::string& label, float sizeX, float sizeY, float v, float v_min, float v_max, const std::string& format, int flags) { bool used = ImGui::VSliderFloat(label.c_str(), { sizeX, sizeY }, &v, v_min, v_max, format.c_str(), flags); return std::make_tuple(v, used); } inline std::tuple VSliderInt(const std::string& label, float sizeX, float sizeY, int v, int v_min, int v_max) { bool used = ImGui::VSliderInt(label.c_str(), { sizeX, sizeY }, &v, v_min, v_max); return std::make_tuple(v, used); } inline std::tuple VSliderInt(const std::string& label, float sizeX, float sizeY, int v, int v_min, int v_max, const std::string& format) { bool used = ImGui::VSliderInt(label.c_str(), { sizeX, sizeY }, &v, v_min, v_max, format.c_str()); return std::make_tuple(v, used); } - inline std::tuple VSliderInt(const std::string& label, float sizeX, float sizeY, int v, int v_min, int v_max, const std::string& format, int flags) { bool used = ImGui::VSliderInt(label.c_str(), { sizeX, sizeY }, &v, v_min, v_max, format.c_str(), static_cast(flags)); return std::make_tuple(v, used); } + inline std::tuple VSliderInt(const std::string& label, float sizeX, float sizeY, int v, int v_min, int v_max, const std::string& format, int flags) { bool used = ImGui::VSliderInt(label.c_str(), { sizeX, sizeY }, &v, v_min, v_max, format.c_str(), flags); return std::make_tuple(v, used); } inline void VSliderScalar() { /* TODO: VSliderScalar(...) ==> UNSUPPORTED */ } // Widgets: Input with Keyboard - inline std::tuple InputText(const std::string& label, std::string text, unsigned int buf_size) { text.resize(buf_size); bool selected = ImGui::InputText(label.c_str(), &text[0], buf_size); return std::make_tuple(text.c_str(), selected); } - inline std::tuple InputText(const std::string& label, std::string text, unsigned int buf_size, int flags) { text.resize(buf_size); bool selected = ImGui::InputText(label.c_str(), &text[0], buf_size, static_cast(flags)); return std::make_tuple(text.c_str(), selected); } - inline std::tuple InputTextMultiline(const std::string& label, std::string text, unsigned int buf_size) { text.resize(buf_size); bool selected = ImGui::InputTextMultiline(label.c_str(), &text[0], buf_size); return std::make_tuple(text.c_str(), selected); } - inline std::tuple InputTextMultiline(const std::string& label, std::string text, unsigned int buf_size, float sizeX, float sizeY) { text.resize(buf_size); bool selected = ImGui::InputTextMultiline(label.c_str(), &text[0], buf_size, { sizeX, sizeY }); return std::make_tuple(text.c_str(), selected); } - inline std::tuple InputTextMultiline(const std::string& label, std::string text, unsigned int buf_size, float sizeX, float sizeY, int flags) { text.resize(buf_size); bool selected = ImGui::InputTextMultiline(label.c_str(), &text[0], buf_size, { sizeX, sizeY }, static_cast(flags)); return std::make_tuple(text.c_str(), selected); } - inline std::tuple InputTextWithHint(const std::string& label, const std::string& hint, std::string text, unsigned int buf_size) { text.resize(buf_size); bool selected = ImGui::InputTextWithHint(label.c_str(), hint.c_str(), &text[0], buf_size); return std::make_tuple(text.c_str(), selected); } - inline std::tuple InputTextWithHint(const std::string& label, const std::string& hint, std::string text, unsigned int buf_size, int flags) { text.resize(buf_size); bool selected = ImGui::InputTextWithHint(label.c_str(), hint.c_str(), &text[0], buf_size, static_cast(flags)); return std::make_tuple(text.c_str(), selected); } + inline std::tuple InputText(const std::string& label, std::string text, unsigned int buf_size) { text.resize(buf_size); bool selected = ImGui::InputText(label.c_str(), text.data(), buf_size); return std::make_tuple(text.c_str(), selected); } + inline std::tuple InputText(const std::string& label, std::string text, unsigned int buf_size, int flags) { text.resize(buf_size); bool selected = ImGui::InputText(label.c_str(), text.data(), buf_size, flags); return std::make_tuple(text.c_str(), selected); } + inline std::tuple InputTextMultiline(const std::string& label, std::string text, unsigned int buf_size) { text.resize(buf_size); bool selected = ImGui::InputTextMultiline(label.c_str(), text.data(), buf_size); return std::make_tuple(text.c_str(), selected); } + inline std::tuple InputTextMultiline(const std::string& label, std::string text, unsigned int buf_size, float sizeX, float sizeY) { text.resize(buf_size); bool selected = ImGui::InputTextMultiline(label.c_str(), text.data(), buf_size, { sizeX, sizeY }); return std::make_tuple(text.c_str(), selected); } + inline std::tuple InputTextMultiline(const std::string& label, std::string text, unsigned int buf_size, float sizeX, float sizeY, int flags) { text.resize(buf_size); bool selected = ImGui::InputTextMultiline(label.c_str(), text.data(), buf_size, { sizeX, sizeY }, flags); return std::make_tuple(text.c_str(), selected); } + inline std::tuple InputTextWithHint(const std::string& label, const std::string& hint, std::string text, unsigned int buf_size) { text.resize(buf_size); bool selected = ImGui::InputTextWithHint(label.c_str(), hint.c_str(), text.data(), buf_size); return std::make_tuple(text.c_str(), selected); } + inline std::tuple InputTextWithHint(const std::string& label, const std::string& hint, std::string text, unsigned int buf_size, int flags) { text.resize(buf_size); bool selected = ImGui::InputTextWithHint(label.c_str(), hint.c_str(), text.data(), buf_size, flags); return std::make_tuple(text.c_str(), selected); } inline std::tuple InputFloat(const std::string& label, float v) { bool selected = ImGui::InputFloat(label.c_str(), &v); return std::make_tuple(v, selected); } inline std::tuple InputFloat(const std::string& label, float v, float step) { bool selected = ImGui::InputFloat(label.c_str(), &v, step); return std::make_tuple(v, selected); } inline std::tuple InputFloat(const std::string& label, float v, float step, float step_fast) { bool selected = ImGui::InputFloat(label.c_str(), &v, step, step_fast); return std::make_tuple(v, selected); } inline std::tuple InputFloat(const std::string& label, float v, float step, float step_fast, const std::string& format) { bool selected = ImGui::InputFloat(label.c_str(), &v, step, step_fast, format.c_str()); return std::make_tuple(v, selected); } - inline std::tuple InputFloat(const std::string& label, float v, float step, float step_fast, const std::string& format, int flags) { bool selected = ImGui::InputFloat(label.c_str(), &v, step, step_fast, format.c_str(), static_cast(flags)); return std::make_tuple(v, selected); } + inline std::tuple InputFloat(const std::string& label, float v, float step, float step_fast, const std::string& format, int flags) { bool selected = ImGui::InputFloat(label.c_str(), &v, step, step_fast, format.c_str(), flags); return std::make_tuple(v, selected); } inline std::tuple >, bool> InputFloat2(const std::string& label, const sol::table& v) { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; + float value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::InputFloat2(label.c_str(), value); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ @@ -1066,7 +1066,7 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; + float value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::InputFloat2(label.c_str(), value, format.c_str()); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ @@ -1079,8 +1079,8 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - float value[2] = { float(v1), float(v2) }; - bool used = ImGui::InputFloat2(label.c_str(), value, format.c_str(), static_cast(flags)); + float value[2] = { static_cast(v1), static_cast(v2) }; + bool used = ImGui::InputFloat2(label.c_str(), value, format.c_str(), flags); sol::as_table_t float2 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1] @@ -1093,7 +1093,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::InputFloat3(label.c_str(), value); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ @@ -1107,7 +1107,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::InputFloat3(label.c_str(), value, format.c_str()); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ @@ -1121,8 +1121,8 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - float value[3] = { float(v1), float(v2), float(v3) }; - bool used = ImGui::InputFloat3(label.c_str(), value, format.c_str(), static_cast(flags)); + float value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; + bool used = ImGui::InputFloat3(label.c_str(), value, format.c_str(), flags); sol::as_table_t float3 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[2] @@ -1136,7 +1136,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::InputFloat4(label.c_str(), value); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ @@ -1151,7 +1151,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::InputFloat4(label.c_str(), value, format.c_str()); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ @@ -1166,8 +1166,8 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - float value[4] = { float(v1), float(v2), float(v3), float(v4) }; - bool used = ImGui::InputFloat4(label.c_str(), value, format.c_str(), static_cast(flags)); + float value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; + bool used = ImGui::InputFloat4(label.c_str(), value, format.c_str(), flags); sol::as_table_t float4 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[2], value[3] @@ -1178,12 +1178,12 @@ namespace sol_ImGui inline std::tuple InputInt(const std::string& label, int v) { bool selected = ImGui::InputInt(label.c_str(), &v); return std::make_tuple(v, selected); } inline std::tuple InputInt(const std::string& label, int v, int step) { bool selected = ImGui::InputInt(label.c_str(), &v, step); return std::make_tuple(v, selected); } inline std::tuple InputInt(const std::string& label, int v, int step, int step_fast) { bool selected = ImGui::InputInt(label.c_str(), &v, step, step_fast); return std::make_tuple(v, selected); } - inline std::tuple InputInt(const std::string& label, int v, int step, int step_fast, int flags) { bool selected = ImGui::InputInt(label.c_str(), &v, step, step_fast, static_cast(flags)); return std::make_tuple(v, selected); } + inline std::tuple InputInt(const std::string& label, int v, int step, int step_fast, int flags) { bool selected = ImGui::InputInt(label.c_str(), &v, step, step_fast, flags); return std::make_tuple(v, selected); } inline std::tuple >, bool> InputInt2(const std::string& label, const sol::table& v) { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - int value[2] = { int(v1), int(v2) }; + int value[2] = { static_cast(v1), static_cast(v2) }; bool used = ImGui::InputInt2(label.c_str(), value); sol::as_table_t int2 = sol::as_table(TiltedPhoques::Vector{ @@ -1196,8 +1196,8 @@ namespace sol_ImGui { const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }; - int value[2] = { int(v1), int(v2) }; - bool used = ImGui::InputInt2(label.c_str(), value, static_cast(flags)); + int value[2] = { static_cast(v1), static_cast(v2) }; + bool used = ImGui::InputInt2(label.c_str(), value, flags); sol::as_table_t int2 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1] @@ -1210,7 +1210,7 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - int value[3] = { int(v1), int(v2), int(v3) }; + int value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; bool used = ImGui::InputInt3(label.c_str(), value); sol::as_table_t int3 = sol::as_table(TiltedPhoques::Vector{ @@ -1224,8 +1224,8 @@ namespace sol_ImGui const lua_Number v1{ v[1].get>().value_or(static_cast(0)) }, v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }; - int value[3] = { int(v1), int(v2), int(v3) }; - bool used = ImGui::InputInt3(label.c_str(), value, static_cast(flags)); + int value[3] = { static_cast(v1), static_cast(v2), static_cast(v3) }; + bool used = ImGui::InputInt3(label.c_str(), value, flags); sol::as_table_t int3 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[2] @@ -1239,7 +1239,7 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - int value[4] = { int(v1), int(v2), int(v3), int(v4) }; + int value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; bool used = ImGui::InputInt4(label.c_str(), value); sol::as_table_t int4 = sol::as_table(TiltedPhoques::Vector{ @@ -1254,8 +1254,8 @@ namespace sol_ImGui v2{ v[2].get>().value_or(static_cast(0)) }, v3{ v[3].get>().value_or(static_cast(0)) }, v4{ v[4].get>().value_or(static_cast(0)) }; - int value[4] = { int(v1), int(v2), int(v3), int(v4) }; - bool used = ImGui::InputInt4(label.c_str(), value, static_cast(flags)); + int value[4] = { static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4) }; + bool used = ImGui::InputInt4(label.c_str(), value, flags); sol::as_table_t int4 = sol::as_table(TiltedPhoques::Vector{ value[0], value[1], value[2], value[3] @@ -1267,7 +1267,7 @@ namespace sol_ImGui inline std::tuple InputDouble(const std::string& label, double v, double step) { bool selected = ImGui::InputDouble(label.c_str(), &v, step); return std::make_tuple(v, selected); } inline std::tuple InputDouble(const std::string& label, double v, double step, double step_fast) { bool selected = ImGui::InputDouble(label.c_str(), &v, step, step_fast); return std::make_tuple(v, selected); } inline std::tuple InputDouble(const std::string& label, double v, double step, double step_fast, const std::string& format) { bool selected = ImGui::InputDouble(label.c_str(), &v, step, step_fast, format.c_str()); return std::make_tuple(v, selected); } - inline std::tuple InputDouble(const std::string& label, double v, double step, double step_fast, const std::string& format, int flags) { bool selected = ImGui::InputDouble(label.c_str(), &v, step, step_fast, format.c_str(), static_cast(flags)); return std::make_tuple(v, selected); } + inline std::tuple InputDouble(const std::string& label, double v, double step, double step_fast, const std::string& format, int flags) { bool selected = ImGui::InputDouble(label.c_str(), &v, step, step_fast, format.c_str(), flags); return std::make_tuple(v, selected); } inline void InputScalar() { /* TODO: InputScalar(...) ==> UNSUPPORTED */ } inline void InputScalarN() { /* TODO: InputScalarN(...) ==> UNSUPPORTED */ } @@ -1277,7 +1277,7 @@ namespace sol_ImGui const lua_Number r{ col[1].get>().value_or(static_cast(0)) }, g{ col[2].get>().value_or(static_cast(0)) }, b{ col[3].get>().value_or(static_cast(0)) }; - float color[3] = { float(r), float(g), float(b) }; + float color[3] = { static_cast(r), static_cast(g), static_cast(b) }; bool used = ImGui::ColorEdit3(label.c_str(), color); sol::as_table_t rgb = sol::as_table(TiltedPhoques::Vector{ @@ -1291,8 +1291,8 @@ namespace sol_ImGui const lua_Number r{ col[1].get>().value_or(static_cast(0)) }, g{ col[2].get>().value_or(static_cast(0)) }, b{ col[3].get>().value_or(static_cast(0)) }; - float color[3] = { float(r), float(g), float(b) }; - bool used = ImGui::ColorEdit3(label.c_str(), color, static_cast(flags)); + float color[3] = { static_cast(r), static_cast(g), static_cast(b) }; + bool used = ImGui::ColorEdit3(label.c_str(), color, flags); sol::as_table_t rgb = sol::as_table(TiltedPhoques::Vector{ color[0], color[1], color[2] @@ -1306,7 +1306,7 @@ namespace sol_ImGui g{ col[2].get>().value_or(static_cast(0)) }, b{ col[3].get>().value_or(static_cast(0)) }, a{ col[4].get>().value_or(static_cast(0)) }; - float color[4] = { float(r), float(g), float(b), float(a) }; + float color[4] = { static_cast(r), static_cast(g), static_cast(b), static_cast(a) }; bool used = ImGui::ColorEdit4(label.c_str(), color); sol::as_table_t rgba = sol::as_table(TiltedPhoques::Vector{ @@ -1321,8 +1321,8 @@ namespace sol_ImGui g{ col[2].get>().value_or(static_cast(0)) }, b{ col[3].get>().value_or(static_cast(0)) }, a{ col[4].get>().value_or(static_cast(0)) }; - float color[4] = { float(r), float(g), float(b), float(a) }; - bool used = ImGui::ColorEdit4(label.c_str(), color, static_cast(flags)); + float color[4] = { static_cast(r), static_cast(g), static_cast(b), static_cast(a) }; + bool used = ImGui::ColorEdit4(label.c_str(), color, flags); sol::as_table_t rgba = sol::as_table(TiltedPhoques::Vector{ color[0], color[1], color[2], color[3] @@ -1335,7 +1335,7 @@ namespace sol_ImGui const lua_Number r{ col[1].get>().value_or(static_cast(0)) }, g{ col[2].get>().value_or(static_cast(0)) }, b{ col[3].get>().value_or(static_cast(0)) }; - float color[3] = { float(r), float(g), float(b) }; + float color[3] = { static_cast(r), static_cast(g), static_cast(b) }; bool used = ImGui::ColorPicker3(label.c_str(), color); sol::as_table_t rgb = sol::as_table(TiltedPhoques::Vector{ @@ -1349,8 +1349,8 @@ namespace sol_ImGui const lua_Number r{ col[1].get>().value_or(static_cast(0)) }, g{ col[2].get>().value_or(static_cast(0)) }, b{ col[3].get>().value_or(static_cast(0)) }; - float color[3] = { float(r), float(g), float(b) }; - bool used = ImGui::ColorPicker3(label.c_str(), color, static_cast(flags)); + float color[3] = { static_cast(r), static_cast(g), static_cast(b) }; + bool used = ImGui::ColorPicker3(label.c_str(), color, flags); sol::as_table_t rgb = sol::as_table(TiltedPhoques::Vector{ color[0], color[1], color[2] @@ -1364,7 +1364,7 @@ namespace sol_ImGui g{ col[2].get>().value_or(static_cast(0)) }, b{ col[3].get>().value_or(static_cast(0)) }, a{ col[4].get>().value_or(static_cast(0)) }; - float color[4] = { float(r), float(g), float(b), float(a) }; + float color[4] = { static_cast(r), static_cast(g), static_cast(b), static_cast(a) }; bool used = ImGui::ColorPicker4(label.c_str(), color); sol::as_table_t rgba = sol::as_table(TiltedPhoques::Vector{ @@ -1379,8 +1379,8 @@ namespace sol_ImGui g{ col[2].get>().value_or(static_cast(0)) }, b{ col[3].get>().value_or(static_cast(0)) }, a{ col[4].get>().value_or(static_cast(0)) }; - float color[4] = { float(r), float(g), float(b), float(a) }; - bool used = ImGui::ColorPicker4(label.c_str(), color, static_cast(flags)); + float color[4] = { static_cast(r), static_cast(g), static_cast(b), static_cast(a) }; + bool used = ImGui::ColorPicker4(label.c_str(), color, flags); sol::as_table_t rgba = sol::as_table(TiltedPhoques::Vector{ color[0], color[1], color[2], color[3] @@ -1394,7 +1394,7 @@ namespace sol_ImGui g{ col[2].get>().value_or(static_cast(0)) }, b{ col[3].get>().value_or(static_cast(0)) }, a{ col[4].get>().value_or(static_cast(0)) }; - const ImVec4 color{ float(r), float(g), float(b), float(a) }; + const ImVec4 color{ static_cast(r), static_cast(g), static_cast(b), static_cast(a) }; return ImGui::ColorButton(desc_id.c_str(), color); } inline bool ColorButton(const std::string& desc_id, const sol::table& col, int flags) @@ -1403,8 +1403,8 @@ namespace sol_ImGui g{ col[2].get>().value_or(static_cast(0)) }, b{ col[3].get>().value_or(static_cast(0)) }, a{ col[4].get>().value_or(static_cast(0)) }; - const ImVec4 color{ float(r), float(g), float(b), float(a) }; - return ImGui::ColorButton(desc_id.c_str(), color, static_cast(flags)); + const ImVec4 color{ static_cast(r), static_cast(g), static_cast(b), static_cast(a) }; + return ImGui::ColorButton(desc_id.c_str(), color, flags); } inline bool ColorButton(const std::string& desc_id, const sol::table& col, int flags, float sizeX, float sizeY) { @@ -1412,36 +1412,36 @@ namespace sol_ImGui g{ col[2].get>().value_or(static_cast(0)) }, b{ col[3].get>().value_or(static_cast(0)) }, a{ col[4].get>().value_or(static_cast(0)) }; - const ImVec4 color{ float(r), float(g), float(b), float(a) }; - return ImGui::ColorButton(desc_id.c_str(), color, static_cast(flags), { sizeX, sizeY }); + const ImVec4 color{ static_cast(r), static_cast(g), static_cast(b), static_cast(a) }; + return ImGui::ColorButton(desc_id.c_str(), color, flags, { sizeX, sizeY }); } - inline void SetColorEditOptions(int flags) { ImGui::SetColorEditOptions(static_cast(flags)); } + inline void SetColorEditOptions(int flags) { ImGui::SetColorEditOptions(flags); } // Widgets: Trees inline bool TreeNode(const std::string& label) { return ImGui::TreeNode(label.c_str()); } - inline bool TreeNode(const std::string& label, const std::string& fmt) { return ImGui::TreeNode(label.c_str(), fmt.c_str()); } + inline bool TreeNode(const std::string& label, const std::string& fmt) { return ImGui::TreeNode(label.c_str(), "%s", fmt.c_str()); } /* TODO: TreeNodeV(...) (2) ==> UNSUPPORTED */ inline bool TreeNodeEx(const std::string& label) { return ImGui::TreeNodeEx(label.c_str()); } - inline bool TreeNodeEx(const std::string& label, int flags) { return ImGui::TreeNodeEx(label.c_str(), static_cast(flags)); } - inline bool TreeNodeEx(const std::string& label, int flags, const std::string& fmt) { return ImGui::TreeNodeEx(label.c_str(), static_cast(flags), fmt.c_str()); } + inline bool TreeNodeEx(const std::string& label, int flags) { return ImGui::TreeNodeEx(label.c_str(), flags); } + inline bool TreeNodeEx(const std::string& label, int flags, const std::string& fmt) { return ImGui::TreeNodeEx(label.c_str(), flags, "%s", fmt.c_str()); } /* TODO: TreeNodeExV(...) (2) ==> UNSUPPORTED */ inline void TreePush(const std::string& str_id) { ImGui::TreePush(str_id.c_str()); } /* TODO: TreePush(const void*) ==> UNSUPPORTED */ inline void TreePop() { ImGui::TreePop(); } inline float GetTreeNodeToLabelSpacing() { return ImGui::GetTreeNodeToLabelSpacing(); } inline bool CollapsingHeader(const std::string& label) { return ImGui::CollapsingHeader(label.c_str()); } - inline bool CollapsingHeader(const std::string& label, int flags) { return ImGui::CollapsingHeader(label.c_str(), static_cast(flags)); } + inline bool CollapsingHeader(const std::string& label, int flags) { return ImGui::CollapsingHeader(label.c_str(), flags); } inline std::tuple CollapsingHeader(const std::string& label, bool open) { bool notCollapsed = ImGui::CollapsingHeader(label.c_str(), &open); return std::make_tuple(open, notCollapsed); } - inline std::tuple CollapsingHeader(const std::string& label, bool open, int flags) { bool notCollapsed = ImGui::CollapsingHeader(label.c_str(), &open, static_cast(flags)); return std::make_tuple(open, notCollapsed); } + inline std::tuple CollapsingHeader(const std::string& label, bool open, int flags) { bool notCollapsed = ImGui::CollapsingHeader(label.c_str(), &open, flags); return std::make_tuple(open, notCollapsed); } inline void SetNextItemOpen(bool is_open) { ImGui::SetNextItemOpen(is_open); } - inline void SetNextItemOpen(bool is_open, int cond) { ImGui::SetNextItemOpen(is_open, static_cast(cond)); } + inline void SetNextItemOpen(bool is_open, int cond) { ImGui::SetNextItemOpen(is_open, cond); } // Widgets: Selectables // TODO: Only one of Selectable variations is possible due to same parameters for Lua inline bool Selectable(const std::string& label) { return ImGui::Selectable(label.c_str()); } inline bool Selectable(const std::string& label, bool selected) { ImGui::Selectable(label.c_str(), &selected); return selected; } - inline bool Selectable(const std::string& label, bool selected, int flags) { ImGui::Selectable(label.c_str(), &selected, static_cast(flags)); return selected; } - inline bool Selectable(const std::string& label, bool selected, int flags, float sizeX, float sizeY) { ImGui::Selectable(label.c_str(), &selected, static_cast(flags), { sizeX, sizeY }); return selected; } + inline bool Selectable(const std::string& label, bool selected, int flags) { ImGui::Selectable(label.c_str(), &selected, flags); return selected; } + inline bool Selectable(const std::string& label, bool selected, int flags, float sizeX, float sizeY) { ImGui::Selectable(label.c_str(), &selected, flags, { sizeX, sizeY }); return selected; } // Widgets: List Boxes inline std::tuple ListBox(const std::string& label, int current_item, const sol::table& items, int items_count) @@ -1450,12 +1450,12 @@ namespace sol_ImGui for (int i{ 1 }; i <= items_count; i++) { const auto& stringItem = items.get>(i); - strings.push_back(stringItem.value_or("Missing")); + strings.emplace_back(stringItem.value_or("Missing")); } TiltedPhoques::Vector cstrings; for (auto& string : strings) - cstrings.push_back(string.c_str()); + cstrings.emplace_back(string.c_str()); bool clicked = ImGui::ListBox(label.c_str(), ¤t_item, cstrings.data(), items_count); return std::make_tuple(current_item, clicked); @@ -1466,12 +1466,12 @@ namespace sol_ImGui for (int i{ 1 }; i <= items_count; i++) { const auto& stringItem = items.get>(i); - strings.push_back(stringItem.value_or("Missing")); + strings.emplace_back(stringItem.value_or("Missing")); } TiltedPhoques::Vector cstrings; for (auto& string : strings) - cstrings.push_back(string.c_str()); + cstrings.emplace_back(string.c_str()); bool clicked = ImGui::ListBox(label.c_str(), ¤t_item, cstrings.data(), items_count, height_in_items); return std::make_tuple(current_item, clicked); @@ -1506,47 +1506,47 @@ namespace sol_ImGui // Tooltips inline void BeginTooltip() { ImGui::BeginTooltip(); } inline void EndTooltip() { ImGui::EndTooltip(); } - inline void SetTooltip(const std::string& fmt) { ImGui::SetTooltip(fmt.c_str()); } + inline void SetTooltip(const std::string& fmt) { ImGui::SetTooltip("%s", fmt.c_str()); } inline void SetTooltipV() { /* TODO: SetTooltipV(...) ==> UNSUPPORTED */ } // Popups, Modals inline bool BeginPopup(const std::string& str_id) { return ImGui::BeginPopup(str_id.c_str()); } - inline bool BeginPopup(const std::string& str_id, int flags) { return ImGui::BeginPopup(str_id.c_str(), static_cast(flags)); } + inline bool BeginPopup(const std::string& str_id, int flags) { return ImGui::BeginPopup(str_id.c_str(), flags); } inline bool BeginPopupModal(const std::string& name) { return ImGui::BeginPopupModal(name.c_str()); } - inline bool BeginPopupModal(const std::string& name, int flags) { return ImGui::BeginPopupModal(name.c_str(), NULL, static_cast(flags)); } + inline bool BeginPopupModal(const std::string& name, int flags) { return ImGui::BeginPopupModal(name.c_str(), nullptr, flags); } inline bool BeginPopupModal(const std::string& name, bool open) { return ImGui::BeginPopupModal(name.c_str(), &open); } - inline bool BeginPopupModal(const std::string& name, bool open, int flags) { return ImGui::BeginPopupModal(name.c_str(), &open, static_cast(flags)); } + inline bool BeginPopupModal(const std::string& name, bool open, int flags) { return ImGui::BeginPopupModal(name.c_str(), &open, flags); } inline void EndPopup() { ImGui::EndPopup(); } inline void OpenPopup(const std::string& str_id) { ImGui::OpenPopup(str_id.c_str()); } - inline void OpenPopup(const std::string& str_id, int popup_flags) { ImGui::OpenPopup(str_id.c_str(), static_cast(popup_flags)); } + inline void OpenPopup(const std::string& str_id, int popup_flags) { ImGui::OpenPopup(str_id.c_str(), popup_flags); } inline void CloseCurrentPopup() { ImGui::CloseCurrentPopup(); } inline bool BeginPopupContextItem() { return ImGui::BeginPopupContextItem(); } inline bool BeginPopupContextItem(const std::string& str_id) { return ImGui::BeginPopupContextItem(str_id.c_str()); } - inline bool BeginPopupContextItem(const std::string& str_id, int popup_flags) { return ImGui::BeginPopupContextItem(str_id.c_str(), static_cast(popup_flags)); } + inline bool BeginPopupContextItem(const std::string& str_id, int popup_flags) { return ImGui::BeginPopupContextItem(str_id.c_str(), popup_flags); } inline bool BeginPopupContextWindow() { return ImGui::BeginPopupContextWindow(); } inline bool BeginPopupContextWindow(const std::string& str_id) { return ImGui::BeginPopupContextWindow(str_id.c_str()); } - inline bool BeginPopupContextWindow(const std::string& str_id, int popup_flags) { return ImGui::BeginPopupContextWindow(str_id.c_str(), static_cast(popup_flags)); } + inline bool BeginPopupContextWindow(const std::string& str_id, int popup_flags) { return ImGui::BeginPopupContextWindow(str_id.c_str(), popup_flags); } inline bool BeginPopupContextVoid() { return ImGui::BeginPopupContextVoid(); } inline bool BeginPopupContextVoid(const std::string& str_id) { return ImGui::BeginPopupContextVoid(str_id.c_str()); } - inline bool BeginPopupContextVoid(const std::string& str_id, int popup_flags) { return ImGui::BeginPopupContextVoid(str_id.c_str(), static_cast(popup_flags)); } + inline bool BeginPopupContextVoid(const std::string& str_id, int popup_flags) { return ImGui::BeginPopupContextVoid(str_id.c_str(), popup_flags); } inline bool IsPopupOpen(const std::string& str_id) { return ImGui::IsPopupOpen(str_id.c_str()); } inline bool IsPopupOpen(const std::string& str_id, int popup_flags) { return ImGui::IsPopupOpen(str_id.c_str(), popup_flags); } //Tables inline bool BeginTable(const std::string& str_id, int columns) { return ImGui::BeginTable(str_id.c_str(), columns); } - inline bool BeginTable(const std::string& str_id, int columns, int flags) { return ImGui::BeginTable(str_id.c_str(), columns, static_cast(flags)); } - inline bool BeginTable(const std::string& str_id, int columns, int flags, float outer_sizeX, float outer_sizeY) { return ImGui::BeginTable(str_id.c_str(), columns, static_cast(flags), { outer_sizeX, outer_sizeY }); } - inline bool BeginTable(const std::string& str_id, int columns, int flags, float outer_sizeX, float outer_sizeY, float inner_width) { return ImGui::BeginTable(str_id.c_str(), columns, static_cast(flags), { outer_sizeX, outer_sizeY }, inner_width); } + inline bool BeginTable(const std::string& str_id, int columns, int flags) { return ImGui::BeginTable(str_id.c_str(), columns, flags); } + inline bool BeginTable(const std::string& str_id, int columns, int flags, float outer_sizeX, float outer_sizeY) { return ImGui::BeginTable(str_id.c_str(), columns, flags, { outer_sizeX, outer_sizeY }); } + inline bool BeginTable(const std::string& str_id, int columns, int flags, float outer_sizeX, float outer_sizeY, float inner_width) { return ImGui::BeginTable(str_id.c_str(), columns, flags, { outer_sizeX, outer_sizeY }, inner_width); } inline void EndTable() { ImGui::EndTable(); } inline void TableNextRow() { ImGui::TableNextRow(); } - inline void TableNextRow(int flags) { ImGui::TableNextRow(static_cast(flags)); } - inline void TableNextRow(int flags, float min_row_height) { ImGui::TableNextRow(static_cast(flags), min_row_height); } + inline void TableNextRow(int flags) { ImGui::TableNextRow(flags); } + inline void TableNextRow(int flags, float min_row_height) { ImGui::TableNextRow(flags, min_row_height); } inline bool TableNextColumn() { return ImGui::TableNextColumn(); } inline bool TableSetColumnIndex(int column_n) { return ImGui::TableSetColumnIndex(column_n); } inline void TableSetupColumn(const std::string& label) { ImGui::TableSetupColumn(label.c_str()); } - inline void TableSetupColumn(const std::string& label, int flags) { ImGui::TableSetupColumn(label.c_str(), static_cast(flags)); } - inline void TableSetupColumn(const std::string& label, int flags, float init_width_or_weight) { ImGui::TableSetupColumn(label.c_str(), static_cast(flags), init_width_or_weight); } - inline void TableSetupColumn(const std::string& label, int flags, float init_width_or_weight, int user_id) { ImGui::TableSetupColumn(label.c_str(), static_cast(flags), init_width_or_weight, ImU32(user_id)); } + inline void TableSetupColumn(const std::string& label, int flags) { ImGui::TableSetupColumn(label.c_str(), ImGuiTableColumnFlags(flags)); } + inline void TableSetupColumn(const std::string& label, int flags, float init_width_or_weight) { ImGui::TableSetupColumn(label.c_str(), ImGuiTableColumnFlags(flags), init_width_or_weight); } + inline void TableSetupColumn(const std::string& label, int flags, float init_width_or_weight, int user_id) { ImGui::TableSetupColumn(label.c_str(), ImGuiTableColumnFlags(flags), init_width_or_weight, static_cast(user_id)); } inline void TableSetupScrollFreeze(int cols, int rows) { ImGui::TableSetupScrollFreeze(cols, rows); } inline void TableHeadersRow() { ImGui::TableHeadersRow(); } inline void TableHeader(const std::string& label) { ImGui::TableHeader(label.c_str()); } @@ -1558,10 +1558,10 @@ namespace sol_ImGui inline std::string TableGetColumnName(int column_n) { return std::string(ImGui::TableGetColumnName(column_n)); } inline ImGuiTableColumnFlags TableGetColumnFlags() { return ImGui::TableGetColumnFlags(); } inline ImGuiTableColumnFlags TableGetColumnFlags(int column_n) { return ImGui::TableGetColumnFlags(column_n); } - inline void TableSetBgColor(int target, int color) { ImGui::TableSetBgColor(static_cast(target), ImU32(color)); } - inline void TableSetBgColor(int target, float colR, float colG, float colB, float colA) { ImGui::TableSetBgColor(static_cast(target), ImGui::ColorConvertFloat4ToU32({ colR, colG, colB, colA })); } - inline void TableSetBgColor(int target, int color, int column_n) { ImGui::TableSetBgColor(static_cast(target), ImU32(color), column_n); } - inline void TableSetBgColor(int target, float colR, float colG, float colB, float colA, int column_n) { ImGui::TableSetBgColor(static_cast(target), ImGui::ColorConvertFloat4ToU32({ colR, colG, colB, colA }), column_n); } + inline void TableSetBgColor(int target, int color) { ImGui::TableSetBgColor(target, static_cast(color)); } + inline void TableSetBgColor(int target, float colR, float colG, float colB, float colA) { ImGui::TableSetBgColor(target, ImGui::ColorConvertFloat4ToU32({ colR, colG, colB, colA })); } + inline void TableSetBgColor(int target, int color, int column_n) { ImGui::TableSetBgColor(target, static_cast(color), column_n); } + inline void TableSetBgColor(int target, float colR, float colG, float colB, float colA, int column_n) { ImGui::TableSetBgColor(target, ImGui::ColorConvertFloat4ToU32({ colR, colG, colB, colA }), column_n); } // Columns inline void Columns() { ImGui::Columns(); } @@ -1580,12 +1580,12 @@ namespace sol_ImGui // Tab Bars, Tabs inline bool BeginTabBar(const std::string& str_id) { return ImGui::BeginTabBar(str_id.c_str()); } - inline bool BeginTabBar(const std::string& str_id, int flags) { return ImGui::BeginTabBar(str_id.c_str(), static_cast(flags)); } + inline bool BeginTabBar(const std::string& str_id, int flags) { return ImGui::BeginTabBar(str_id.c_str(), flags); } inline void EndTabBar() { ImGui::EndTabBar(); } inline bool BeginTabItem(const std::string& label) { return ImGui::BeginTabItem(label.c_str()); } - inline bool BeginTabItem(const std::string& label, int flags) { return ImGui::BeginTabItem(label.c_str(), NULL, static_cast(flags)); } + inline bool BeginTabItem(const std::string& label, int flags) { return ImGui::BeginTabItem(label.c_str(), nullptr, flags); } inline std::tuple BeginTabItem(const std::string& label, bool open) { bool selected = ImGui::BeginTabItem(label.c_str(), &open); return std::make_tuple(open, selected); } - inline std::tuple BeginTabItem(const std::string& label, bool open, int flags) { bool selected = ImGui::BeginTabItem(label.c_str(), &open, static_cast(flags)); return std::make_tuple(open, selected); } + inline std::tuple BeginTabItem(const std::string& label, bool open, int flags) { bool selected = ImGui::BeginTabItem(label.c_str(), &open, flags); return std::make_tuple(open, selected); } inline void EndTabItem() { ImGui::EndTabItem(); } inline void SetTabItemClosed(const std::string& tab_or_docked_window_label) { ImGui::SetTabItemClosed(tab_or_docked_window_label.c_str()); } @@ -1608,11 +1608,11 @@ namespace sol_ImGui // Item/Widgets Utilities inline bool IsItemHovered() { return ImGui::IsItemHovered(); } - inline bool IsItemHovered(int flags) { return ImGui::IsItemHovered(static_cast(flags)); } + inline bool IsItemHovered(int flags) { return ImGui::IsItemHovered(flags); } inline bool IsItemActive() { return ImGui::IsItemActive(); } inline bool IsItemFocused() { return ImGui::IsItemFocused(); } inline bool IsItemClicked() { return ImGui::IsItemClicked(); } - inline bool IsItemClicked(int mouse_button) { return ImGui::IsItemClicked(static_cast(mouse_button)); } + inline bool IsItemClicked(int mouse_button) { return ImGui::IsItemClicked(mouse_button); } inline bool IsItemVisible() { return ImGui::IsItemVisible(); } inline bool IsItemEdited() { return ImGui::IsItemEdited(); } inline bool IsItemActivated() { return ImGui::IsItemActivated(); } @@ -1635,10 +1635,10 @@ namespace sol_ImGui inline ImDrawList* GetBackgroundDrawList() { return ImGui::GetBackgroundDrawList(); } inline ImDrawList* GetForegroundDrawList() { return ImGui::GetForegroundDrawList(); } /* TODO: GetDrawListSharedData() ==> UNSUPPORTED */ - inline std::string GetStyleColorName(int idx) { return std::string(ImGui::GetStyleColorName(static_cast(idx))); } + inline std::string GetStyleColorName(int idx) { return std::string(ImGui::GetStyleColorName(idx)); } /* TODO: SetStateStorage(), GetStateStorage(), CalcListClipping() ==> UNSUPPORTED */ inline bool BeginChildFrame(unsigned int id, float sizeX, float sizeY) { return ImGui::BeginChildFrame(id, { sizeX, sizeY }); } - inline bool BeginChildFrame(unsigned int id, float sizeX, float sizeY, int flags) { return ImGui::BeginChildFrame(id, { sizeX, sizeY }, static_cast(flags)); } + inline bool BeginChildFrame(unsigned int id, float sizeX, float sizeY, int flags) { return ImGui::BeginChildFrame(id, { sizeX, sizeY }, flags); } inline void EndChildFrame() { return ImGui::EndChildFrame(); } inline ImGuiStyle& GetStyle() { @@ -1667,7 +1667,7 @@ namespace sol_ImGui b{ rgba[3].get>().value_or(static_cast(0)) }, a{ rgba[4].get>().value_or(static_cast(0)) }; - return ImGui::ColorConvertFloat4ToU32({ float(r), float(g), float(b), float(a) }); + return ImGui::ColorConvertFloat4ToU32({ static_cast(r), static_cast(g), static_cast(b), static_cast(a) }); } inline std::tuple ColorConvertRGBtoHSV(float r, float g, float b) { @@ -1701,57 +1701,57 @@ namespace sol_ImGui // Drawing APIs // Primitives - inline void ImDrawListAddLine(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, int col) { drawlist->AddLine({ p1X, p1Y }, { p2X, p2Y }, ImU32(col)); } - inline void ImDrawListAddLine(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, int col, float thickness) { drawlist->AddLine({ p1X, p1Y }, { p2X, p2Y }, ImU32(col), thickness); } - inline void ImDrawListAddRect(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col) { drawlist->AddRect({ p_minX, p_minY }, { p_maxX, p_maxY }, ImU32(col)); } - inline void ImDrawListAddRect(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col, float rounding) { drawlist->AddRect({ p_minX, p_minY }, { p_maxX, p_maxY }, ImU32(col), rounding); } - inline void ImDrawListAddRect(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col, float rounding, int flags) { drawlist->AddRect({ p_minX, p_minY }, { p_maxX, p_maxY }, ImU32(col), rounding, static_cast(flags)); } - inline void ImDrawListAddRect(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col, float rounding, int flags, float thickness) { drawlist->AddRect({ p_minX, p_minY }, { p_maxX, p_maxY }, ImU32(col), rounding, static_cast(flags), thickness); } - inline void ImDrawListAddRectFilled(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col) { drawlist->AddRectFilled({ p_minX, p_minY }, { p_maxX, p_maxY }, ImU32(col)); } - inline void ImDrawListAddRectFilled(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col, float rounding) { drawlist->AddRectFilled({ p_minX, p_minY }, { p_maxX, p_maxY }, ImU32(col), rounding); } - inline void ImDrawListAddRectFilled(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col, float rounding, int flags) { drawlist->AddRectFilled({ p_minX, p_minY }, { p_maxX, p_maxY }, ImU32(col), rounding, static_cast(flags)); } - inline void ImDrawListAddRectFilledMultiColor(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col_upr_left, int col_upr_right, int col_bot_right, int col_bot_left) { drawlist->AddRectFilledMultiColor({ p_minX, p_minY }, { p_maxX, p_maxY }, ImU32(col_upr_left), ImU32(col_upr_right), ImU32(col_bot_right), ImU32(col_bot_left)); } - inline void ImDrawListAddQuad(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, float p4X, float p4Y, int col) { drawlist->AddQuad({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, { p4X, p4Y }, ImU32(col)); } - inline void ImDrawListAddQuad(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, float p4X, float p4Y, int col, float thickness) { drawlist->AddQuad({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, { p4X, p4Y }, ImU32(col), thickness); } - inline void ImDrawListAddQuadFilled(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, float p4X, float p4Y, int col) { drawlist->AddQuadFilled({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, { p4X, p4Y }, ImU32(col)); } - inline void ImDrawListAddTriangle(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, int col) { drawlist->AddTriangle({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, ImU32(col)); } - inline void ImDrawListAddTriangle(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, int col, float thickness) { drawlist->AddTriangle({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, ImU32(col), thickness); } - inline void ImDrawListAddTriangleFilled(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, int col) { drawlist->AddTriangleFilled({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, ImU32(col)); } - inline void ImDrawListAddCircle(ImDrawList* drawlist, float centerX, float centerY, float radius, int col) { drawlist->AddCircle({ centerX, centerY }, radius, ImU32(col)); } - inline void ImDrawListAddCircle(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments) { drawlist->AddCircle({ centerX, centerY }, radius, ImU32(col), num_segments); } - inline void ImDrawListAddCircle(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments, float thickness) { drawlist->AddCircle({ centerX, centerY }, radius, ImU32(col), num_segments, thickness); } - inline void ImDrawListAddCircleFilled(ImDrawList* drawlist, float centerX, float centerY, float radius, int col) { drawlist->AddCircleFilled({ centerX, centerY }, radius, ImU32(col)); } - inline void ImDrawListAddCircleFilled(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments) { drawlist->AddCircleFilled({ centerX, centerY }, radius, ImU32(col), num_segments); } - inline void ImDrawListAddNgon(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments) { drawlist->AddNgon({ centerX, centerY }, radius, ImU32(col), num_segments); } - inline void ImDrawListAddNgon(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments, float thickness) { drawlist->AddNgon({ centerX, centerY }, radius, ImU32(col), num_segments, thickness); } - inline void ImDrawListAddNgonFilled(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments) { drawlist->AddNgonFilled({ centerX, centerY }, radius, ImU32(col), num_segments); } - inline void ImDrawListAddText(ImDrawList* drawlist, float posX, float posY, int col, const std::string& text_begin) { drawlist->AddText({ posX, posY }, ImU32(col), text_begin.c_str()); } - inline void ImDrawListAddText(ImDrawList* drawlist, float font_size, float posX, float posY, int col, const std::string& text_begin) { drawlist->AddText(ImGui::GetFont(), font_size, { posX, posY }, ImU32(col), text_begin.c_str()); } - inline void ImDrawListAddText(ImDrawList* drawlist, float font_size, float posX, float posY, int col, const std::string& text_begin, float wrap_width) { drawlist->AddText(ImGui::GetFont(), font_size, { posX, posY }, ImU32(col), text_begin.c_str(), NULL, wrap_width); } + inline void ImDrawListAddLine(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, int col) { drawlist->AddLine({ p1X, p1Y }, { p2X, p2Y }, static_cast(col)); } + inline void ImDrawListAddLine(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, int col, float thickness) { drawlist->AddLine({ p1X, p1Y }, { p2X, p2Y }, static_cast(col), thickness); } + inline void ImDrawListAddRect(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col) { drawlist->AddRect({ p_minX, p_minY }, { p_maxX, p_maxY }, static_cast(col)); } + inline void ImDrawListAddRect(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col, float rounding) { drawlist->AddRect({ p_minX, p_minY }, { p_maxX, p_maxY }, static_cast(col), rounding); } + inline void ImDrawListAddRect(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col, float rounding, int flags) { drawlist->AddRect({ p_minX, p_minY }, { p_maxX, p_maxY }, static_cast(col), rounding, static_cast(flags)); } + inline void ImDrawListAddRect(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col, float rounding, int flags, float thickness) { drawlist->AddRect({ p_minX, p_minY }, { p_maxX, p_maxY }, static_cast(col), rounding, static_cast(flags), thickness); } + inline void ImDrawListAddRectFilled(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col) { drawlist->AddRectFilled({ p_minX, p_minY }, { p_maxX, p_maxY }, static_cast(col)); } + inline void ImDrawListAddRectFilled(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col, float rounding) { drawlist->AddRectFilled({ p_minX, p_minY }, { p_maxX, p_maxY }, static_cast(col), rounding); } + inline void ImDrawListAddRectFilled(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col, float rounding, int flags) { drawlist->AddRectFilled({ p_minX, p_minY }, { p_maxX, p_maxY }, static_cast(col), rounding, static_cast(flags)); } + inline void ImDrawListAddRectFilledMultiColor(ImDrawList* drawlist, float p_minX, float p_minY, float p_maxX, float p_maxY, int col_upr_left, int col_upr_right, int col_bot_right, int col_bot_left) { drawlist->AddRectFilledMultiColor({ p_minX, p_minY }, { p_maxX, p_maxY }, static_cast(col_upr_left), static_cast(col_upr_right), static_cast(col_bot_right), static_cast(col_bot_left)); } + inline void ImDrawListAddQuad(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, float p4X, float p4Y, int col) { drawlist->AddQuad({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, { p4X, p4Y }, static_cast(col)); } + inline void ImDrawListAddQuad(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, float p4X, float p4Y, int col, float thickness) { drawlist->AddQuad({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, { p4X, p4Y }, static_cast(col), thickness); } + inline void ImDrawListAddQuadFilled(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, float p4X, float p4Y, int col) { drawlist->AddQuadFilled({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, { p4X, p4Y }, static_cast(col)); } + inline void ImDrawListAddTriangle(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, int col) { drawlist->AddTriangle({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, static_cast(col)); } + inline void ImDrawListAddTriangle(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, int col, float thickness) { drawlist->AddTriangle({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, static_cast(col), thickness); } + inline void ImDrawListAddTriangleFilled(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, int col) { drawlist->AddTriangleFilled({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, static_cast(col)); } + inline void ImDrawListAddCircle(ImDrawList* drawlist, float centerX, float centerY, float radius, int col) { drawlist->AddCircle({ centerX, centerY }, radius, static_cast(col)); } + inline void ImDrawListAddCircle(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments) { drawlist->AddCircle({ centerX, centerY }, radius, static_cast(col), num_segments); } + inline void ImDrawListAddCircle(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments, float thickness) { drawlist->AddCircle({ centerX, centerY }, radius, static_cast(col), num_segments, thickness); } + inline void ImDrawListAddCircleFilled(ImDrawList* drawlist, float centerX, float centerY, float radius, int col) { drawlist->AddCircleFilled({ centerX, centerY }, radius, static_cast(col)); } + inline void ImDrawListAddCircleFilled(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments) { drawlist->AddCircleFilled({ centerX, centerY }, radius, static_cast(col), num_segments); } + inline void ImDrawListAddNgon(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments) { drawlist->AddNgon({ centerX, centerY }, radius, static_cast(col), num_segments); } + inline void ImDrawListAddNgon(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments, float thickness) { drawlist->AddNgon({ centerX, centerY }, radius, static_cast(col), num_segments, thickness); } + inline void ImDrawListAddNgonFilled(ImDrawList* drawlist, float centerX, float centerY, float radius, int col, int num_segments) { drawlist->AddNgonFilled({ centerX, centerY }, radius, static_cast(col), num_segments); } + inline void ImDrawListAddText(ImDrawList* drawlist, float posX, float posY, int col, const std::string& text_begin) { drawlist->AddText({ posX, posY }, static_cast(col), text_begin.c_str()); } + inline void ImDrawListAddText(ImDrawList* drawlist, float font_size, float posX, float posY, int col, const std::string& text_begin) { drawlist->AddText(ImGui::GetFont(), font_size, { posX, posY }, static_cast(col), text_begin.c_str()); } + inline void ImDrawListAddText(ImDrawList* drawlist, float font_size, float posX, float posY, int col, const std::string& text_begin, float wrap_width) { drawlist->AddText(ImGui::GetFont(), font_size, { posX, posY }, static_cast(col), text_begin.c_str(), nullptr, wrap_width); } // TODO // inline void ImDrawListAddText(ImDrawList* drawlist, float font_size, float posX, float posY, int col, const std::string& text_begin, float wrap_width, sol::table float cpu_fine_clip_rect) { drawlist->AddText(ImGui::GetFont(), font_size, { posX, posY }, ImU32(col), text_begin.c_str(), NULL, wrap_width, cpu_fine_clip_rect); } // inline void ImDrawListAddPolyline(ImDrawList* drawlist, sol::table points, int num_points, int col, int flags, float thickness) { drawlist->AddPolyline(points, num_points, ImU32(col), static_cast(flags), thickness); } // inline void ImDrawListAddConvexPolyFilled(ImDrawList* drawlist, sol::table points, int num_points, int col) { drawlist->AddConvexPolyFilled(points, num_points, ImU32(col)); } - inline void ImDrawListAddBezierCubic(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, float p4X, float p4Y, int col, float thickness) { drawlist->AddBezierCubic({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, { p4X, p4Y }, ImU32(col), thickness); } - inline void ImDrawListAddBezierCubic(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, float p4X, float p4Y, int col, float thickness, int num_segments) { drawlist->AddBezierCubic({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, { p4X, p4Y }, ImU32(col), thickness, num_segments); } - inline void ImDrawListAddBezierQuadratic(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, int col, float thickness) { drawlist->AddBezierQuadratic({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, ImU32(col), thickness); } - inline void ImDrawListAddBezierQuadratic(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, int col, float thickness, int num_segments) { drawlist->AddBezierQuadratic({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, ImU32(col), thickness, num_segments); } + inline void ImDrawListAddBezierCubic(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, float p4X, float p4Y, int col, float thickness) { drawlist->AddBezierCubic({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, { p4X, p4Y }, static_cast(col), thickness); } + inline void ImDrawListAddBezierCubic(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, float p4X, float p4Y, int col, float thickness, int num_segments) { drawlist->AddBezierCubic({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, { p4X, p4Y }, static_cast(col), thickness, num_segments); } + inline void ImDrawListAddBezierQuadratic(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, int col, float thickness) { drawlist->AddBezierQuadratic({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, static_cast(col), thickness); } + inline void ImDrawListAddBezierQuadratic(ImDrawList* drawlist, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y, int col, float thickness, int num_segments) { drawlist->AddBezierQuadratic({ p1X, p1Y }, { p2X, p2Y }, { p3X, p3Y }, static_cast(col), thickness, num_segments); } - inline void InitUserType(sol::state& lua) + inline void InitUserType(sol::table luaGlobals) { - lua.new_usertype("ImVec2", sol::constructors(), + luaGlobals.new_usertype("ImVec2", sol::constructors(), "x" , &ImVec2::x, "y" , &ImVec2::y ); - lua.new_usertype("ImVec4", sol::constructors(), + luaGlobals.new_usertype("ImVec4", sol::constructors(), "x" , &ImVec4::x, "y" , &ImVec4::y, "z" , &ImVec4::z, "w" , &ImVec4::w ); - lua.new_usertype("ImGuiStyle", + luaGlobals.new_usertype("ImGuiStyle", "Alpha" , &ImGuiStyle::Alpha, "DisabledAlpha" , &ImGuiStyle::DisabledAlpha, "WindowPadding" , &ImGuiStyle::WindowPadding, @@ -1796,10 +1796,10 @@ namespace sol_ImGui ); } - inline void InitEnums(sol::state& lua) + inline void InitEnums(sol::table luaGlobals) { #pragma region Window Flags - lua.new_enum("ImGuiWindowFlags", + luaGlobals.new_enum("ImGuiWindowFlags", "None" , ImGuiWindowFlags_None, "NoTitleBar" , ImGuiWindowFlags_NoTitleBar, "NoResize" , ImGuiWindowFlags_NoResize, @@ -1835,7 +1835,7 @@ namespace sol_ImGui #pragma endregion Window Flags #pragma region Focused Flags - lua.new_enum("ImGuiFocusedFlags", + luaGlobals.new_enum("ImGuiFocusedFlags", "None" , ImGuiFocusedFlags_None, "ChildWindows" , ImGuiFocusedFlags_ChildWindows, "RootWindow" , ImGuiFocusedFlags_RootWindow, @@ -1845,7 +1845,7 @@ namespace sol_ImGui #pragma endregion Focused Flags #pragma region Hovered Flags - lua.new_enum("ImGuiHoveredFlags", + luaGlobals.new_enum("ImGuiHoveredFlags", "None" , ImGuiHoveredFlags_None, "ChildWindows" , ImGuiHoveredFlags_ChildWindows, "RootWindow" , ImGuiHoveredFlags_RootWindow, @@ -1860,7 +1860,7 @@ namespace sol_ImGui #pragma endregion Hovered Flags #pragma region Cond - lua.new_enum("ImGuiCond", + luaGlobals.new_enum("ImGuiCond", "None" , ImGuiCond_None, "Always" , ImGuiCond_Always, "Once" , ImGuiCond_Once, @@ -1870,7 +1870,7 @@ namespace sol_ImGui #pragma endregion Cond #pragma region Col - lua.new_enum("ImGuiCol", + luaGlobals.new_enum("ImGuiCol", "Text" , ImGuiCol_Text, "TextDisabled" , ImGuiCol_TextDisabled, "WindowBg" , ImGuiCol_WindowBg, @@ -1929,7 +1929,7 @@ namespace sol_ImGui #pragma endregion Col #pragma region Style - lua.new_enum("ImGuiStyleVar", + luaGlobals.new_enum("ImGuiStyleVar", "Alpha" , ImGuiStyleVar_Alpha, "DisabledAlpha" , ImGuiStyleVar_DisabledAlpha, "WindowPadding" , ImGuiStyleVar_WindowPadding, @@ -1960,7 +1960,7 @@ namespace sol_ImGui #pragma endregion Style #pragma region Dir - lua.new_enum("ImGuiDir", + luaGlobals.new_enum("ImGuiDir", "None" , ImGuiDir_None, "Left" , ImGuiDir_Left, "Right" , ImGuiDir_Right, @@ -1971,7 +1971,7 @@ namespace sol_ImGui #pragma endregion Dir #pragma region Combo Flags - lua.new_enum("ImGuiComboFlags", + luaGlobals.new_enum("ImGuiComboFlags", "None" , ImGuiComboFlags_None, "PopupAlignLeft" , ImGuiComboFlags_PopupAlignLeft, "HeightSmall" , ImGuiComboFlags_HeightSmall, @@ -1985,7 +1985,7 @@ namespace sol_ImGui #pragma endregion Combo Flags #pragma region InputText Flags - lua.new_enum("ImGuiInputTextFlags", + luaGlobals.new_enum("ImGuiInputTextFlags", "None" , ImGuiInputTextFlags_None, "CharsDecimal" , ImGuiInputTextFlags_CharsDecimal, "CharsHexadecimal" , ImGuiInputTextFlags_CharsHexadecimal, @@ -2011,7 +2011,7 @@ namespace sol_ImGui #pragma endregion InputText Flags #pragma region Slider Flags - lua.new_enum("ImGuiSliderFlags", + luaGlobals.new_enum("ImGuiSliderFlags", "None" , ImGuiSliderFlags_None, "ClampOnInput" , ImGuiSliderFlags_ClampOnInput, "Logarithmic" , ImGuiSliderFlags_Logarithmic, @@ -2021,7 +2021,7 @@ namespace sol_ImGui #pragma endregion Slider Flags #pragma region ColorEdit Flags - lua.new_enum("ImGuiColorEditFlags", + luaGlobals.new_enum("ImGuiColorEditFlags", "None" , ImGuiColorEditFlags_None, "NoAlpha" , ImGuiColorEditFlags_NoAlpha, "NoPicker" , ImGuiColorEditFlags_NoPicker, @@ -2058,7 +2058,7 @@ namespace sol_ImGui #pragma endregion ColorEdit Flags #pragma region TreeNode Flags - lua.new_enum("ImGuiTreeNodeFlags", + luaGlobals.new_enum("ImGuiTreeNodeFlags", "None" , ImGuiTreeNodeFlags_None, "Selected" , ImGuiTreeNodeFlags_Selected, "Framed" , ImGuiTreeNodeFlags_Framed, @@ -2079,7 +2079,7 @@ namespace sol_ImGui #pragma endregion TreeNode Flags #pragma region Selectable Flags - lua.new_enum("ImGuiSelectableFlags", + luaGlobals.new_enum("ImGuiSelectableFlags", "None" , ImGuiSelectableFlags_None, "DontClosePopups" , ImGuiSelectableFlags_DontClosePopups, "SpanAllColumns" , ImGuiSelectableFlags_SpanAllColumns, @@ -2090,7 +2090,7 @@ namespace sol_ImGui #pragma endregion Selectable Flags #pragma region Popup Flags - lua.new_enum("ImGuiPopupFlags", + luaGlobals.new_enum("ImGuiPopupFlags", "None" , ImGuiPopupFlags_None, "MouseButtonLeft" , ImGuiPopupFlags_MouseButtonLeft, "MouseButtonRight" , ImGuiPopupFlags_MouseButtonRight, @@ -2106,7 +2106,7 @@ namespace sol_ImGui #pragma endregion Popup Flags #pragma region Table Flags - lua.new_enum("ImGuiTableFlags", + luaGlobals.new_enum("ImGuiTableFlags", // Features "None" , ImGuiTableFlags_None, "Resizable" , ImGuiTableFlags_Resizable, @@ -2156,7 +2156,7 @@ namespace sol_ImGui #pragma endregion Table Flags #pragma region TableColumn Flags - lua.new_enum("ImGuiTableColumnFlags", + luaGlobals.new_enum("ImGuiTableColumnFlags", // Input configuration flags "None" , ImGuiTableColumnFlags_None, "Disabled" , ImGuiTableColumnFlags_Disabled, @@ -2191,14 +2191,14 @@ namespace sol_ImGui #pragma endregion TableColumn Flags #pragma region TableRow Flags - lua.new_enum("ImGuiTableRowFlags", + luaGlobals.new_enum("ImGuiTableRowFlags", "None" , ImGuiTableRowFlags_None, "Headers" , ImGuiTableRowFlags_Headers ); #pragma endregion TableRow Flags #pragma region TableBg Target - lua.new_enum("ImGuiTableBgTarget", + luaGlobals.new_enum("ImGuiTableBgTarget", "None" , ImGuiTableBgTarget_None, "RowBg0" , ImGuiTableBgTarget_RowBg0, "RowBg1" , ImGuiTableBgTarget_RowBg1, @@ -2207,7 +2207,7 @@ namespace sol_ImGui #pragma endregion TableBg Target #pragma region Draw Flags - lua.new_enum("ImDrawFlags", + luaGlobals.new_enum("ImDrawFlags", "None" , ImDrawFlags_None, "Closed" , ImDrawFlags_Closed, "ImDrawFlags_RoundCornersTopLeft" , ImDrawFlags_RoundCornersTopLeft, @@ -2224,7 +2224,7 @@ namespace sol_ImGui #pragma endregion Draw Flags #pragma region TabBar Flags - lua.new_enum("ImGuiTabBarFlags", + luaGlobals.new_enum("ImGuiTabBarFlags", "None" , ImGuiTabBarFlags_None, "Reorderable" , ImGuiTabBarFlags_Reorderable, "AutoSelectNewTabs" , ImGuiTabBarFlags_AutoSelectNewTabs, @@ -2240,7 +2240,7 @@ namespace sol_ImGui #pragma endregion TabBar Flags #pragma region TabItem Flags - lua.new_enum("ImGuiTabItemFlags", + luaGlobals.new_enum("ImGuiTabItemFlags", "None" , ImGuiTabItemFlags_None, "UnsavedDocument" , ImGuiTabItemFlags_UnsavedDocument, "SetSelected" , ImGuiTabItemFlags_SetSelected, @@ -2254,7 +2254,7 @@ namespace sol_ImGui #pragma endregion TabItem Flags #pragma region MouseButton - lua.new_enum("ImGuiMouseButton", + luaGlobals.new_enum("ImGuiMouseButton", "Left" , ImGuiMouseButton_Left, "Right" , ImGuiMouseButton_Right, "Middle" , ImGuiMouseButton_Middle, @@ -2263,7 +2263,7 @@ namespace sol_ImGui #pragma endregion MouseButton #pragma region ImDrawCorner Flags - lua.new_enum("ImDrawCornerFlags", + luaGlobals.new_enum("ImDrawCornerFlags", "None" , ImDrawCornerFlags_None, "TopLeft" , ImDrawCornerFlags_TopLeft, "TopRight" , ImDrawCornerFlags_TopRight, @@ -2278,12 +2278,12 @@ namespace sol_ImGui #pragma endregion ImDrawCorner Flags } - inline void InitBindings(sol::state& lua) + inline void InitBindings(sol::state& lua, sol::table luaGlobals) { - InitUserType(lua); - InitEnums(lua); + InitUserType(luaGlobals); + InitEnums(luaGlobals); - sol::table ImGui = lua.create_named_table("ImGui"); + sol::table ImGui(lua, sol::create); #pragma region Windows ImGui.set_function("Begin" , sol::overload( @@ -3119,5 +3119,7 @@ namespace sol_ImGui sol::resolve(ImDrawListAddBezierQuadratic) )); #pragma endregion Drawing APIs + + luaGlobals["ImGui"] = ImGui; } } diff --git a/src/stdafx.h b/src/stdafx.h index c5ef0758..04e8680b 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -4,63 +4,75 @@ #include #include #include +#include +#include #include -#include -#include #include #include +#include +#include +#include #include +#include +#include #include #include -#include -#include -#include #include #include #include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include #include -#include +#include #include -#include -#include #include +#include +#include +#include +#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "Paths.h" -#include "VKBindings.h" -#include "Options.h" #include "CETVersion.h" #include "common/Logging.h" -#include "reverse/Addresses.h" \ No newline at end of file +#include "Options.h" +#include "Paths.h" +#include "reverse/Addresses.h" +#include "VKBindings.h" + +template<> +struct std::hash +{ + std::size_t operator()(RED4ext::CName aKey) const noexcept + { + return static_cast(aKey.hash); + } +}; diff --git a/src/window/window.cpp b/src/window/window.cpp index a67dfeb5..9e6eb77f 100644 --- a/src/window/window.cpp +++ b/src/window/window.cpp @@ -1,10 +1,8 @@ #include -#include "CET.h" #include "window.h" -#include -#include +#include using namespace std::chrono_literals; @@ -33,7 +31,7 @@ LRESULT APIENTRY Window::WndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM { if (auMsg == WM_WINDOWPOSCHANGED) { - auto* wp = reinterpret_cast(alParam); + const auto* wp = reinterpret_cast(alParam); s_pWindow->m_wndPos = {wp->x, wp->y}; s_pWindow->m_wndSize = {wp->cx, wp->cy}; @@ -44,35 +42,28 @@ LRESULT APIENTRY Window::WndProc(HWND ahWnd, UINT auMsg, WPARAM awParam, LPARAM } { - const auto res = s_pWindow->m_pBindings->OnWndProc(ahWnd, auMsg, awParam, alParam); - if (res) + if (s_pWindow->m_pBindings->OnWndProc(ahWnd, auMsg, awParam, alParam)) return 0; // VKBindings wants this input ignored! } { - const auto res = s_pWindow->m_pOverlay->OnWndProc(ahWnd, auMsg, awParam, alParam); - if (res) - return 0; // Toolbar wants this input ignored! - } - - { - const auto res = s_pWindow->m_pD3D12->OnWndProc(ahWnd, auMsg, awParam, alParam); - if (res) + if (s_pWindow->m_pD3D12->OnWndProc(ahWnd, auMsg, awParam, alParam)) return 0; // D3D12 wants this input ignored! } + + return CallWindowProc(s_pWindow->m_wndProc, ahWnd, auMsg, awParam, alParam); } - - return CallWindowProc(s_pWindow->m_wndProc, ahWnd, auMsg, awParam, alParam); + + return 0; } -Window::Window(Overlay* apOverlay, VKBindings* apBindings, D3D12* apD3D12) - : m_pOverlay(apOverlay) - , m_pBindings(apBindings) +Window::Window(VKBindings* apBindings, D3D12* apD3D12) + : m_pBindings(apBindings) , m_pD3D12(apD3D12) { s_pWindow = this; - std::thread t([this]() + std::thread t([this] { while (m_hWnd == nullptr) { @@ -92,7 +83,7 @@ Window::Window(Overlay* apOverlay, VKBindings* apBindings, D3D12* apD3D12) t.detach(); } -Window::~Window() +Window::~Window() { s_pWindow = nullptr; diff --git a/src/window/window.h b/src/window/window.h index e87f12d7..acf246a4 100644 --- a/src/window/window.h +++ b/src/window/window.h @@ -1,12 +1,11 @@ #pragma once -struct Overlay; struct D3D12; struct VKBindings; struct Window { - Window(Overlay* apOverlay, VKBindings* apBindings, D3D12* apD3D12); + Window(VKBindings* apBindings, D3D12* apD3D12); ~Window(); HWND GetWindow() const { return m_hWnd; } @@ -25,7 +24,7 @@ struct Window private: - + bool m_initialized{ false }; HWND m_hWnd{ nullptr }; @@ -36,7 +35,6 @@ struct Window POINT m_clientPos{ }; SIZE m_clientSize{ }; - Overlay* m_pOverlay; VKBindings* m_pBindings; D3D12* m_pD3D12; }; diff --git a/vendor/asiloader/version.dll b/vendor/asiloader/version.dll index cc77ed5e..e6d882db 100644 Binary files a/vendor/asiloader/version.dll and b/vendor/asiloader/version.dll differ diff --git a/xmake.lua b/xmake.lua index 2bf2c21e..34aea0c5 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,32 +1,54 @@ -set_xmakever("2.6.0") +set_xmakever("2.7.2") -set_languages("c++20") +set_languages("cxx20") set_arch("x64") -add_requires("spdlog 1.9", "nlohmann_json", "hopscotch-map", "minhook", "mem", "imgui 1.84.2", "sol2", "tiltedcore 0.2.7", "sqlite3", "xbyak", "stb", "openrestry-luajit") -add_requireconfs("sol2", { configs = { includes_lua = false } }) -add_requireconfs("openrestry-luajit", { configs = { gc64 = true } }) - -local imguiUserConfig = path.absolute("src/imgui_impl/imgui_user_config.h") -add_requireconfs("imgui", { configs = { user_config = imguiUserConfig } }) - add_rules("mode.debug","mode.releasedbg", "mode.release") add_rules("plugin.vsxmake.autoupdate") add_rules("c.unity_build") +add_cxflags("/bigobj", "/MP") +add_defines("RED4EXT_STATIC_LIB", "UNICODE", "_UNICODE", "_CRT_SECURE_NO_WARNINGS") + if is_mode("debug") then add_defines("CET_DEBUG") + set_symbols("debug") set_optimize("none") + set_runtimes("MDd") + set_warnings("all") + set_policy("build.optimization.lto", false) elseif is_mode("releasedbg") then add_defines("CET_DEBUG") + set_symbols("debug") set_optimize("fastest") + set_runtimes("MD") + set_warnings("all") + set_policy("build.optimization.lto", true) elseif is_mode("release") then add_defines("NDEBUG") + set_symbols("hidden") + set_strip("all") set_optimize("fastest") + set_runtimes("MD") + set_warnings("all", "error") + set_policy("build.optimization.lto", true) end -add_cxflags("/bigobj", "/MP") -add_defines("RED4EXT_STATIC_LIB", "UNICODE") +local imguiUserConfig = path.absolute("src/imgui_impl/imgui_user_config.h") + +add_requireconfs("*", { debug = is_mode("debug"), lto = not is_mode("debug"), configs = { shared = false } }) +add_requires("spdlog 1.10.0") +add_requires("nlohmann_json") +add_requires("hopscotch-map") +add_requires("minhook") +add_requires("mem") +add_requires("tiltedcore 0.2.7") +add_requires("sqlite3") +add_requires("xbyak") +add_requires("stb") +add_requires("sol2", { configs = { includes_lua = false } }) +add_requires("openrestry-luajit", { configs = { gc64 = true } }) +add_requires("imgui v1.88-docking", { configs = { user_config = imguiUserConfig } }) target("RED4ext.SDK") set_kind("static") @@ -34,61 +56,51 @@ target("RED4ext.SDK") add_files("vendor/RED4ext.SDK/src/**.cpp") add_headerfiles("vendor/RED4ext.SDK/include/**.hpp") add_includedirs("vendor/RED4ext.SDK/include/", { public = true }) - on_install(function() end) + on_install(function() end) target("cyber_engine_tweaks") - add_defines("WIN32_LEAN_AND_MEAN", "NOMINMAX", "WINVER=0x0601", "SOL_ALL_SAFETIES_ON", "SOL_LUAJIT=1", "SPDLOG_WCHAR_TO_UTF8_SUPPORT", "IMGUI_USER_CONFIG=\""..imguiUserConfig.."\"") -- WINVER=0x0601 == Windows 7, we need this specified now for some reason + add_defines("WIN32_LEAN_AND_MEAN", "NOMINMAX", "WINVER=0x0601", "SOL_ALL_SAFETIES_ON", "SOL_LUAJIT=1", "SOL_EXCEPTIONS_SAFE_PROPAGATION", "SPDLOG_WCHAR_TO_UTF8_SUPPORT", "SPDLOG_WCHAR_FILENAMES", "SPDLOG_WCHAR_SUPPORT", "IMGUI_USER_CONFIG=\""..imguiUserConfig.."\"") -- WINVER=0x0601 == Windows 7xmake set_pcxxheader("src/stdafx.h") set_kind("shared") set_filename("cyber_engine_tweaks.asi") - add_files("src/**.c", "src/**.cpp") + add_files("src/**.cpp") add_headerfiles("src/**.h", "build/CETVersion.h") add_includedirs("src/", "build/") add_syslinks("User32", "Version", "d3d11") add_packages("spdlog", "nlohmann_json", "minhook", "hopscotch-map", "imgui", "mem", "sol2", "tiltedcore", "sqlite3", "openrestry-luajit", "xbyak", "stb") add_deps("RED4ext.SDK") - add_configfiles("src/CETVersion.h.in") - - on_package(function(target) - os.mkdir("package/bin/x64/plugins/cyber_engine_tweaks/scripts") - os.cp("vendor/asiloader/*", "package/bin/x64/") - os.cp("LICENSE", "package/bin/x64/") - os.cp("ThirdParty_LICENSES", "package/bin/x64/plugins/cyber_engine_tweaks/ThirdParty_LICENSES") - os.cp(target:targetfile(), "package/bin/x64/plugins/") - os.cp("scripts/*", "package/bin/x64/plugins/cyber_engine_tweaks/scripts") - os.rm("package/*.zip") - end) - on_install(function (target) - cprint("${green bright}Installing Cyber Engine Tweaks ..") - assert(os.isdir("$(installpath)"), format("The path in your configuration doesn't exist or isn't a directory.\n\tUse the follow command to set install path:\n\txmake f --installpath=%s", [["C:\Program Files (x86)\Steam\steamapps\common\Cyberpunk 2077\bin\x64\plugins"]])) - os.cp(target:targetfile(), "$(installpath)") - cprint("Cyber Engine Tweaks installed at: ${underline}%s", "$(installpath)") - end) + add_configfiles("src/CETVersion.h.in") -option("installpath") - set_default("installpath") - set_showmenu(true) - set_description("Set the path to install cyber_engine_tweaks.asi to.", "e.g.", format("\t-xmake f --installpath=%s", [["C:\Program Files (x86)\Steam\steamapps\common\Cyberpunk 2077\bin\x64\plugins"]])) + on_package(function(target) + import("net.http") -task("dephash") - on_run(function () - import("core.project.project") - import("private.action.require.impl.package") + os.rm("package/*") - local requires, requires_extra = project.requires_str() + os.mkdir("package/bin/x64/plugins/cyber_engine_tweaks/tweakdb") + http.download("https://github.com/WolvenKit/WolvenKit/raw/main/WolvenKit.Common/Resources/usedhashes.kark", "package/bin/x64/plugins/cyber_engine_tweaks/tweakdb/usedhashes.kark") + http.download("https://github.com/WolvenKit/WolvenKit/raw/main/WolvenKit.Common/Resources/tweakdbstr.kark", "package/bin/x64/plugins/cyber_engine_tweaks/tweakdb/tweakdbstr.kark") - local key = {} - for _, instance in irpairs(package.load_packages(requires, {requires_extra = requires_extra})) do - table.insert(key, instance:name() .. "-" .. instance:version_str() .. "-" .. instance:buildhash()) - end + os.mkdir("package/bin/x64/plugins/cyber_engine_tweaks/scripts") + os.cp("scripts/*", "package/bin/x64/plugins/cyber_engine_tweaks/scripts") - table.sort(key) + os.mkdir("package/bin/x64/plugins/cyber_engine_tweaks/fonts") + os.cp("fonts/*", "package/bin/x64/plugins/cyber_engine_tweaks/fonts") - key = table.concat(key, ",") - print(hash.uuid4(key):gsub('-', ''):lower()) - end) + os.cp("vendor/asiloader/*", "package/bin/x64/") - set_menu { - usage = "xmake dephash", - description = "Outputs a hash key of current dependencies version/configuration" - } + os.cp("LICENSE", "package/bin/x64/") + os.cp("ThirdParty_LICENSES", "package/bin/x64/plugins/cyber_engine_tweaks/ThirdParty_LICENSES") + + os.cp(target:targetfile(), "package/bin/x64/plugins/") + end) + on_install(function(target) + cprint("${green bright}Installing Cyber Engine Tweaks ..") + assert(os.isdir("$(installpath)"), format("The path in your configuration doesn't exist or isn't a directory.\n\tUse the follow command to set install path:\n\txmake f --installpath=%s", [["C:\Program Files (x86)\Steam\steamapps\common\Cyberpunk 2077\bin\x64\plugins"]])) + os.cp(target:targetfile(), "$(installpath)") + cprint("Cyber Engine Tweaks installed at: ${underline}%s", "$(installpath)") + end) + +option("installpath") + set_default("installpath") + set_showmenu(true) + set_description("Set the path to install cyber_engine_tweaks.asi to.", "e.g.", format("\t-xmake f --installpath=%s", [["C:\Program Files (x86)\Steam\steamapps\common\Cyberpunk 2077\bin\x64\plugins"]]))