From 481f9727ef8d8ef7d7f4ff1ecdcc6f794af92870 Mon Sep 17 00:00:00 2001 From: yamashi Date: Sun, 6 Feb 2022 19:02:32 +0100 Subject: [PATCH] Improve d3d12 hook and logging Logging now displays the function's name D3D12 above Windows 7 now hook the game render functions directly instead of creating a d3d12 device and hooking the functions --- .gitignore | 3 +- ida/find_patterns.py | 160 +++++++++++++++++++++++++++++ ida/patterns.py | 36 +++++++ src/Image.cpp | 2 +- src/Options.cpp | 17 +-- src/Utils.h | 4 +- src/VKBindings.cpp | 2 +- src/common/Logging.h | 60 +++++++++++ src/d3d12/D3D12.cpp | 9 +- src/d3d12/D3D12.h | 7 +- src/d3d12/D3D12_Functions.cpp | 75 +++++++------- src/d3d12/D3D12_Hooks.cpp | 129 +++++++++++++---------- src/overlay/Overlay.cpp | 4 +- src/patches/DisableBoundaries.cpp | 4 +- src/patches/DisableIntroMovies.cpp | 4 +- src/patches/DisableVignette.cpp | 4 +- src/patches/EnableDebug.cpp | 4 +- src/patches/MinimapFlicker.cpp | 4 +- src/patches/OptionsPatch.cpp | 14 +-- src/patches/RemovePeds.cpp | 4 +- src/patches/SkipStartScreen.cpp | 4 +- src/patches/Smt.cpp | 4 +- src/reverse/Addresses.hpp | 23 +++++ src/reverse/RenderContext.cpp | 10 ++ src/reverse/RenderContext.h | 24 +++++ src/scripting/FunctionOverride.cpp | 10 +- src/scripting/GameDump.cpp | 2 +- src/scripting/GameHooks.cpp | 4 +- src/scripting/GameOptions.cpp | 2 +- src/scripting/LuaVM_Hooks.cpp | 28 ++--- src/scripting/Scripting.cpp | 2 +- src/stdafx.h | 2 + src/window/window.cpp | 2 +- xmake.lua | 5 +- 34 files changed, 504 insertions(+), 164 deletions(-) create mode 100644 ida/find_patterns.py create mode 100644 ida/patterns.py create mode 100644 src/common/Logging.h create mode 100644 src/reverse/Addresses.hpp create mode 100644 src/reverse/RenderContext.cpp create mode 100644 src/reverse/RenderContext.h diff --git a/.gitignore b/.gitignore index 7bf9cd39..d2870782 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ build/* src/CETVersion.h package/* vs2022/* -vsxmake*/ \ No newline at end of file +vsxmake*/ +*.pyc diff --git a/ida/find_patterns.py b/ida/find_patterns.py new file mode 100644 index 00000000..d96b2480 --- /dev/null +++ b/ida/find_patterns.py @@ -0,0 +1,160 @@ +import traceback +from typing import Dict, List + +import ida_bytes +import ida_idaapi +import ida_kernwin +import ida_nalt +import idc + +ida_idaapi.require('patterns') + +cached_patterns: Dict[str, List[int]] = dict() + +def bin_search(bin_str: str) -> List[int]: + if not isinstance(bin_str, str): + raise ValueError('bin_str must be a string') + + if bin_str in cached_patterns: + return cached_patterns[bin_str] + + bin_list = bin_str.split() + image = bytearray() + mask = bytearray() + + # Create the mask and convert '?' to 'CC'. + for i in range(len(bin_list)): + byte = bin_list[i] + if byte== '?': + image.append(int('CC', 16)) + mask.append(0) + else: + image.append(int(byte, 16)) + mask.append(1) + + image = bytes(image) + mask = bytes(mask) + + start = ida_nalt.get_imagebase() + end = ida_idaapi.BADADDR + + addrs: List[int] = [] + + ea = ida_bytes.bin_search(start, end, image, mask, 0, ida_bytes.BIN_SEARCH_FORWARD) + while ea != ida_idaapi.BADADDR: + addrs.append(ea) + ea = ida_bytes.bin_search(ea + len(image), end, image, mask, 0, ida_bytes.BIN_SEARCH_FORWARD) + + cached_patterns[bin_str] = addrs + return cached_patterns[bin_str] + +def find_pattern(pattern: str, expected: int = 1, index: int = 0) -> int: + if not isinstance(expected, int): + raise ValueError('expected must be an integer') + + if not isinstance(index, int): + raise ValueError('index must be an integer') + + addrs = bin_search(pattern) + if len(addrs) != expected: + print(f'Found {len(addrs)} match(es) but {expected} match(es) were expected for pattern "{pattern}"') + return ida_idaapi.BADADDR + + return addrs[index] + +def find_function(pattern: str, expected: int = 1, index: int = 0) -> int: + return find_pattern(pattern, expected, index) + +def find_ptr(pattern: str, expected: int = 1, index: int = 0, offset: int = 0) -> int: + addr = find_pattern(pattern, expected, index) + if addr == ida_idaapi.BADADDR: + return addr + + disp = ida_bytes.get_dword(addr + offset) + + # Real address is: pattern_addr + offset + displacement + size_of_displacement. + return addr + offset + disp + 4 + +try: + groups = patterns.get_groups() + total = sum(map(lambda g: len(g.pointers) + len(g.functions), groups)) + + groups.sort(key=lambda g: g.name.lower()) + + addr = find_ptr(pattern='4C 8D 05 ? ? ? ? 49 89 AE 80 01 00 00 8D 55 11', offset=3) + version = idc.get_strlit_contents(addr) + + print(f'Finding {total} item(s)...') + with open('Addresses.hpp', 'w') as file: + file.write('#pragma once\n') + file.write('\n') + file.write('/*\n') + file.write(' * This file is generated. DO NOT modify it!\n') + file.write(' *\n') + file.write(' * Add new patterns in "patterns.py" file located in "project_root/scripts" and run "find_patterns.py".\n') + file.write(' * The new file should be located in "idb_path/Addresses.hpp".\n') + file.write(' */\n') + file.write('#include \n') + file.write('\n') + file.write(f'// Addresses for Cyberpunk 2077, version {version.decode()}.\n') + file.write('namespace CyberEngineTweaks::Addresses\n') + file.write('{\n') + file.write(f'constexpr uintptr_t ImageBase = 0x{ida_nalt.get_imagebase():X};\n') + file.write('\n') + + for group in groups: + if group.name: + file.write(f'#pragma region {group.name}\n') + + for ptr in group.pointers: + addr = find_ptr(pattern=ptr.pattern, expected=ptr.expected, index=ptr.index, offset=ptr.offset) + if addr == ida_idaapi.BADADDR: + file.write(f'#error Could not find pattern "{ptr.pattern}", expected: {ptr.expected}, index: {ptr.index}, offset: {ptr.offset}\n') + continue + + if not group.name and not ptr.name: + ptr.name = f'ptr_{addr:X}' + + file.write('constexpr uintptr_t ') + if group.name: + file.write(f'{group.name}') + + if ptr.name: + file.write('_') + + ptr.name = ptr.name.replace('::', '_') + + file.write(f'{ptr.name} = 0x{addr:X} - ImageBase; ') + file.write(f'// {ptr.pattern}, expected: {ptr.expected}, index: {ptr.index}, offset: {ptr.offset}\n') + + for func in group.functions: + addr = find_function(pattern=func.pattern, expected=func.expected, index=func.index) + if addr == ida_idaapi.BADADDR: + file.write(f'#error Could not find pattern "{func.pattern}", expected: {func.expected}, index: {func.index}\n') + continue + + file.write('constexpr uintptr_t ') + + if group.name: + file.write(f'{group.name}_') + + if not func.name: + func.name = f'sub_{addr:X}' + + func.name = func.name.replace('::', '_') + + file.write(f'{func.name} = 0x{addr:X} - ImageBase; ') + file.write(f'// {func.pattern}, expected: {func.expected}, index: {func.index}\n') + + if group.name: + file.write(f'#pragma endregion\n') + + if group != groups[-1]: + file.write('\n') + + file.write('} // namespace CyberEngineTweaks::Addresses\n') + + print('Done!') + ida_kernwin.beep() +except: + traceback.print_exc() diff --git a/ida/patterns.py b/ida/patterns.py new file mode 100644 index 00000000..bc1deb5e --- /dev/null +++ b/ida/patterns.py @@ -0,0 +1,36 @@ +from typing import List + +class Item: + name: str + pattern: str + expected: int + index: int + offset: int + + def __init__(self, pattern: str, name: str = '', expected: int = 1, index: int = 0, offset: int = 0) -> None: + self.name = name + self.pattern = pattern + self.expected = expected + self.index = index + self.offset = offset + +class Group: + name: str + pointers: List[Item] + functions: List[Item] + + def __init__(self, name: str, pointers: List[Item] = [], functions: List[Item] = []) -> None: + self.name = name + self.pointers = pointers + self.functions = functions + +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='48 8B 05 ? ? ? ? 8B D1 48 8B 88 18 8A 5A 01', expected=1, offset=3) + ]), + 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) + ]) + ] diff --git a/src/Image.cpp b/src/Image.cpp index f90bdc3f..e0692501 100644 --- a/src/Image.cpp +++ b/src/Image.cpp @@ -75,7 +75,7 @@ void Image::Initialize() if (version == 0) { for (auto c : pdb_info->Guid) - spdlog::error("{:X}", (uint32_t)c); + Log::Error("{:X}", (uint32_t)c); } } } diff --git a/src/Options.cpp b/src/Options.cpp index f56a170b..f140f9bd 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -138,30 +138,31 @@ Options::Options(Paths& aPaths) set_default_logger(CreateLogger(m_paths.CETRoot() / "cyber_engine_tweaks.log", "main")); - spdlog::info("Cyber Engine Tweaks is starting..."); + + Log::Info("Cyber Engine Tweaks is starting..."); GameImage.Initialize(); if (GameImage.version) { - spdlog::info("CET version {} [{}]", CET_BUILD_COMMIT, CET_BUILD_BRANCH); + Log::Info("CET version {} [{}]", CET_BUILD_COMMIT, CET_BUILD_BRANCH); auto [major, minor] = GameImage.GetVersion(); - spdlog::info("Game version {}.{:02d}", major, minor); - spdlog::info("Root path: \"{}\"", aPaths.GameRoot().string()); - spdlog::info("Cyber Engine Tweaks path: \"{}\"", aPaths.CETRoot().string()); - spdlog::info("Lua scripts search path: \"{}\"", aPaths.ModsRoot().string()); + 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()); if (GameImage.GetVersion() != GameImage.GetSupportedVersion()) { auto [smajor, sminor] = GameImage.GetSupportedVersion(); - spdlog::error("Unsupported game version! Only {}.{:02d} is supported.", smajor, sminor); + Log::Error("Unsupported game version! Only {}.{:02d} is supported.", smajor, sminor); throw std::runtime_error("Unsupported version"); } } else { - spdlog::info("Unknown Game Version, update the mod"); + Log::Info("Unknown Game Version, update the mod"); throw std::runtime_error("Unknown version"); } diff --git a/src/Utils.h b/src/Utils.h index d44b8e8c..f152a026 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -6,7 +6,7 @@ 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::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"); // deep copies sol object (doesnt take into account potential duplicates) sol::object DeepCopySolObject(sol::object aObj, const sol::state_view& aStateView); @@ -33,4 +33,4 @@ sol::function MakeSolFunction(sol::state& aState, F aFunc) } // Check if Lua object is of cdata type -bool IsLuaCData(sol::object aObject); +bool IsLuaCData(sol::object aObject); \ No newline at end of file diff --git a/src/VKBindings.cpp b/src/VKBindings.cpp index 0fd17a73..f1bfa081 100644 --- a/src/VKBindings.cpp +++ b/src/VKBindings.cpp @@ -722,7 +722,7 @@ LRESULT VKBindings::HandleRAWInput(HRAWINPUT ahRAWInput) const auto lpb = std::make_unique(dwSize); if (GetRawInputData(ahRAWInput, RID_INPUT, lpb.get(), &dwSize, sizeof(RAWINPUTHEADER)) != dwSize ) - spdlog::warn("VKBindings::HandleRAWInput() - GetRawInputData() does not return correct size !"); + Log::Warn("VKBindings::HandleRAWInput() - GetRawInputData() does not return correct size !"); const auto* raw = reinterpret_cast(lpb.get()); if (raw->header.dwType == RIM_TYPEKEYBOARD) diff --git a/src/common/Logging.h b/src/common/Logging.h new file mode 100644 index 00000000..7266df7f --- /dev/null +++ b/src/common/Logging.h @@ -0,0 +1,60 @@ +#pragma once + +namespace Log +{ +using source_location = std::source_location; +[[nodiscard]] constexpr auto get_log_source_location(const source_location& location) +{ + return spdlog::source_loc{location.file_name(), static_cast(location.line()), + location.function_name()}; +} + +struct format_with_location +{ + std::string_view value; + spdlog::source_loc loc; + + template + format_with_location(const String& s, const source_location& location = source_location::current()) + : value{s} + , loc{get_log_source_location(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)...); +} + +template +void Info(format_with_location fmt, Args&&... args) +{ + spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::info, 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)...); +} + +template +void Error(format_with_location fmt, Args&&... args) +{ + spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::err, 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)...); +} + +template +void Trace(format_with_location fmt, Args&&... args) +{ + spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::trace, fmt.value, std::forward(args)...); +} +} // namespace logging \ No newline at end of file diff --git a/src/d3d12/D3D12.cpp b/src/d3d12/D3D12.cpp index 4a5eb382..e75140cd 100644 --- a/src/d3d12/D3D12.cpp +++ b/src/d3d12/D3D12.cpp @@ -64,15 +64,18 @@ D3D12::D3D12(Window& aWindow, Paths& aPaths, Options& aOptions) , m_window(aWindow) , m_options(aOptions) { + HookGame(); + std::thread t([this]() { if (kiero::init() != kiero::Status::Success) - spdlog::error("Kiero failed!"); + Log::Error("Kiero failed!"); else { std::string_view d3d12type = (kiero::isDownLevelDevice()) ? ("D3D12on7") : ("D3D12"); - spdlog::info("Kiero initialized for {0}", d3d12type); - this->Hook(); + Log::Info("Kiero initialized for {0}", d3d12type); + + Hook(); } }); diff --git a/src/d3d12/D3D12.h b/src/d3d12/D3D12.h index 81330661..ecf76bc5 100644 --- a/src/d3d12/D3D12.h +++ b/src/d3d12/D3D12.h @@ -4,10 +4,10 @@ #include "window/Window.h" using TResizeBuffersD3D12 = HRESULT(IDXGISwapChain*, UINT, UINT, UINT, DXGI_FORMAT, UINT); -using TPresentD3D12 = HRESULT(IDXGISwapChain*, UINT, 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); struct D3D12 { @@ -30,6 +30,7 @@ struct D3D12 protected: void Hook(); + void HookGame(); struct FrameContext { @@ -46,18 +47,18 @@ struct D3D12 void Update(); static HRESULT ResizeBuffers(IDXGISwapChain* apSwapChain, UINT aBufferCount, UINT aWidth, UINT aHeight, DXGI_FORMAT aNewFormat, UINT aSwapChainFlags); - static HRESULT Present(IDXGISwapChain* apSwapChain, UINT aSyncInterval, UINT aPresentFlags); 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); private: TResizeBuffersD3D12* m_realResizeBuffersD3D12{ nullptr }; - TPresentD3D12* m_realPresentD3D12{ nullptr }; TPresentD3D12Downlevel* m_realPresentD3D12Downlevel{ nullptr }; TCreateCommittedResource* m_realCreateCommittedResource{ nullptr }; TExecuteCommandLists* m_realExecuteCommandLists{ nullptr }; + TCRenderNode_Present_InternalPresent* m_realInternalPresent{ nullptr }; bool m_initialized{ false }; diff --git a/src/d3d12/D3D12_Functions.cpp b/src/d3d12/D3D12_Functions.cpp index fe10cb8e..da6c049a 100644 --- a/src/d3d12/D3D12_Functions.cpp +++ b/src/d3d12/D3D12_Functions.cpp @@ -17,9 +17,12 @@ bool D3D12::ResetState(bool aClearDownlevelBackbuffers) ImGui_ImplDX12_Shutdown(); ImGui_ImplWin32_Shutdown(); } + m_frameContexts.clear(); + if (aClearDownlevelBackbuffers) m_downlevelBackbuffers.clear(); + m_pdxgiSwapChain = nullptr; m_pd3d12Device = nullptr; m_pd3dRtvDescHeap = nullptr; @@ -45,12 +48,12 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) if(desc.Type != D3D12_COMMAND_LIST_TYPE_DIRECT) { d3d12->m_pCommandQueue = nullptr; - spdlog::warn("D3D12::Initialize() - invalid type of command list!"); + Log::Warn("D3D12::Initialize() - invalid type of command list!"); return false; } return true; } - spdlog::warn("D3D12::Initialize() - swap chain is missing command queue!"); + Log::Warn("D3D12::Initialize() - swap chain is missing command queue!"); return false; } return true; @@ -59,10 +62,10 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) if (!apSwapChain) return false; - HWND hWnd = m_window.GetWindow(); + const HWND hWnd = m_window.GetWindow(); if (!hWnd) { - spdlog::warn("D3D12::InitializeDownlevel() - window not yet hooked!"); + Log::Warn("D3D12::InitializeDownlevel() - window not yet hooked!"); return false; } @@ -71,12 +74,12 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) IDXGISwapChain3* pSwapChain3{ nullptr }; if (FAILED(apSwapChain->QueryInterface(IID_PPV_ARGS(&pSwapChain3)))) { - spdlog::error("D3D12::Initialize() - unable to query pSwapChain interface for IDXGISwapChain3! (pSwapChain = {0})", reinterpret_cast(apSwapChain)); + Log::Error("D3D12::Initialize() - unable to query pSwapChain interface for IDXGISwapChain3! (pSwapChain = {:X})", reinterpret_cast(apSwapChain)); return false; } if (m_pdxgiSwapChain != pSwapChain3) { - spdlog::warn("D3D12::Initialize() - multiple swap chains detected! Currently hooked to {0}, this call was from {1}.", reinterpret_cast(*(&m_pdxgiSwapChain)), reinterpret_cast(apSwapChain)); + 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; } { @@ -84,11 +87,11 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) m_pdxgiSwapChain->GetDesc(&sdesc); if (hWnd != sdesc.OutputWindow) - spdlog::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 {0} while swap chain output window is {1}.", reinterpret_cast(hWnd), reinterpret_cast(sdesc.OutputWindow)); } if (!checkCmdQueue(this)) { - spdlog::error("D3D12::Initialize() - missing command queue!"); + Log::Error("D3D12::Initialize() - missing command queue!"); return false; } return true; @@ -96,13 +99,13 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) if (FAILED(apSwapChain->QueryInterface(IID_PPV_ARGS(&m_pdxgiSwapChain)))) { - spdlog::error("D3D12::Initialize() - unable to query pSwapChain interface for IDXGISwapChain3! (pSwapChain = {0})", reinterpret_cast(apSwapChain)); + 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)))) { - spdlog::error("D3D12::Initialize() - failed to get device!"); + Log::Error("D3D12::Initialize() - failed to get device!"); return ResetState(); } @@ -110,11 +113,11 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) m_pdxgiSwapChain->GetDesc(&sdesc); if (hWnd != sdesc.OutputWindow) - spdlog::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 {0} while swap chain output window is {1}.", reinterpret_cast(hWnd), reinterpret_cast(sdesc.OutputWindow)); m_outSize = { static_cast(sdesc.BufferDesc.Width), static_cast(sdesc.BufferDesc.Height) }; - auto buffersCounts = sdesc.BufferCount; + const auto buffersCounts = sdesc.BufferCount; m_frameContexts.resize(buffersCounts); for (UINT i = 0; i < buffersCounts; i++) m_pdxgiSwapChain->GetBuffer(i, IID_PPV_ARGS(&m_frameContexts[i].BackBuffer)); @@ -126,7 +129,7 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) rtvdesc.NodeMask = 1; if (FAILED(m_pd3d12Device->CreateDescriptorHeap(&rtvdesc, IID_PPV_ARGS(&m_pd3dRtvDescHeap)))) { - spdlog::error("D3D12::Initialize() - failed to create RTV descriptor heap!"); + Log::Error("D3D12::Initialize() - failed to create RTV descriptor heap!"); return ResetState(); } @@ -144,21 +147,21 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) srvdesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; if (FAILED(m_pd3d12Device->CreateDescriptorHeap(&srvdesc, IID_PPV_ARGS(&m_pd3dSrvDescHeap)))) { - spdlog::error("D3D12::Initialize() - failed to create SRV descriptor heap!"); + 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)))) { - spdlog::error("D3D12::Initialize() - failed to create command allocator!"); + Log::Error("D3D12::Initialize() - failed to create command allocator!"); return ResetState(); } if (FAILED(m_pd3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_frameContexts[0].CommandAllocator, nullptr, IID_PPV_ARGS(&m_pd3dCommandList))) || FAILED(m_pd3dCommandList->Close())) { - spdlog::error("D3D12::Initialize() - failed to create command list!"); + Log::Error("D3D12::Initialize() - failed to create command list!"); return ResetState(); } @@ -167,17 +170,17 @@ bool D3D12::Initialize(IDXGISwapChain* apSwapChain) if (!checkCmdQueue(this)) { - spdlog::error("D3D12::Initialize() - missing command queue!"); + Log::Error("D3D12::Initialize() - missing command queue!"); return false; } if (!InitializeImGui(buffersCounts)) { - spdlog::error("D3D12::Initialize() - failed to initialize ImGui!"); + Log::Error("D3D12::Initialize() - failed to initialize ImGui!"); return ResetState(); } - spdlog::info("D3D12::Initialize() - initialization successful!"); + Log::Info("D3D12::Initialize() - initialization successful!"); m_initialized = true; OnInitialized.Emit(); @@ -193,14 +196,14 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour HWND hWnd = m_window.GetWindow(); if (!hWnd) { - spdlog::warn("D3D12::InitializeDownlevel() - window not yet hooked!"); + Log::Warn("D3D12::InitializeDownlevel() - window not yet hooked!"); return false; } if (m_initialized) { if (hWnd != ahWindow) - spdlog::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 {0} while current output window is {1}.", reinterpret_cast(hWnd), reinterpret_cast(ahWindow)); return true; } @@ -208,7 +211,7 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour auto cmdQueueDesc = apCommandQueue->GetDesc(); if(cmdQueueDesc.Type != D3D12_COMMAND_LIST_TYPE_DIRECT) { - spdlog::warn("D3D12::InitializeDownlevel() - ignoring command queue - invalid type of command list!"); + Log::Warn("D3D12::InitializeDownlevel() - ignoring command queue - invalid type of command list!"); return false; } @@ -218,11 +221,11 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour m_outSize = { static_cast(st2DDesc.Width), static_cast(st2DDesc.Height) }; if (hWnd != ahWindow) - spdlog::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 {0} while current output window is {1}.", reinterpret_cast(hWnd), reinterpret_cast(ahWindow)); if (FAILED(apSourceTex2D->GetDevice(IID_PPV_ARGS(&m_pd3d12Device)))) { - spdlog::error("D3D12::InitializeDownlevel() - failed to get device!"); + Log::Error("D3D12::InitializeDownlevel() - failed to get device!"); return ResetState(); } @@ -230,12 +233,12 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour m_frameContexts.resize(buffersCounts); if (buffersCounts == 0) { - spdlog::error("D3D12::InitializeDownlevel() - no backbuffers were found!"); + Log::Error("D3D12::InitializeDownlevel() - no backbuffers were found!"); return ResetState(); } if (buffersCounts < g_numDownlevelBackbuffersRequired) { - spdlog::info("D3D12::InitializeDownlevel() - backbuffer list is not complete yet; assuming window was resized"); + Log::Info("D3D12::InitializeDownlevel() - backbuffer list is not complete yet; assuming window was resized"); return false; } @@ -246,7 +249,7 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour rtvdesc.NodeMask = 1; if (FAILED(m_pd3d12Device->CreateDescriptorHeap(&rtvdesc, IID_PPV_ARGS(&m_pd3dRtvDescHeap)))) { - spdlog::error("D3D12::InitializeDownlevel() - failed to create RTV descriptor heap!"); + Log::Error("D3D12::InitializeDownlevel() - failed to create RTV descriptor heap!"); return ResetState(); } @@ -264,7 +267,7 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour srvdesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; if (FAILED(m_pd3d12Device->CreateDescriptorHeap(&srvdesc, IID_PPV_ARGS(&m_pd3dSrvDescHeap)))) { - spdlog::error("D3D12::InitializeDownlevel() - failed to create SRV descriptor heap!"); + Log::Error("D3D12::InitializeDownlevel() - failed to create SRV descriptor heap!"); return ResetState(); } @@ -272,20 +275,20 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour { if (FAILED(m_pd3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&context.CommandAllocator)))) { - spdlog::error("D3D12::InitializeDownlevel() - failed to create command allocator!"); + Log::Error("D3D12::InitializeDownlevel() - failed to create command allocator!"); return ResetState(); } } if (FAILED(m_pd3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_frameContexts[0].CommandAllocator, nullptr, IID_PPV_ARGS(&m_pd3dCommandList)))) { - spdlog::error("D3D12::InitializeDownlevel() - failed to create command list!"); + Log::Error("D3D12::InitializeDownlevel() - failed to create command list!"); return ResetState(); } if (FAILED(m_pd3dCommandList->Close())) { - spdlog::error("D3D12::InitializeDownlevel() - failed to close command list!"); + Log::Error("D3D12::InitializeDownlevel() - failed to close command list!"); return ResetState(); } @@ -298,11 +301,11 @@ bool D3D12::InitializeDownlevel(ID3D12CommandQueue* apCommandQueue, ID3D12Resour if (!InitializeImGui(buffersCounts)) { - spdlog::error("D3D12::InitializeDownlevel() - failed to initialize ImGui!"); + Log::Error("D3D12::InitializeDownlevel() - failed to initialize ImGui!"); return ResetState(); } - spdlog::info("D3D12::InitializeDownlevel() - initialization successful!"); + Log::Info("D3D12::InitializeDownlevel() - initialization successful!"); m_initialized = true; OnInitialized.Emit(); @@ -362,7 +365,7 @@ bool D3D12::InitializeImGui(size_t aBuffersCounts) if (!ImGui_ImplWin32_Init(m_window.GetWindow())) { - spdlog::error("D3D12::InitializeImGui() - ImGui_ImplWin32_Init call failed!"); + Log::Error("D3D12::InitializeImGui() - ImGui_ImplWin32_Init call failed!"); return false; } @@ -371,14 +374,14 @@ bool D3D12::InitializeImGui(size_t aBuffersCounts) m_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), m_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart())) { - spdlog::error("D3D12::InitializeImGui() - ImGui_ImplDX12_Init call failed!"); + Log::Error("D3D12::InitializeImGui() - ImGui_ImplDX12_Init call failed!"); ImGui_ImplWin32_Shutdown(); return false; } if (!ImGui_ImplDX12_CreateDeviceObjects(m_pCommandQueue)) { - spdlog::error("D3D12::InitializeImGui() - ImGui_ImplDX12_CreateDeviceObjects call failed!"); + Log::Error("D3D12::InitializeImGui() - ImGui_ImplDX12_CreateDeviceObjects call failed!"); ImGui_ImplDX12_Shutdown(); ImGui_ImplWin32_Shutdown(); return false; diff --git a/src/d3d12/D3D12_Hooks.cpp b/src/d3d12/D3D12_Hooks.cpp index 7e7f0165..ca86a5ec 100644 --- a/src/d3d12/D3D12_Hooks.cpp +++ b/src/d3d12/D3D12_Hooks.cpp @@ -3,6 +3,8 @@ #include #include "D3D12.h" +#include "reverse/Addresses.hpp" +#include "reverse/RenderContext.h" #include @@ -13,23 +15,13 @@ HRESULT D3D12::ResizeBuffers(IDXGISwapChain* apSwapChain, UINT aBufferCount, UIN 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 - spdlog::info("D3D12::ResizeBuffers() called with initialized D3D12, triggering D3D12::ResetState."); + Log::Info("D3D12::ResizeBuffers() called with initialized D3D12, triggering D3D12::ResetState."); d3d12.ResetState(); } return d3d12.m_realResizeBuffersD3D12(apSwapChain, aBufferCount, aWidth, aHeight, aNewFormat, aSwapChainFlags); } -HRESULT D3D12::Present(IDXGISwapChain* apSwapChain, UINT aSyncInterval, UINT aPresentFlags) -{ - auto& d3d12 = CET::Get().GetD3D12(); - - if (d3d12.Initialize(apSwapChain)) - d3d12.Update(); - - return d3d12.m_realPresentD3D12(apSwapChain, aSyncInterval, aPresentFlags); -} - HRESULT D3D12::PresentDownlevel(ID3D12CommandQueueDownlevel* apCommandQueueDownlevel, ID3D12GraphicsCommandList* apOpenCommandList, ID3D12Resource* apSourceTex2D, HWND ahWindow, D3D12_DOWNLEVEL_PRESENT_FLAGS aFlags) { if (CET::Get().GetOptions().PatchDisableWin7Vsync) @@ -110,11 +102,13 @@ void D3D12::ExecuteCommandLists(ID3D12CommandQueue* apCommandQueue, UINT aNumCom auto desc = apCommandQueue->GetDesc(); if(desc.Type == D3D12_COMMAND_LIST_TYPE_DIRECT) { + auto ret = (uintptr_t)_ReturnAddress() - (uintptr_t)GetModuleHandleA(nullptr); + d3d12.m_pCommandQueue = apCommandQueue; - spdlog::info("D3D12::ExecuteCommandListsD3D12() - found valid command queue."); + Log::Info("D3D12::ExecuteCommandListsD3D12() - found valid command queue. {:X}", ret); } else - spdlog::info("D3D12::ExecuteCommandListsD3D12() - ignoring command queue - unusable command list type"); + Log::Info("D3D12::ExecuteCommandListsD3D12() - ignoring command queue - unusable command list type"); } d3d12.m_realExecuteCommandLists(apCommandQueue, aNumCommandLists, apcpCommandLists); @@ -131,77 +125,98 @@ void* ApplyHook(void** vtable, size_t index, void* target) return ret; } -void D3D12::Hook() +void* D3D12::CRenderNode_Present_InternalPresent(int32_t* apSomeInt, uint8_t aSomeSync, UINT aSyncInterval) { - int d3d12FailedHooksCount = 0; - int d3d12CompleteHooksCount = 0; - - std::string_view d3d12type = (kiero::isDownLevelDevice()) ? ("D3D12on7") : ("D3D12"); + auto& d3d12 = CET::Get().GetD3D12(); - if (kiero::isDownLevelDevice()) + if (!kiero::isDownLevelDevice()) { - if (kiero::bind(175, reinterpret_cast(&m_realPresentD3D12Downlevel), &PresentDownlevel) != kiero::Status::Success) - { - spdlog::error("{0} Downlevel Present hook failed!", d3d12type); - ++d3d12FailedHooksCount; - } - else + const auto idx = *apSomeInt - 1; + + const auto* pContext = RenderContext::GetInstance(); + if (pContext->unkED65C0 == nullptr) { - spdlog::info("{0} Downlevel Present hook complete.", d3d12type); - ++d3d12CompleteHooksCount; + auto* pDevice = pContext->devices[idx].pSwapChain; + + static std::once_flag s_init; + std::call_once(s_init, [&]() { + void** vtbl = *reinterpret_cast(pDevice); + d3d12.m_realResizeBuffersD3D12 = + static_cast(ApplyHook(vtbl, 13, &D3D12::ResizeBuffers)); + Log::Info("D3D12: Applied ResizeBuffers vtable hook"); + }); + + if (d3d12.Initialize(pDevice)) + d3d12.Update(); } + } + + return d3d12.m_realInternalPresent(apSomeInt, aSomeSync, aSyncInterval); +} + +void D3D12::Hook() +{ + if (kiero::isDownLevelDevice()) + { + int d3d12FailedHooksCount = 0; + int d3d12CompleteHooksCount = 0; - if (kiero::bind(27, reinterpret_cast(&m_realCreateCommittedResource), &CreateCommittedResource) != kiero::Status::Success) + if (kiero::bind(175, reinterpret_cast(&m_realPresentD3D12Downlevel), &PresentDownlevel) != + kiero::Status::Success) { - spdlog::error("{0} CreateCommittedResource Hook failed!", d3d12type); + Log::Error("D3D12on7: Downlevel Present hook failed!"); ++d3d12FailedHooksCount; } - else + else { - spdlog::info("{0} CreateCommittedResource hook complete.", d3d12type); + Log::Info("D3D12on7: Downlevel Present hook complete."); ++d3d12CompleteHooksCount; } - } - else - { - if (kiero::bind(140, reinterpret_cast(&m_realPresentD3D12), &Present) != kiero::Status::Success) + + if (kiero::bind(27, reinterpret_cast(&m_realCreateCommittedResource), &CreateCommittedResource) != + kiero::Status::Success) { - spdlog::error("{0} Present hook failed!", d3d12type); + Log::Error("D3D12on7: CreateCommittedResource Hook failed!"); ++d3d12FailedHooksCount; } - else + else { - spdlog::info("{0} Present hook complete.", d3d12type); + Log::Info("D3D12on7: CreateCommittedResource hook complete."); ++d3d12CompleteHooksCount; } - if (kiero::bind(145, reinterpret_cast(&m_realResizeBuffersD3D12), &ResizeBuffers) != + if (kiero::bind(54, reinterpret_cast(&m_realExecuteCommandLists), &ExecuteCommandLists) != kiero::Status::Success) { - spdlog::error("{0} ResizeBuffers hook failed!", d3d12type); + Log::Error("D3D12on7: ExecuteCommandLists hook failed!"); ++d3d12FailedHooksCount; } - else + else { - spdlog::info("{0} ResizeBuffers hook complete.", d3d12type); + Log::Info("D3D12on7: ExecuteCommandLists hook complete."); ++d3d12CompleteHooksCount; } - } - if (kiero::bind(54, reinterpret_cast(&m_realExecuteCommandLists), &ExecuteCommandLists) != - kiero::Status::Success) - { - spdlog::error("{0} ExecuteCommandLists hook failed!", d3d12type); - ++d3d12FailedHooksCount; - } - else - { - spdlog::info("{0} ExecuteCommandLists hook complete.", d3d12type); - ++d3d12CompleteHooksCount; + if (d3d12FailedHooksCount == 0) + Log::Info("D3D12on7: hook complete. ({1}/{2})", d3d12CompleteHooksCount, + d3d12CompleteHooksCount + d3d12FailedHooksCount); + else + Log::Error("D3D12on7: hook failed! ({1}/{2})", d3d12CompleteHooksCount, + d3d12CompleteHooksCount + d3d12FailedHooksCount); } + else + Log::Info("Skipping internal d3d12 hook, using game method"); +} + +void D3D12::HookGame() +{ + RED4ext::RelocPtr presentInternal(CyberEngineTweaks::Addresses::CRenderNode_Present_DoInternal); - if (d3d12FailedHooksCount == 0) - spdlog::info("{0} hook complete. ({1}/{2})", d3d12type, d3d12CompleteHooksCount, d3d12CompleteHooksCount+d3d12FailedHooksCount); - else - spdlog::error("{0} hook failed! ({1}/{2})", d3d12type, d3d12CompleteHooksCount, d3d12CompleteHooksCount+d3d12FailedHooksCount); + if (MH_CreateHook(presentInternal.GetAddr(), &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!"); } + diff --git a/src/overlay/Overlay.cpp b/src/overlay/Overlay.cpp index f124868e..0e427914 100644 --- a/src/overlay/Overlay.cpp +++ b/src/overlay/Overlay.cpp @@ -226,9 +226,9 @@ void Overlay::Hook() if (pLocation) { if (MH_CreateHook(pLocation, &ClipToCenter, reinterpret_cast(&m_realClipToCenter)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - spdlog::error("Could not hook mouse clip function!"); + Log::Error("Could not hook mouse clip function!"); else - spdlog::info("Hook mouse clip function!"); + Log::Info("Hook mouse clip function!"); } } diff --git a/src/patches/DisableBoundaries.cpp b/src/patches/DisableBoundaries.cpp index ae33943c..311a636b 100644 --- a/src/patches/DisableBoundaries.cpp +++ b/src/patches/DisableBoundaries.cpp @@ -12,7 +12,7 @@ void DisableBoundaryTeleportPatch(const Image* apImage) if (pLocation == nullptr) { - spdlog::warn("Disable boundary teleport: failed, could not be found"); + Log::Warn("Disable boundary teleport: failed, could not be found"); return; } @@ -21,5 +21,5 @@ void DisableBoundaryTeleportPatch(const Image* apImage) pLocation[0] = 0xC3; VirtualProtect(pLocation, 32, oldProtect, nullptr); - spdlog::info("Disable boundary teleport: success"); + Log::Info("Disable boundary teleport: success"); } diff --git a/src/patches/DisableIntroMovies.cpp b/src/patches/DisableIntroMovies.cpp index ce28a241..ea1a457b 100644 --- a/src/patches/DisableIntroMovies.cpp +++ b/src/patches/DisableIntroMovies.cpp @@ -37,10 +37,10 @@ void DisableIntroMoviesPatch(const Image* apImage) if (RealInitScriptMemberVariable == nullptr) { - spdlog::warn("Disable intro movies patch: failed, could not be found"); + Log::Warn("Disable intro movies patch: failed, could not be found"); return; } MH_CreateHook(RealInitScriptMemberVariable, &HookInitScriptMemberVariable, reinterpret_cast(&RealInitScriptMemberVariable)); - spdlog::info("Disable intro movies patch: success"); + Log::Info("Disable intro movies patch: success"); } diff --git a/src/patches/DisableVignette.cpp b/src/patches/DisableVignette.cpp index e799d590..05ca5abf 100644 --- a/src/patches/DisableVignette.cpp +++ b/src/patches/DisableVignette.cpp @@ -11,7 +11,7 @@ void DisableVignettePatch(const Image* apImage) if (pLocation == nullptr) { - spdlog::warn("Disable vignette patch: failed, could not be found"); + Log::Warn("Disable vignette patch: failed, could not be found"); return; } @@ -22,5 +22,5 @@ void DisableVignettePatch(const Image* apImage) pLocation[2] = 0xC3; VirtualProtect(pLocation, 32, oldProtect, nullptr); - spdlog::info("Disable vignette patch: success"); + Log::Info("Disable vignette patch: success"); } diff --git a/src/patches/EnableDebug.cpp b/src/patches/EnableDebug.cpp index 36025968..7bf2cd2f 100644 --- a/src/patches/EnableDebug.cpp +++ b/src/patches/EnableDebug.cpp @@ -114,11 +114,11 @@ void EnableDebugPatch(const Image* apImage) MH_CreateHook(RealRegisterScriptMemberFunction, &HookRegisterScriptMemberFunction, reinterpret_cast(&RealRegisterScriptMemberFunction)); } - spdlog::info("{}: success", patchType); + Log::Info("{}: success", patchType); } else { - spdlog::warn("{}: failed, unknown version", patchType); + Log::Warn("{}: failed, unknown version", patchType); } } } diff --git a/src/patches/MinimapFlicker.cpp b/src/patches/MinimapFlicker.cpp index 39cee87c..4a21aa93 100644 --- a/src/patches/MinimapFlicker.cpp +++ b/src/patches/MinimapFlicker.cpp @@ -10,7 +10,7 @@ void MinimapFlickerPatch(const Image* apImage) if (pLocation == nullptr) { - spdlog::warn("Minimap Flicker Patch: failed"); + Log::Warn("Minimap Flicker Patch: failed"); return; } @@ -21,5 +21,5 @@ void MinimapFlickerPatch(const Image* apImage) pLocation[0] = 0x01; VirtualProtect(pLocation, 32, oldProtect, nullptr); - spdlog::info("Minimap Flicker Patch: success"); + Log::Info("Minimap Flicker Patch: success"); } diff --git a/src/patches/OptionsPatch.cpp b/src/patches/OptionsPatch.cpp index 2f7e949e..d4e49865 100644 --- a/src/patches/OptionsPatch.cpp +++ b/src/patches/OptionsPatch.cpp @@ -20,21 +20,21 @@ void* HookGameOptionInit(GameOption* apThis) } if (options.DumpGameOptions) - spdlog::info(apThis->GetInfo()); + Log::Info(apThis->GetInfo()); if (options.PatchAsyncCompute && strcmp(apThis->pCategory, "Rendering/AsyncCompute") == 0) { if (apThis->SetBool(false)) - spdlog::info("Disabled hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); + Log::Info("Disabled hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); else - spdlog::warn("Failed to disable hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); + Log::Warn("Failed to disable hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); } else if (options.PatchAntialiasing && (strcmp(apThis->pName, "Antialiasing") == 0 || strcmp(apThis->pName, "ScreenSpaceReflection") == 0)) { if (apThis->SetBool(false)) - spdlog::info("Disabled hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); + Log::Info("Disabled hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); else - spdlog::warn("Failed to disable hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); + Log::Warn("Failed to disable hidden setting \"{}/{}\"", apThis->pCategory, apThis->pName); } return RealGameOptionInit(apThis); @@ -51,8 +51,8 @@ void OptionsInitHook(const Image* apImage) MH_CreateHook(GameOptionInit, &HookGameOptionInit, reinterpret_cast(&RealGameOptionInit)); MH_EnableHook(GameOptionInit); - spdlog::info("Hidden options hook: success"); + Log::Info("Hidden options hook: success"); } else - spdlog::warn("Hidden options hook: failed"); + Log::Warn("Hidden options hook: failed"); } diff --git a/src/patches/RemovePeds.cpp b/src/patches/RemovePeds.cpp index 3bad4b3e..cc94f20b 100644 --- a/src/patches/RemovePeds.cpp +++ b/src/patches/RemovePeds.cpp @@ -11,7 +11,7 @@ void RemovePedsPatch(const Image* apImage) if(pLocation == nullptr) { - spdlog::warn("Remove peds patch: failed, could not be found"); + Log::Warn("Remove peds patch: failed, could not be found"); return; } @@ -21,5 +21,5 @@ void RemovePedsPatch(const Image* apImage) pLocation[10] = 0xE9; VirtualProtect(pLocation, 32, oldProtect, nullptr); - spdlog::info("Remove peds patch: success"); + Log::Info("Remove peds patch: success"); } diff --git a/src/patches/SkipStartScreen.cpp b/src/patches/SkipStartScreen.cpp index 7fed8b45..54afee8d 100644 --- a/src/patches/SkipStartScreen.cpp +++ b/src/patches/SkipStartScreen.cpp @@ -10,7 +10,7 @@ void StartScreenPatch(const Image* apImage) if(pLocation == nullptr) { - spdlog::warn("Start screen patch: failed, could not be found"); + Log::Warn("Start screen patch: failed, could not be found"); return; } @@ -22,5 +22,5 @@ void StartScreenPatch(const Image* apImage) pLocation[1] = 0x90; VirtualProtect(pLocation, 32, oldProtect, nullptr); - spdlog::info("Start screen patch: success"); + Log::Info("Start screen patch: success"); } diff --git a/src/patches/Smt.cpp b/src/patches/Smt.cpp index 45e8100b..f0d3f57f 100644 --- a/src/patches/Smt.cpp +++ b/src/patches/Smt.cpp @@ -10,7 +10,7 @@ void SmtAmdPatch(const Image* apImage) if (pLocation == nullptr) { - spdlog::warn("AMD SMT Patch: failed"); + Log::Warn("AMD SMT Patch: failed"); return; } @@ -19,5 +19,5 @@ void SmtAmdPatch(const Image* apImage) pLocation[0] = 0x74; VirtualProtect(pLocation, 8, oldProtect, nullptr); - spdlog::info("AMD SMT Patch: success"); + Log::Info("AMD SMT Patch: success"); } diff --git a/src/reverse/Addresses.hpp b/src/reverse/Addresses.hpp new file mode 100644 index 00000000..5bb0252c --- /dev/null +++ b/src/reverse/Addresses.hpp @@ -0,0 +1,23 @@ +#pragma once + +/* + * This file is generated. DO NOT modify it! + * + * Add new patterns in "patterns.py" file located in "project_root/scripts" and run "find_patterns.py". + * The new file should be located in "idb_path/Addresses.hpp". + */ +#include + +// Addresses for Cyberpunk 2077, version 1.31. +namespace CyberEngineTweaks::Addresses +{ +constexpr uintptr_t ImageBase = 0x140000000; + +#pragma region CRenderGlobal +constexpr uintptr_t CRenderGlobal_InstanceOffset = 0x144C91230 - ImageBase; // 48 8B 05 ? ? ? ? 8B D1 48 8B 88 18 8A 5A 01, expected: 1, index: 0, offset: 3 +#pragma endregion + +#pragma region CRenderNode_Present +constexpr uintptr_t CRenderNode_Present_DoInternal = 0x142D439E0 - 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 +} // namespace CyberEngineTweaks::Addresses diff --git a/src/reverse/RenderContext.cpp b/src/reverse/RenderContext.cpp new file mode 100644 index 00000000..8a2c7b28 --- /dev/null +++ b/src/reverse/RenderContext.cpp @@ -0,0 +1,10 @@ +#include + +#include "RenderContext.h" +#include "Addresses.hpp" + +RenderContext* RenderContext::GetInstance() noexcept +{ + static RED4ext::RelocPtr s_instance(CyberEngineTweaks::Addresses::CRenderGlobal_InstanceOffset); + return *s_instance.GetAddr(); +} diff --git a/src/reverse/RenderContext.h b/src/reverse/RenderContext.h new file mode 100644 index 00000000..f2491939 --- /dev/null +++ b/src/reverse/RenderContext.h @@ -0,0 +1,24 @@ +#pragma once + +struct RenderContext +{ + struct Device + { + IDXGISwapChain* pSwapChain; + uint8_t pad8[0xB8 - 0x8]; + }; + + RenderContext() = delete; + ~RenderContext() = delete; + + static RenderContext* GetInstance() noexcept; + + uint8_t pad0[0xC97F38]; + Device devices[0x30]; // Count unknown, it is at least 0x20 + uint8_t pad[0xED65C0 - 0xC9A1B8]; + void* unkED65C0; // Some pointer, might be related to d3d12on7, I have never seen it being anything other than null +}; + +static_assert(sizeof(RenderContext::Device) == 0xB8); +static_assert(offsetof(RenderContext, devices) == 0xC97F38); +static_assert(offsetof(RenderContext, unkED65C0) == 0xED65C0); diff --git a/src/scripting/FunctionOverride.cpp b/src/scripting/FunctionOverride.cpp index 932f56ca..b6259f6a 100644 --- a/src/scripting/FunctionOverride.cpp +++ b/src/scripting/FunctionOverride.cpp @@ -462,16 +462,16 @@ void FunctionOverride::Hook(Options& aOptions) const const mem::default_scanner cScanner(cPattern); RealRunPureScriptFunction = cScanner(gameImage.TextRegion).as(); if (!RealRunPureScriptFunction) - spdlog::error("Could not find pure run script function!"); + Log::Error("Could not find pure run script function!"); else { auto* pLocation = RealRunPureScriptFunction; if (MH_CreateHook(pLocation, &FunctionOverride::HookRunPureScriptFunction, reinterpret_cast(&RealRunPureScriptFunction)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - spdlog::error("Could not hook RealRunScriptFunction function!"); + Log::Error("Could not hook RealRunScriptFunction function!"); else - spdlog::info("RealRunScriptFunction function hook complete!"); + Log::Info("RealRunScriptFunction function hook complete!"); } } @@ -493,10 +493,10 @@ void FunctionOverride::Hook(Options& aOptions) const *pFirstLocation = *pSecondLocation = std::max(s_cMaxFunctionSize, sizeof(RED4ext::CScriptedFunction)); VirtualProtect(pLocation, 0x40, oldProtect, &oldProtect); - spdlog::info("Override function allocator patched!"); + Log::Info("Override function allocator patched!"); } else - spdlog::error("Could not fix allocator for override functions!"); + Log::Error("Could not fix allocator for override functions!"); } } } diff --git a/src/scripting/GameDump.cpp b/src/scripting/GameDump.cpp index 5a7fbb51..e1afb07d 100644 --- a/src/scripting/GameDump.cpp +++ b/src/scripting/GameDump.cpp @@ -83,7 +83,7 @@ void DumpVTablesTask::Run() for (auto& [key, value] : vtableMap) { - spdlog::info("{:016X},{}", key, value); + Log::Info("{:016X},{}", key, value); } } } // namespace GameDump diff --git a/src/scripting/GameHooks.cpp b/src/scripting/GameHooks.cpp index 1bb4358a..bd098cb2 100644 --- a/src/scripting/GameHooks.cpp +++ b/src/scripting/GameHooks.cpp @@ -62,9 +62,9 @@ void GameMainThread::Hook() { if (MH_CreateHook(m_pMainThreadLocation, &GameMainThread::HookMainThread, reinterpret_cast(&m_pMainThreadOriginal)) != MH_OK || MH_EnableHook(m_pMainThreadLocation) != MH_OK) - spdlog::error("Could not hook main thread function!"); + Log::Error("Could not hook main thread function!"); else - spdlog::info("Main thread function hook complete!"); + Log::Info("Main thread function hook complete!"); } } diff --git a/src/scripting/GameOptions.cpp b/src/scripting/GameOptions.cpp index 25843caf..786a8fae 100644 --- a/src/scripting/GameOptions.cpp +++ b/src/scripting/GameOptions.cpp @@ -310,7 +310,7 @@ void GameOptions::Toggle(const std::string& category, const std::string& name) void GameOptions::Dump() { for (auto option : s_gameOptions) - spdlog::info(option->GetInfo()); + Log::Info(option->GetInfo()); spdlog::get("scripting")->info("Dumped {} options to cyber_engine_tweaks.log", s_gameOptions.size()); } diff --git a/src/scripting/LuaVM_Hooks.cpp b/src/scripting/LuaVM_Hooks.cpp index 6b106fd8..38f08a5c 100644 --- a/src/scripting/LuaVM_Hooks.cpp +++ b/src/scripting/LuaVM_Hooks.cpp @@ -172,9 +172,9 @@ void LuaVM::Hook(Options& aOptions) { if (MH_CreateHook(pLocation, &HookLog, reinterpret_cast(&m_realLog)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - spdlog::error("Could not hook Log function!"); + Log::Error("Could not hook Log function!"); else - spdlog::info("Log function hook complete!"); + Log::Info("Log function hook complete!"); } } @@ -187,9 +187,9 @@ void LuaVM::Hook(Options& aOptions) { if (MH_CreateHook(pLocation, &HookLogChannel, reinterpret_cast(&m_realLogChannel)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - spdlog::error("Could not hook LogChannel function!"); + Log::Error("Could not hook LogChannel function!"); else - spdlog::info("LogChannel function hook complete!"); + Log::Info("LogChannel function hook complete!"); } } @@ -203,9 +203,9 @@ void LuaVM::Hook(Options& aOptions) if (MH_CreateHook(pLocation, &HookTDBIDCtorDerive, reinterpret_cast(&m_realTDBIDCtorDerive)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - spdlog::error("Could not hook TDBID::ctor[Derive] function!"); + Log::Error("Could not hook TDBID::ctor[Derive] function!"); else - spdlog::info("TDBID::ctor[Derive] function hook complete!"); + Log::Info("TDBID::ctor[Derive] function hook complete!"); } } @@ -219,10 +219,10 @@ void LuaVM::Hook(Options& aOptions) if (MH_CreateHook(pLocation, &HookRunningStateRun, reinterpret_cast(&m_realRunningStateRun)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - spdlog::error("Could not hook RunningState::Run function!"); + Log::Error("Could not hook RunningState::Run function!"); else { - spdlog::info("RunningState::Run function hook complete!"); + Log::Info("RunningState::Run function hook complete!"); } } } @@ -246,9 +246,9 @@ void LuaVM::Hook(Options& aOptions) if (MH_CreateHook(pLocation, &HookTDBIDToStringDEBUG, reinterpret_cast(&m_realTDBIDToStringDEBUG)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - spdlog::error("Could not hook TDBID::ToStringDEBUG function!"); + Log::Error("Could not hook TDBID::ToStringDEBUG function!"); else - spdlog::info("TDBID::ToStringDEBUG function hook complete!"); + Log::Info("TDBID::ToStringDEBUG function hook complete!"); } } } @@ -262,10 +262,10 @@ void LuaVM::Hook(Options& aOptions) { if (MH_CreateHook(pLocation, &HookTweakDBLoad, reinterpret_cast(&m_realTweakDBLoad)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) - spdlog::error("Could not hook TweakDB::Load function!"); + Log::Error("Could not hook TweakDB::Load function!"); else { - spdlog::info("TweakDB::Load function hook complete!"); + Log::Info("TweakDB::Load function hook complete!"); } } } @@ -281,10 +281,10 @@ void LuaVM::Hook(Options& aOptions) // { // if (MH_CreateHook(pLocation, &HookSetLoadingState, reinterpret_cast(&m_realSetLoadingState)) != MH_OK // || MH_EnableHook(pLocation) != MH_OK) - // spdlog::error("Could not hook SetLoadingState function!"); + // Log::Error("Could not hook SetLoadingState function!"); // else // { - // spdlog::info("SetLoadingState function hook complete!"); + // Log::Info("SetLoadingState function hook complete!"); // } // } //} diff --git a/src/scripting/Scripting.cpp b/src/scripting/Scripting.cpp index 7e76ff47..c13385cf 100644 --- a/src/scripting/Scripting.cpp +++ b/src/scripting/Scripting.cpp @@ -539,7 +539,7 @@ void Scripting::PostInitializeStage2() uint32_t count = 0; pRtti->types.for_each([&count](RED4ext::CName name, RED4ext::CBaseRTTIType*& type) { - spdlog::info(name.ToString()); + Log::Info(name.ToString()); count++; }); const sol::environment cEnv = aThisEnv; diff --git a/src/stdafx.h b/src/stdafx.h index 6c002d21..63c0b10d 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -60,3 +61,4 @@ #include "VKBindings.h" #include "Options.h" #include "CETVersion.h" +#include "common/Logging.h" \ No newline at end of file diff --git a/src/window/window.cpp b/src/window/window.cpp index e6889890..a67dfeb5 100644 --- a/src/window/window.cpp +++ b/src/window/window.cpp @@ -82,7 +82,7 @@ Window::Window(Overlay* apOverlay, VKBindings* apBindings, D3D12* apD3D12) { m_wndProc = reinterpret_cast( SetWindowLongPtr(m_hWnd, GWLP_WNDPROC, reinterpret_cast(WndProc))); - spdlog::info("Window::Initialize() - window hook complete."); + Log::Info("Window::Initialize() - window hook complete."); } } diff --git a/xmake.lua b/xmake.lua index d4ee00b6..cae51857 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,6 +1,6 @@ -set_xmakever("2.5.3") +set_xmakever("2.6.0") -set_languages("cxx20") +set_languages("c++20") set_arch("x64") add_requires("spdlog", "nlohmann_json", "hopscotch-map", "minhook", "mem", "imgui 1.84.2", "sol2", "tiltedcore 0.2.3", "sqlite3", "luajit") @@ -12,6 +12,7 @@ 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") if is_mode("debug") then add_defines("CET_DEBUG")