From 4e2b637d51350063a7b633bfc28e05cde40bdd24 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Mon, 16 Mar 2026 16:24:09 -0400 Subject: [PATCH 1/3] Cleanup whitespace and formatting --- SharpPluginLoader.Core/IPlugin.cs | 8 +-- mhw-cs-plugin-loader/D3DModule.cpp | 53 +++++++++---------- .../NativePluginFramework.cpp | 2 +- mhw-cs-plugin-loader/NativePluginFramework.h | 2 +- .../PrimitiveRenderingModule.cpp | 36 ++++++------- 5 files changed, 49 insertions(+), 52 deletions(-) diff --git a/SharpPluginLoader.Core/IPlugin.cs b/SharpPluginLoader.Core/IPlugin.cs index 0b29c47..40863a5 100644 --- a/SharpPluginLoader.Core/IPlugin.cs +++ b/SharpPluginLoader.Core/IPlugin.cs @@ -52,7 +52,7 @@ public class PluginData internal bool OnQuestDepart; /// - internal bool OnQuestEnter; + internal bool OnQuestEnter; /// internal bool OnQuestLeave; @@ -107,7 +107,7 @@ public class PluginData #region Entity /// internal bool OnEntityAction; - + /// internal bool OnEntityAnimation; @@ -159,7 +159,7 @@ public interface IPlugin /// /// Gets called when the plugin is loaded. This is where you can optionally configure your plugin within the framework. - /// + /// /// Default event, always called once per plugin [re]load. /// /// The filled out PluginData @@ -168,7 +168,7 @@ public interface IPlugin /// /// Gets called after the game has initialized it's singletons. This is you initialize anything in your plugin /// that uses the game state (e.g. reading pointers, accessing singletons, etc). - /// + /// /// Default event, always called once per plugin [re]load. /// public void OnLoad() { } // Not marked as a plugin event because it's always called diff --git a/mhw-cs-plugin-loader/D3DModule.cpp b/mhw-cs-plugin-loader/D3DModule.cpp index e88ad29..cbed5ac 100644 --- a/mhw-cs-plugin-loader/D3DModule.cpp +++ b/mhw-cs-plugin-loader/D3DModule.cpp @@ -39,7 +39,7 @@ void D3DModule::initialize(CoreClr* coreclr) { L"SharpPluginLoader.Core.Rendering.Renderer", L"Render" ); - m_core_imgui_render = coreclr->get_method( + m_core_imgui_render = coreclr->get_method( config::SPL_CORE_ASSEMBLY_NAME, L"SharpPluginLoader.Core.Rendering.Renderer", L"ImGuiRender" @@ -268,7 +268,7 @@ void D3DModule::initialize_for_d3d12() { const auto resize_buffers = swap_chain_vft[13]; const auto execute_command_lists = command_queue_vft[10]; const auto signal = command_queue_vft[14]; - + m_d3d_present_hook = safetyhook::create_inline(present, d3d12_present_hook); m_d3d_execute_command_lists_hook = safetyhook::create_inline(execute_command_lists, d3d12_execute_command_lists_hook); m_d3d_signal_hook = safetyhook::create_inline(signal, d3d12_signal_hook); @@ -332,12 +332,12 @@ void D3DModule::initialize_for_d3d11() { ID3D11DeviceContext* device_context; if (FAILED(d3d11_create_device_and_swap_chain( - nullptr, - D3D_DRIVER_TYPE_HARDWARE, - nullptr, 0, - feature_levels, - _countof(feature_levels), - D3D11_SDK_VERSION, + nullptr, + D3D_DRIVER_TYPE_HARDWARE, + nullptr, 0, + feature_levels, + _countof(feature_levels), + D3D11_SDK_VERSION, &sd, &swap_chain, &device, &feature_level, &device_context))) { dlog::error("Failed to create D3D11 device and swap chain"); return; @@ -482,11 +482,11 @@ void D3DModule::d3d12_initialize_imgui(IDXGISwapChain* swap_chain) { GetClientRect(desc.OutputWindow, &client_rect); const MtSize viewport_size = { desc.BufferDesc.Width, desc.BufferDesc.Height }; - const MtSize window_size = { - (u32)(client_rect.right - client_rect.left), - (u32)(client_rect.bottom - client_rect.top) + const MtSize window_size = { + (u32)(client_rect.right - client_rect.left), + (u32)(client_rect.bottom - client_rect.top) }; - + const auto& config = preloader::LoaderConfig::get(); const auto context = m_core_initialize_imgui(viewport_size, window_size, true, config.get_menu_key().c_str()); @@ -525,7 +525,7 @@ void D3DModule::d3d12_initialize_imgui(IDXGISwapChain* swap_chain) { m_d3d12_frame_contexts[i].CommandAllocator = command_allocator; } - if (FAILED(m_d3d12_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, + if (FAILED(m_d3d12_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, command_allocator.Get(), nullptr, IID_PPV_ARGS(m_d3d12_command_list.GetAddressOf())))) { dlog::error("Failed to create D3D12 command list"); return; @@ -560,7 +560,7 @@ void D3DModule::d3d12_initialize_imgui(IDXGISwapChain* swap_chain) { const auto buffer_desc = back_buffer->GetDesc(); dlog::debug("Creating RTV for back buffer {}, with size {}x{}", i, buffer_desc.Width, buffer_desc.Height); - + m_d3d12_device->CreateRenderTargetView(back_buffer.Get(), nullptr, rtv_handle); m_d3d12_frame_contexts[i].RenderTargetDescriptor = rtv_handle; m_d3d12_frame_contexts[i].RenderTarget = back_buffer; @@ -615,9 +615,9 @@ void D3DModule::d3d11_initialize_imgui(IDXGISwapChain* swap_chain) { GetClientRect(desc.OutputWindow, &client_rect); const MtSize viewport_size = { desc.BufferDesc.Width, desc.BufferDesc.Height }; - const MtSize window_size = { - (u32)(client_rect.right - client_rect.left), - (u32)(client_rect.bottom - client_rect.top) + const MtSize window_size = { + (u32)(client_rect.right - client_rect.left), + (u32)(client_rect.bottom - client_rect.top) }; const auto& config = preloader::LoaderConfig::get(); @@ -772,14 +772,14 @@ HRESULT D3DModule::d3d12_present_hook(IDXGISwapChain* swap_chain, UINT sync_inte } self->m_is_inside_present = true; - + if (!self->m_is_initialized) { self->d3d12_initialize_imgui(swap_chain); - + if (!self->m_texture_manager) { self->m_texture_manager = std::make_unique( - self->m_d3d12_device, - self->m_d3d12_command_queue, + self->m_d3d12_device, + self->m_d3d12_command_queue, self->m_d3d12_srv_heap ); } @@ -856,8 +856,7 @@ void D3DModule::d3d12_present_hook_core(IDXGISwapChain* swap_chain, const std::s m_d3d12_command_queue->ExecuteCommandLists(1, (ID3D12CommandList* const*)m_d3d12_command_list.GetAddressOf()); - if (igGetIO()->ConfigFlags & ImGuiConfigFlags_ViewportsEnable) - { + if (igGetIO()->ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { igUpdatePlatformWindows(); igRenderPlatformWindowsDefault(nullptr, m_d3d12_command_list.Get()); } @@ -899,8 +898,7 @@ HRESULT D3DModule::d3d_resize_buffers_hook(IDXGISwapChain* swap_chain, UINT buff self->m_is_initialized = false; if (self->m_is_d3d12) { self->d3d12_deinitialize_imgui(); - } - else { + } else { self->d3d11_deinitialize_imgui(); } } @@ -927,7 +925,7 @@ HRESULT D3DModule::d3d11_present_hook(IDXGISwapChain* swap_chain, UINT sync_inte if (!self->m_texture_manager) { self->m_texture_manager = std::make_unique(self->m_d3d11_device, self->m_d3d11_device_context); } - + if (config.get_primitive_rendering_enabled()) { prm->late_init(self.get(), swap_chain); } @@ -956,8 +954,7 @@ void D3DModule::d3d11_present_hook_core(IDXGISwapChain* swap_chain, const std::s ImGui_ImplDX11_RenderDrawData(draw_data); - if (igGetIO()->ConfigFlags & ImGuiConfigFlags_ViewportsEnable) - { + if (igGetIO()->ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { igUpdatePlatformWindows(); igRenderPlatformWindowsDefault(nullptr, nullptr); } diff --git a/mhw-cs-plugin-loader/NativePluginFramework.cpp b/mhw-cs-plugin-loader/NativePluginFramework.cpp index 066a078..908cb5d 100644 --- a/mhw-cs-plugin-loader/NativePluginFramework.cpp +++ b/mhw-cs-plugin-loader/NativePluginFramework.cpp @@ -64,7 +64,7 @@ const char* NativePluginFramework::get_game_revision() { if (s_instance->m_game_revision != nullptr) { return s_instance->m_game_revision; } - + const auto pattern = Pattern::from_string("48 83 EC 48 48 8B 05 ? ? ? ? 4C 8D 0D ? ? ? ? BA 0A 00 00 00"); const auto func = PatternScanner::find_first(pattern); diff --git a/mhw-cs-plugin-loader/NativePluginFramework.h b/mhw-cs-plugin-loader/NativePluginFramework.h index 4b12762..0b05dfc 100644 --- a/mhw-cs-plugin-loader/NativePluginFramework.h +++ b/mhw-cs-plugin-loader/NativePluginFramework.h @@ -32,7 +32,7 @@ class NativePluginFramework { void trigger_on_pre_main(); void trigger_on_win_main(); void trigger_on_mh_main_ctor(); - + static void run_compatibility_checks(); static uintptr_t get_repository_address(const char* name); static const char* get_game_revision(); diff --git a/mhw-cs-plugin-loader/PrimitiveRenderingModule.cpp b/mhw-cs-plugin-loader/PrimitiveRenderingModule.cpp index e9e9c41..1defe57 100644 --- a/mhw-cs-plugin-loader/PrimitiveRenderingModule.cpp +++ b/mhw-cs-plugin-loader/PrimitiveRenderingModule.cpp @@ -105,8 +105,8 @@ void PrimitiveRenderingModule::render_primitives_for_d3d11(ID3D11DeviceContext* m_capsules = std::span(capsules, m_capsule_count); m_lines = std::span(lines, m_line_count); - //if (m_spheres.empty() && - // m_cubes.empty() && + //if (m_spheres.empty() && + // m_cubes.empty() && // m_capsules.empty() && // m_lines.empty()) { // return; @@ -292,15 +292,15 @@ void PrimitiveRenderingModule::render_primitives_for_d3d11(ID3D11DeviceContext* -XMVectorGetY(v), -XMVectorGetX(v), 0, 0, 0, 0, 0, 0 }; const XMMATRIX vx2 = XMMatrixMultiply(vx, vx); - + rotation = XMMatrixAdd(XMMatrixAdd(XMMatrixIdentity(), vx), vx2 * (1.0f / (1.0f + c))); } // Scale const XMMATRIX scale_hemisphere = XMMatrixScaling(capsule.capsule.r, capsule.capsule.r, capsule.capsule.r); const XMMATRIX scale_cylinder = XMMatrixScaling( - capsule.capsule.r, - XMVectorGetX(XMVector3Length(p1 - p0)) * 0.5f, + capsule.capsule.r, + XMVectorGetX(XMVector3Length(p1 - p0)) * 0.5f, capsule.capsule.r ); @@ -388,7 +388,7 @@ void PrimitiveRenderingModule::render_primitives_for_d3d11(ID3D11DeviceContext* const auto params = (LineParams*)msr.pData; params->Thickness = m_line_thickness; context->Unmap(m_d3d11_line_params_buffer.Get(), 0); - + const std::array constant_buffers = { m_d3d11_viewproj_buffer.Get(), m_d3d11_line_params_buffer.Get() @@ -451,7 +451,7 @@ void PrimitiveRenderingModule::render_primitives_for_d3d12(IDXGISwapChain3* swap m_capsules = std::span(capsules, m_capsule_count); m_lines = std::span(lines, m_line_count); - //if (m_spheres.empty() && + //if (m_spheres.empty() && // m_cubes.empty() && // m_capsules.empty() && // m_lines.empty()) { @@ -465,7 +465,7 @@ void PrimitiveRenderingModule::render_primitives_for_d3d12(IDXGISwapChain3* swap } // Set up common pipeline state - + const FrameContext& frame_context = m_d3d12_frame_contexts[swap_chain->GetCurrentBackBufferIndex()]; HandleResult(m_d3d12_command_allocator->Reset()); @@ -487,9 +487,9 @@ void PrimitiveRenderingModule::render_primitives_for_d3d12(IDXGISwapChain3* swap m_d3d12_command_list->RSSetViewports(1, &m_d3d12_viewport); m_d3d12_command_list->RSSetScissorRects(1, &m_d3d12_scissor_rect); m_d3d12_command_list->OMSetRenderTargets( - 1, - &frame_context.RenderTargetDescriptor, - false, + 1, + &frame_context.RenderTargetDescriptor, + false, &m_d3d12_depth_stencil_view ); @@ -524,8 +524,8 @@ void PrimitiveRenderingModule::render_primitives_for_d3d12(IDXGISwapChain3* swap // Spheres ------------------------------ if (m_sphere_count != 0) { // Build Instance Data - const D3D12_RANGE range{ - 0, + const D3D12_RANGE range{ + 0, sizeof(Instance) * std::min((u32)m_sphere_count, MAX_INSTANCES) }; Instance* data = nullptr; @@ -823,7 +823,7 @@ void PrimitiveRenderingModule::late_init_d3d11(D3DModule* d3dmodule) { HandleResult(d3dmodule->m_d3d11_device->CreateBuffer(&bd, nullptr, m_d3d11_transform_buffer.GetAddressOf())); HandleResult(d3dmodule->m_d3d11_device->CreateBuffer(&bd, nullptr, m_d3d11_htop_transform_buffer.GetAddressOf())); HandleResult(d3dmodule->m_d3d11_device->CreateBuffer(&bd, nullptr, m_d3d11_hbottom_transform_buffer.GetAddressOf())); - + // Create Line Vertex Buffer bd.ByteWidth = sizeof LineVertex * MAX_LINES * 2; bd.Usage = D3D11_USAGE_DYNAMIC; @@ -1047,7 +1047,7 @@ void PrimitiveRenderingModule::late_init_d3d12(D3DModule* d3dmodule, IDXGISwapCh ComPtr signature_blob; ComPtr error_blob; - + const auto serialize_root_signature = (decltype(D3D12SerializeRootSignature)*)GetProcAddress( d3dmodule->m_d3d12_module, "D3D12SerializeRootSignature" ); @@ -1163,7 +1163,7 @@ void PrimitiveRenderingModule::late_init_d3d12(D3DModule* d3dmodule, IDXGISwapCh depth_stencil_desc.StencilEnable = false; // Blend State - + D3D12_BLEND_DESC blend_desc{}; blend_desc.IndependentBlendEnable = false; blend_desc.RenderTarget[0].BlendEnable = true; @@ -1174,7 +1174,7 @@ void PrimitiveRenderingModule::late_init_d3d12(D3DModule* d3dmodule, IDXGISwapCh blend_desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ZERO; blend_desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; - + // Pipeline State const auto sc3 = (IDXGISwapChain3*)swap_chain; DXGI_SWAP_CHAIN_DESC swap_chain_desc{}; @@ -1380,7 +1380,7 @@ void PrimitiveRenderingModule::late_init_d3d12(D3DModule* d3dmodule, IDXGISwapCh depth_clear_value.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depth_clear_value.DepthStencil.Depth = 1.0f; depth_clear_value.DepthStencil.Stencil = 0; - + const auto default_heap_properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); HandleResult(d3dmodule->m_d3d12_device->CreateCommittedResource( &default_heap_properties, From 298993277377d5a74180365410cbae6e98f5a631 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Tue, 17 Mar 2026 17:40:19 -0400 Subject: [PATCH 2/3] Refactor D3DModule --- Assets/Common/AddressRecords.json | 5 + mhw-cs-plugin-loader/D3DModule.cpp | 479 +++--------------- mhw-cs-plugin-loader/D3DModule.h | 21 +- .../PrimitiveRenderingModule.cpp | 12 +- 4 files changed, 89 insertions(+), 428 deletions(-) diff --git a/Assets/Common/AddressRecords.json b/Assets/Common/AddressRecords.json index bd11e23..9324e9a 100644 --- a/Assets/Common/AddressRecords.json +++ b/Assets/Common/AddressRecords.json @@ -434,6 +434,11 @@ "Pattern": "48 8b 81 70 02 00 00 48 8b f2 48 8b d9 48 3b c2 74 1e", "Offset": -15 }, + { + "Name": "sMhRender:Assignment", + "Pattern": "89 44 24 20 E8 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? C7 83 ?? ?? ?? ?? 06 00 00 00 48 89 03", + "Offset": 9 + }, { "Name": "D3DRender12:SwapChainPresentCall", "Pattern": "FF 50 40 C6 83 D9 10 00 00 01 85 C0 75 1A 41 FF C4 44 8D 78 01", diff --git a/mhw-cs-plugin-loader/D3DModule.cpp b/mhw-cs-plugin-loader/D3DModule.cpp index cbed5ac..c24bc26 100644 --- a/mhw-cs-plugin-loader/D3DModule.cpp +++ b/mhw-cs-plugin-loader/D3DModule.cpp @@ -1,6 +1,8 @@ #include "D3DModule.h" +#include #include +#include #include "CoreClr.h" #include "Config.h" @@ -14,9 +16,6 @@ #include "imgui_impl_dx11.h" #include "imgui_impl_win32.h" -#include -#include - #include "ChunkModule.h" #include "HResultHandler.h" #include "LoaderConfig.h" @@ -65,8 +64,11 @@ void D3DModule::initialize(CoreClr* coreclr) { L"GetSingletonNative" ); - const auto play = (void*)NativePluginFramework::get_repository_address("GUITitle:Play"); - m_title_menu_ready_hook = safetyhook::create_inline(play, title_menu_ready_hook); + const auto render_assignment = (void*)NativePluginFramework::get_repository_address("sMhRender:Assignment"); + m_create_renderer_hook = safetyhook::create_mid(render_assignment, [](safetyhook::Context& ctx) { + const auto self = NativePluginFramework::get_module(); + self->common_initialize(ctx.rax); + }); coreclr->add_internal_call("LoadTexture", (void*)load_texture); coreclr->add_internal_call("UnloadTexture", (void*)unload_texture); @@ -77,12 +79,11 @@ void D3DModule::shutdown() { m_d3d_present_hook.reset(); if (m_is_d3d12) { - m_d3d_execute_command_lists_hook.reset(); m_d3d_signal_hook.reset(); } } -void D3DModule::common_initialize() { +void D3DModule::common_initialize(const uintptr_t render_singleton) { const uintptr_t callIsD3D12 = PatternScanner::find_first( Pattern::from_string("05 7D 14 00 4C 8B 8D D8 08 00 00 84 C0 0F B6 85 F0 08 00 00") ); @@ -91,8 +92,6 @@ void D3DModule::common_initialize() { m_is_d3d12 = isD3D12(); dlog::debug("Found cD3DRender::isD3D12 at {:p}", (void*)isD3D12); - m_title_menu_ready_hook.reset(); - dlog::debug("Initializing D3D module for {}", m_is_d3d12 ? "D3D12" : "D3D11"); const auto game_window_name = std::format("MONSTER HUNTER: WORLD({})", NativePluginFramework::get_game_revision()); @@ -130,7 +129,7 @@ void D3DModule::common_initialize() { TEXT("SharpPluginLoader DX Hook"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, - 100, 100, + 0, 0, nullptr, nullptr, window_class->hInstance, @@ -142,336 +141,53 @@ void D3DModule::common_initialize() { return; } + const auto renderer = *(uintptr_t*)(render_singleton + 0x78); if (m_is_d3d12) { - initialize_for_d3d12_alt(); + initialize_for_d3d12(renderer); } else { - initialize_for_d3d11_alt(); + initialize_for_d3d11(renderer); } DestroyWindow(m_temp_window); UnregisterClass(window_class->lpszClassName, window_class->hInstance); } -void D3DModule::initialize_for_d3d12() { - HMODULE dxgi; - - if ((dxgi = GetModuleHandleA("dxgi.dll")) == nullptr) { - dlog::error("Failed to find dxgi.dll"); - return; - } - - if ((m_d3d12_module = GetModuleHandleA("d3d12.dll")) == nullptr) { - dlog::error("Failed to find d3d12.dll"); - return; - } - - decltype(CreateDXGIFactory)* create_dxgi_factory; - if ((create_dxgi_factory = (decltype(create_dxgi_factory))GetProcAddress(dxgi, "CreateDXGIFactory")) == nullptr) { - dlog::error("Failed to find CreateDXGIFactory"); - return; - } - - IDXGIFactory* factory; - if (FAILED(create_dxgi_factory(IID_PPV_ARGS(&factory)))) { - dlog::error("Failed to create DXGI factory"); - return; - } - - IDXGIAdapter* adapter; - if (FAILED(factory->EnumAdapters(0, &adapter))) { - dlog::error("Failed to enumerate DXGI adapters"); - return; - } - - decltype(D3D12CreateDevice)* d3d12_create_device; - if ((d3d12_create_device = (decltype(d3d12_create_device))GetProcAddress(m_d3d12_module, "D3D12CreateDevice")) == nullptr) { - dlog::error("Failed to find D3D12CreateDevice"); - return; - } - - ID3D12Device* device; - if (FAILED(d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device)))) { - dlog::error("Failed to create D3D12 device"); - return; - } - - constexpr D3D12_COMMAND_QUEUE_DESC queue_desc = { - .Type = D3D12_COMMAND_LIST_TYPE_DIRECT, - .Priority = 0, - .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE, - .NodeMask = 0 - }; - - ID3D12CommandQueue* command_queue; - if (FAILED(device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&command_queue)))) { - dlog::error("Failed to create D3D12 command queue"); - return; - } - - ID3D12CommandAllocator* command_allocator; - if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&command_allocator)))) { - dlog::error("Failed to create D3D12 command allocator"); - return; - } - - ID3D12CommandList* command_list; - if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, command_allocator, nullptr, IID_PPV_ARGS(&command_list)))) { - dlog::error("Failed to create D3D12 command list"); - return; - } - - constexpr DXGI_RATIONAL refresh_rate = { - .Numerator = 60, - .Denominator = 1 - }; - - DXGI_MODE_DESC buffer_desc = { - .Width = 100, - .Height = 100, - .RefreshRate = refresh_rate, - .Format = DXGI_FORMAT_R8G8B8A8_UNORM, - .ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, - .Scaling = DXGI_MODE_SCALING_UNSPECIFIED - }; - - constexpr DXGI_SAMPLE_DESC sample_desc = { - .Count = 1, - .Quality = 0 - }; - - DXGI_SWAP_CHAIN_DESC sd = { - .BufferDesc = buffer_desc, - .SampleDesc = sample_desc, - .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT, - .BufferCount = 2, - .OutputWindow = m_temp_window, - .Windowed = TRUE, - .SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD, - .Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH - }; - - IDXGISwapChain* swap_chain; - if (FAILED(factory->CreateSwapChain(command_queue, &sd, &swap_chain))) { - dlog::error("Failed to create DXGI swap chain"); +void D3DModule::initialize_for_d3d12(const uintptr_t renderer) { + const auto swap_chain = *(IDXGISwapChain**)(renderer + 0x1470); + m_d3d12_command_queue = *(ID3D12CommandQueue**)(renderer + 0x20); + dlog::debug("D3D12 Command Queue found at {:p}", (void*)m_d3d12_command_queue); + if (FAILED(swap_chain->GetDevice(IID_PPV_ARGS(&m_d3d12_device)))) { + dlog::error("Failed to get D3D12 device during initialization"); return; } - // SwapChainVFT[8]: Present - // SwapChainVFT[13]: ResizeBuffers - // CommandQueueVFT[10]: ExecuteCommandLists - // CommandQueueVFT[14]: Signal - const auto swap_chain_vft = *(void***)swap_chain; - const auto command_queue_vft = *(void***)command_queue; + const auto cmd_queue_vft = *(void***)m_d3d12_command_queue; const auto present = swap_chain_vft[8]; const auto resize_buffers = swap_chain_vft[13]; - const auto execute_command_lists = command_queue_vft[10]; - const auto signal = command_queue_vft[14]; + const auto signal = cmd_queue_vft[14]; m_d3d_present_hook = safetyhook::create_inline(present, d3d12_present_hook); - m_d3d_execute_command_lists_hook = safetyhook::create_inline(execute_command_lists, d3d12_execute_command_lists_hook); - m_d3d_signal_hook = safetyhook::create_inline(signal, d3d12_signal_hook); m_d3d_resize_buffers_hook = safetyhook::create_inline(resize_buffers, d3d_resize_buffers_hook); - - device->Release(); - command_queue->Release(); - command_allocator->Release(); - command_list->Release(); - swap_chain->Release(); - factory->Release(); + m_d3d_signal_hook = safetyhook::create_inline(signal, d3d12_signal_hook); } -void D3DModule::initialize_for_d3d11() { - if ((m_d3d11_module = GetModuleHandleA("d3d11.dll")) == nullptr) { - dlog::error("Failed to find d3d11.dll"); - return; - } - - decltype(D3D11CreateDeviceAndSwapChain)* d3d11_create_device_and_swap_chain; - if ((d3d11_create_device_and_swap_chain = (decltype(d3d11_create_device_and_swap_chain))GetProcAddress(m_d3d11_module, "D3D11CreateDeviceAndSwapChain")) == nullptr) { - dlog::error("Failed to find D3D11CreateDeviceAndSwapChain"); - return; - } - - constexpr D3D_FEATURE_LEVEL feature_levels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1 }; - D3D_FEATURE_LEVEL feature_level; - - constexpr DXGI_RATIONAL refresh_rate = { - .Numerator = 60, - .Denominator = 1 - }; - - constexpr DXGI_MODE_DESC buffer_desc = { - .Width = 100, - .Height = 100, - .RefreshRate = refresh_rate, - .Format = DXGI_FORMAT_R8G8B8A8_UNORM, - .ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, - .Scaling = DXGI_MODE_SCALING_UNSPECIFIED - }; - - constexpr DXGI_SAMPLE_DESC sample_desc = { - .Count = 1, - .Quality = 0 - }; - - DXGI_SWAP_CHAIN_DESC sd = { - .BufferDesc = buffer_desc, - .SampleDesc = sample_desc, - .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT, - .BufferCount = 1, - .OutputWindow = m_temp_window, - .Windowed = TRUE, - .SwapEffect = DXGI_SWAP_EFFECT_DISCARD, - .Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH - }; - - IDXGISwapChain* swap_chain; - ID3D11Device* device; - ID3D11DeviceContext* device_context; - - if (FAILED(d3d11_create_device_and_swap_chain( - nullptr, - D3D_DRIVER_TYPE_HARDWARE, - nullptr, 0, - feature_levels, - _countof(feature_levels), - D3D11_SDK_VERSION, - &sd, &swap_chain, &device, &feature_level, &device_context))) { - dlog::error("Failed to create D3D11 device and swap chain"); +void D3DModule::initialize_for_d3d11(const uintptr_t renderer) { + const auto swap_chain = *(IDXGISwapChain**)(renderer + 0x1488); + if (FAILED(swap_chain->GetDevice(IID_PPV_ARGS(&m_d3d11_device)))) { + dlog::error("Failed to get D3D11 device during initialization"); return; } - // SwapChainVFT[8]: Present const auto swap_chain_vft = *(void***)swap_chain; + const auto present = swap_chain_vft[8]; m_d3d_present_hook = safetyhook::create_inline(present, d3d11_present_hook); } -void D3DModule::initialize_for_d3d12_alt() { - const auto present_call = NativePluginFramework::get_repository_address("D3DRender12:SwapChainPresentCall"); - if (!present_call) { - dlog::error("Failed to find SwapChainPresentCall"); - return; - } - - if ((m_d3d12_module = GetModuleHandleA("d3d12.dll")) == nullptr) { - dlog::error("Failed to find d3d12.dll"); - return; - } - - m_d3d_present_hook_alt = safetyhook::create_mid(present_call, [](safetyhook::Context& ctx) { - const auto self = NativePluginFramework::get_module(); - const auto prm = NativePluginFramework::get_module(); - const auto& config = preloader::LoaderConfig::get(); - - const auto swap_chain = (IDXGISwapChain*)ctx.rcx; - - if (self->m_is_inside_present) { - return; - } - - self->m_is_inside_present = true; - - if (!self->m_is_initialized) { - self->d3d12_initialize_imgui(swap_chain); - - if (!self->m_texture_manager) { - self->m_texture_manager = std::make_unique( - self->m_d3d12_device, - self->m_d3d12_command_queue, - self->m_d3d12_srv_heap - ); - } - - if (config.get_primitive_rendering_enabled()) { - prm->late_init(self.get(), swap_chain); - } - } - - if (!self->m_d3d12_command_queue) { - self->m_is_inside_present = false; - return; - } - - const auto facility = (uintptr_t)self->m_get_singleton("sFacility"); - - // Check if Steamworks is active. This is a very hacky fix for the AutoSteamworks app, - // which sometimes sends invalid input events that trip up ImGui. - // So we just disable ImGui rendering when Steamworks is active. Ideally we should - // check if the app is even running, but whatever. This is (probably) a temporary fix. - // +0x348 is the offset to cSteamControl, +0x444 is the offset from that to the mState field. - if (facility && *(u32*)(facility + 0x348 + 0x444) > 5) { - self->m_is_inside_present = false; - return; - } - - self->d3d12_present_hook_core(swap_chain, prm); - self->m_is_inside_present = false; - }); - - const auto render_singleton = (uintptr_t)m_get_singleton("sMhRender"); - const auto renderer = *(uintptr_t*)(render_singleton + 0x78); - - m_d3d12_command_queue = *(ID3D12CommandQueue**)(renderer + 0x20); - const auto swap_chain = *(IDXGISwapChain3**)(renderer + 0x1470); - const auto swap_chain_vft = *(void***)swap_chain; - const auto cmd_queue_vft = *(void***)m_d3d12_command_queue; - - const auto resize_buffers = swap_chain_vft[13]; - const auto signal = cmd_queue_vft[14]; - - dlog::debug("D3D12 Command Queue found at {:p}", (void*)m_d3d12_command_queue); - - m_d3d_resize_buffers_hook = safetyhook::create_inline(resize_buffers, d3d_resize_buffers_hook); - m_d3d_signal_hook = safetyhook::create_inline(signal, d3d12_signal_hook); -} - -void D3DModule::initialize_for_d3d11_alt() { - const auto present_call = NativePluginFramework::get_repository_address("D3DRender11:SwapChainPresentCall"); - if (!present_call) { - dlog::error("Failed to find SwapChainPresentCall"); - return; - } - - m_d3d_present_hook_alt = safetyhook::create_mid(present_call, [](safetyhook::Context& ctx) { - const auto self = NativePluginFramework::get_module(); - const auto prm = NativePluginFramework::get_module(); - const auto& config = preloader::LoaderConfig::get(); - - const auto swap_chain = (IDXGISwapChain*)ctx.rcx; - - if (self->m_is_inside_present) { - return; - } - - self->m_is_inside_present = true; - - if (!self->m_is_initialized) { - self->d3d11_initialize_imgui(swap_chain); - - if (!self->m_texture_manager) { - self->m_texture_manager = std::make_unique(self->m_d3d11_device, self->m_d3d11_device_context); - } - - if (config.get_primitive_rendering_enabled()) { - prm->late_init(self.get(), swap_chain); - } - } - - self->d3d11_present_hook_core(swap_chain, prm); - self->m_is_inside_present = false; - }); -} - void D3DModule::d3d12_initialize_imgui(IDXGISwapChain* swap_chain) { - if (FAILED(swap_chain->GetDevice(IID_PPV_ARGS(&m_d3d12_device)))) { - dlog::error("Failed to get D3D12 device in present hook"); - return; - } - DXGI_SWAP_CHAIN_DESC desc; if (FAILED(swap_chain->GetDesc(&desc))) { dlog::error("Failed to get DXGI swap chain description"); @@ -598,11 +314,6 @@ void D3DModule::d3d12_initialize_imgui(IDXGISwapChain* swap_chain) { } void D3DModule::d3d11_initialize_imgui(IDXGISwapChain* swap_chain) { - if (FAILED(swap_chain->GetDevice(IID_PPV_ARGS(&m_d3d11_device)))) { - dlog::error("Failed to get D3D11 device in present hook"); - return; - } - m_d3d11_device->GetImmediateContext(&m_d3d11_device_context); DXGI_SWAP_CHAIN_DESC desc; @@ -747,66 +458,48 @@ bool D3DModule::is_d3d12() { return m_is_d3d12; } -void D3DModule::title_menu_ready_hook(void* gui) { - const auto self = NativePluginFramework::get_module(); - - std::thread t(&D3DModule::common_initialize, self); - self->m_title_menu_ready_hook.call(gui); - t.join(); - - self->m_title_menu_ready_hook = {}; -} - -template -auto invoke_if(const std::optional>& opt, F func, Args&&... args) { - return opt.and_then(std::bind(func, std::forward(args)...)); -} +/* + uintptr_t facility = 0; + // Check if Steamworks is active. This is a very hacky fix for the AutoSteamworks app, + // which sometimes sends invalid input events that trip up ImGui. + // So we just disable ImGui rendering when Steamworks is active. Ideally we should + // check if the app is even running, but whatever. This is (probably) a temporary fix. + // +0x348 is the offset to cSteamControl, +0x444 is the offset from that to the mState field. + facility = (uintptr_t)self->m_get_singleton("sFacility"); + if (facility && *(u32*)(facility + 0x348 + 0x444) > 5) { + } +*/ HRESULT D3DModule::d3d12_present_hook(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags) { const auto self = NativePluginFramework::get_module(); const auto prm = NativePluginFramework::get_module(); const auto& config = preloader::LoaderConfig::get(); - if (self->m_is_inside_present) { - return self->m_d3d_present_hook.call(swap_chain, sync_interval, flags); - } + if (!self->m_is_inside_present) { + self->m_is_inside_present = true; - self->m_is_inside_present = true; + if (!self->m_is_initialized) { + self->d3d12_initialize_imgui(swap_chain); - if (!self->m_is_initialized) { - self->d3d12_initialize_imgui(swap_chain); + if (!self->m_texture_manager) { + self->m_texture_manager = std::make_unique( + self->m_d3d12_device, + self->m_d3d12_command_queue, + self->m_d3d12_srv_heap + ); + } - if (!self->m_texture_manager) { - self->m_texture_manager = std::make_unique( - self->m_d3d12_device, - self->m_d3d12_command_queue, - self->m_d3d12_srv_heap - ); + if (config.get_primitive_rendering_enabled()) { + prm->late_init(self.get(), swap_chain); + } } - if (config.get_primitive_rendering_enabled()) { - prm->late_init(self.get(), swap_chain); + if (self->m_d3d12_command_queue) { + self->d3d12_present_hook_core(swap_chain, prm); } } - if (!self->m_d3d12_command_queue) { - return self->m_d3d_present_hook.call(swap_chain, sync_interval, flags); - } - - const auto facility = (uintptr_t)self->m_get_singleton("sFacility"); - - // Check if Steamworks is active. This is a very hacky fix for the AutoSteamworks app, - // which sometimes sends invalid input events that trip up ImGui. - // So we just disable ImGui rendering when Steamworks is active. Ideally we should - // check if the app is even running, but whatever. This is (probably) a temporary fix. - // +0x348 is the offset to cSteamControl, +0x444 is the offset from that to the mState field. - if (facility && *(u32*)(facility + 0x348 + 0x444) > 5) { - return self->m_d3d_present_hook.call(swap_chain, sync_interval, flags); - } - - self->d3d12_present_hook_core(swap_chain, prm); - - const HRESULT result = self->m_d3d_present_hook.call(swap_chain, sync_interval, flags); + HRESULT result = self->m_d3d_present_hook.call(swap_chain, sync_interval, flags); self->m_is_inside_present = false; return result; @@ -862,32 +555,6 @@ void D3DModule::d3d12_present_hook_core(IDXGISwapChain* swap_chain, const std::s } } -void D3DModule::d3d12_execute_command_lists_hook(ID3D12CommandQueue* command_queue, UINT num_command_lists, ID3D12CommandList* const* command_lists) { - const auto self = NativePluginFramework::get_module(); - - if (!self->m_d3d12_command_queue && command_queue->GetDesc().Type == D3D12_COMMAND_LIST_TYPE_DIRECT) { - dlog::debug("Found D3D12 command queue"); - self->m_d3d12_command_queue = command_queue; - - if (self->m_texture_manager) { - self->m_texture_manager->update_command_queue(command_queue); - } - } - - return self->m_d3d_execute_command_lists_hook.call(command_queue, num_command_lists, command_lists); -} - -UINT64 D3DModule::d3d12_signal_hook(ID3D12CommandQueue* command_queue, ID3D12Fence* fence, UINT64 value) { - const auto self = NativePluginFramework::get_module(); - - if (self->m_d3d12_command_queue == command_queue) { - self->m_d3d12_fence = fence; - self->m_d3d12_fence_value = value; - } - - return self->m_d3d_signal_hook.call(command_queue, fence, value); -} - HRESULT D3DModule::d3d_resize_buffers_hook(IDXGISwapChain* swap_chain, UINT buffer_count, UINT w, UINT h, DXGI_FORMAT format, UINT flags) { const auto self = NativePluginFramework::get_module(); const auto prm = NativePluginFramework::get_module(); @@ -908,32 +575,41 @@ HRESULT D3DModule::d3d_resize_buffers_hook(IDXGISwapChain* swap_chain, UINT buff return self->m_d3d_resize_buffers_hook.call(swap_chain, buffer_count, w, h, format, flags); } +UINT64 D3DModule::d3d12_signal_hook(ID3D12CommandQueue* command_queue, ID3D12Fence* fence, UINT64 value) { + const auto self = NativePluginFramework::get_module(); + + if (self->m_d3d12_command_queue == command_queue) { + self->m_d3d12_fence = fence; + self->m_d3d12_fence_value = value; + } + + return self->m_d3d_signal_hook.call(command_queue, fence, value); +} + HRESULT D3DModule::d3d11_present_hook(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags) { const auto self = NativePluginFramework::get_module(); const auto prm = NativePluginFramework::get_module(); const auto& config = preloader::LoaderConfig::get(); - if (self->m_is_inside_present) { - return self->m_d3d_present_hook.call(swap_chain, sync_interval, flags); - } + if (!self->m_is_inside_present) { + self->m_is_inside_present = true; - self->m_is_inside_present = true; + if (!self->m_is_initialized) { + self->d3d11_initialize_imgui(swap_chain); - if (!self->m_is_initialized) { - self->d3d11_initialize_imgui(swap_chain); + if (!self->m_texture_manager) { + self->m_texture_manager = std::make_unique(self->m_d3d11_device, self->m_d3d11_device_context); + } - if (!self->m_texture_manager) { - self->m_texture_manager = std::make_unique(self->m_d3d11_device, self->m_d3d11_device_context); + if (config.get_primitive_rendering_enabled()) { + prm->late_init(self.get(), swap_chain); + } } - if (config.get_primitive_rendering_enabled()) { - prm->late_init(self.get(), swap_chain); - } + self->d3d11_present_hook_core(swap_chain, prm); } - self->d3d11_present_hook_core(swap_chain, prm); - - const auto result = self->m_d3d_present_hook.call(swap_chain, sync_interval, flags); + HRESULT result = self->m_d3d_present_hook.call(swap_chain, sync_interval, flags); self->m_is_inside_present = false; return result; @@ -960,7 +636,6 @@ void D3DModule::d3d11_present_hook_core(IDXGISwapChain* swap_chain, const std::s } } - extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT D3DModule::my_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { diff --git a/mhw-cs-plugin-loader/D3DModule.h b/mhw-cs-plugin-loader/D3DModule.h index 9a8973e..867eefd 100644 --- a/mhw-cs-plugin-loader/D3DModule.h +++ b/mhw-cs-plugin-loader/D3DModule.h @@ -22,11 +22,9 @@ class D3DModule final : public NativeModule { void shutdown() override; private: - void common_initialize(); - void initialize_for_d3d12(); - void initialize_for_d3d11(); - void initialize_for_d3d12_alt(); - void initialize_for_d3d11_alt(); + void common_initialize(const uintptr_t render_singleton); + void initialize_for_d3d12(const uintptr_t renderer); + void initialize_for_d3d11(const uintptr_t renderer); void d3d12_initialize_imgui(IDXGISwapChain* swap_chain); void d3d11_initialize_imgui(IDXGISwapChain* swap_chain); @@ -41,11 +39,8 @@ class D3DModule final : public NativeModule { static bool is_d3d12(); - static void title_menu_ready_hook(void* gui); - static HRESULT d3d12_present_hook(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags); void d3d12_present_hook_core(IDXGISwapChain* swap_chain, const std::shared_ptr& prm); - static void d3d12_execute_command_lists_hook(ID3D12CommandQueue* command_queue, UINT num_command_lists, ID3D12CommandList* const* command_lists); static UINT64 d3d12_signal_hook(ID3D12CommandQueue* command_queue, ID3D12Fence* fence, UINT64 value); static HRESULT d3d11_present_hook(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags); @@ -75,15 +70,12 @@ class D3DModule final : public NativeModule { bool m_is_inside_present = false; bool m_fonts_loaded = false; - safetyhook::InlineHook m_title_menu_ready_hook; + safetyhook::MidHook m_create_renderer_hook; safetyhook::InlineHook m_d3d_present_hook; - safetyhook::InlineHook m_d3d_execute_command_lists_hook; safetyhook::InlineHook m_d3d_signal_hook; safetyhook::InlineHook m_d3d_resize_buffers_hook; - safetyhook::MidHook m_d3d_present_hook_alt; - std::unique_ptr m_texture_manager; #pragma region D3D12 @@ -106,15 +98,10 @@ class D3DModule final : public NativeModule { ID3D11Device* m_d3d11_device = nullptr; ID3D11DeviceContext* m_d3d11_device_context = nullptr; - IDXGISwapChain* m_d3d11_swap_chain = nullptr; #pragma endregion - HMODULE m_d3d12_module = nullptr; - HMODULE m_d3d11_module = nullptr; - HWND m_game_window = nullptr; - HMODULE m_game_module = nullptr; WNDPROC m_game_window_proc = nullptr; HWND m_temp_window = nullptr; diff --git a/mhw-cs-plugin-loader/PrimitiveRenderingModule.cpp b/mhw-cs-plugin-loader/PrimitiveRenderingModule.cpp index 1defe57..0fe26ed 100644 --- a/mhw-cs-plugin-loader/PrimitiveRenderingModule.cpp +++ b/mhw-cs-plugin-loader/PrimitiveRenderingModule.cpp @@ -1048,14 +1048,8 @@ void PrimitiveRenderingModule::late_init_d3d12(D3DModule* d3dmodule, IDXGISwapCh ComPtr signature_blob; ComPtr error_blob; - const auto serialize_root_signature = (decltype(D3D12SerializeRootSignature)*)GetProcAddress( - d3dmodule->m_d3d12_module, "D3D12SerializeRootSignature" - ); - if (!serialize_root_signature) { - dlog::error("Failed to get D3D12SerializeRootSignature"); - } - - HandleResult(serialize_root_signature( + // d3d12.lib linked in D3DModule.cpp + HandleResult(D3D12SerializeRootSignature( &root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, signature_blob.GetAddressOf(), @@ -1089,7 +1083,7 @@ void PrimitiveRenderingModule::late_init_d3d12(D3DModule* d3dmodule, IDXGISwapCh signature_blob.Reset(); error_blob.Reset(); - HandleResult(serialize_root_signature( + HandleResult(D3D12SerializeRootSignature( &root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, signature_blob.GetAddressOf(), From d8ebc6e6bd6c6ec98c08c67a7ca551f816895021 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Tue, 17 Mar 2026 18:09:52 -0400 Subject: [PATCH 3/3] Add shader replacement functionality --- SharpPluginLoader.Core/IPlugin.cs | 26 +++ SharpPluginLoader.Core/Rendering/Renderer.cs | 7 + mhw-cs-plugin-loader/D3DModule.cpp | 164 +++++++++++++++++++ mhw-cs-plugin-loader/D3DModule.h | 40 +++++ 4 files changed, 237 insertions(+) diff --git a/SharpPluginLoader.Core/IPlugin.cs b/SharpPluginLoader.Core/IPlugin.cs index 40863a5..a56eed3 100644 --- a/SharpPluginLoader.Core/IPlugin.cs +++ b/SharpPluginLoader.Core/IPlugin.cs @@ -11,6 +11,22 @@ namespace SharpPluginLoader.Core #pragma warning disable CS0649 + public enum ShaderSourceType : int { + HLSL = 0, + BINARY + }; + + unsafe public struct ShaderReplacement { + public ShaderSourceType Type; + public byte* Source; + public int Length; + }; + + unsafe public struct ShaderInfo { + public fixed sbyte DxbcHash[36]; + public ShaderReplacement Replacement; + }; + /// /// Contains some configuration data for a plugin. This is returned by . /// @@ -37,6 +53,9 @@ public class PluginData /// internal bool OnResourceLoad; + /// + internal bool OnCreateShader; + /// internal bool OnChatMessageSent; #endregion @@ -237,6 +256,13 @@ public void OnUnload() { } [PluginEvent] public void OnResourceLoad(Resource? resource, MtDti dti, string path, LoadFlags flags) => throw new NotImplementedException(); + /// + /// Gets called when the game creates a new shader. + /// + /// Metadata and fields to optionally define a replacement shader + [PluginEvent] + public unsafe void OnCreateShader(ShaderInfo* info) => throw new NotImplementedException(); + /// /// Gets called when a chat message is sent (on the local side). /// diff --git a/SharpPluginLoader.Core/Rendering/Renderer.cs b/SharpPluginLoader.Core/Rendering/Renderer.cs index 4e8d2f6..d1c5675 100644 --- a/SharpPluginLoader.Core/Rendering/Renderer.cs +++ b/SharpPluginLoader.Core/Rendering/Renderer.cs @@ -466,6 +466,13 @@ ref MemoryUtil.AsRef(_renderingOptionPointers.LineThickness), return (nint)ImGui.GetDrawData().NativePtr; } + [UnmanagedCallersOnly] + internal static unsafe void CreateShader(ShaderInfo* info) + { + foreach (var plugin in PluginManager.Instance.GetPlugins(p => p.OnCreateShader)) + plugin.OnCreateShader(info); + } + internal static void Shutdown() { ImGui.DestroyContext(); diff --git a/mhw-cs-plugin-loader/D3DModule.cpp b/mhw-cs-plugin-loader/D3DModule.cpp index c24bc26..6ea4f29 100644 --- a/mhw-cs-plugin-loader/D3DModule.cpp +++ b/mhw-cs-plugin-loader/D3DModule.cpp @@ -43,6 +43,11 @@ void D3DModule::initialize(CoreClr* coreclr) { L"SharpPluginLoader.Core.Rendering.Renderer", L"ImGuiRender" ); + m_core_create_shader = coreclr->get_method( + config::SPL_CORE_ASSEMBLY_NAME, + L"SharpPluginLoader.Core.Rendering.Renderer", + L"CreateShader" + ); m_core_initialize_imgui = coreclr->get_method( config::SPL_CORE_ASSEMBLY_NAME, L"SharpPluginLoader.Core.Rendering.Renderer", @@ -163,14 +168,19 @@ void D3DModule::initialize_for_d3d12(const uintptr_t renderer) { const auto swap_chain_vft = *(void***)swap_chain; const auto cmd_queue_vft = *(void***)m_d3d12_command_queue; + const auto device_vft = *(void***)m_d3d12_device; const auto present = swap_chain_vft[8]; const auto resize_buffers = swap_chain_vft[13]; const auto signal = cmd_queue_vft[14]; + const auto create_graphics_pipeline_state = device_vft[10]; + const auto create_compute_pipeline_state = device_vft[11]; m_d3d_present_hook = safetyhook::create_inline(present, d3d12_present_hook); m_d3d_resize_buffers_hook = safetyhook::create_inline(resize_buffers, d3d_resize_buffers_hook); m_d3d_signal_hook = safetyhook::create_inline(signal, d3d12_signal_hook); + m_d3d_create_graphics_pipeline_hook = safetyhook::create_inline(create_graphics_pipeline_state, d3d12_create_graphics_pipeline_state_hook); + m_d3d_create_compute_pipeline_hook = safetyhook::create_inline(create_compute_pipeline_state, d3d12_create_compute_pipeline_state_hook); } void D3DModule::initialize_for_d3d11(const uintptr_t renderer) { @@ -181,10 +191,17 @@ void D3DModule::initialize_for_d3d11(const uintptr_t renderer) { } const auto swap_chain_vft = *(void***)swap_chain; + const auto device_vft = *(void***)m_d3d11_device; const auto present = swap_chain_vft[8]; + const auto create_vertex_shader = device_vft[12]; + const auto create_pixel_shader = device_vft[15]; + const auto create_compute_shader = device_vft[18]; m_d3d_present_hook = safetyhook::create_inline(present, d3d11_present_hook); + m_d3d_create_vertex_shader_hook = safetyhook::create_inline(create_vertex_shader, d3d11_create_vertex_shader_hook); + m_d3d_create_pixel_shader_hook = safetyhook::create_inline(create_pixel_shader, d3d11_create_pixel_shader_hook); + m_d3d_create_compute_shader_hook = safetyhook::create_inline(create_compute_shader, d3d11_create_compute_shader_hook); } void D3DModule::d3d12_initialize_imgui(IDXGISwapChain* swap_chain) { @@ -586,6 +603,84 @@ UINT64 D3DModule::d3d12_signal_hook(ID3D12CommandQueue* command_queue, ID3D12Fen return self->m_d3d_signal_hook.call(command_queue, fence, value); } +bool D3DModule::compile_replacement_shader(ShaderReplacement& re, const char* target, D3D12_SHADER_BYTECODE* out) { + ID3DBlob* blob = nullptr; + HRESULT hr = D3DCompile( + re.Source, + re.Length, + nullptr, + nullptr, + nullptr, + "main", + target, + 0, + 0, + &blob, + nullptr + ); + if (FAILED(hr)) { + dlog::error("Failed to compile replacement shader"); + return false; + } + *out = CD3DX12_SHADER_BYTECODE(blob); + return true; +} + +HRESULT D3DModule::d3d12_create_graphics_pipeline_state_hook(ID3D12Device* device, const D3D12_GRAPHICS_PIPELINE_STATE_DESC* desc, REFIID riid, void** pipeline_state) { + const auto self = NativePluginFramework::get_module(); + if (desc->VS.pShaderBytecode) { + ShaderInfo info = self->get_shader_info((uint32_t*)desc->VS.pShaderBytecode); + self->m_core_create_shader(&info); + ShaderReplacement re = info.Replacement; + if (re.Source) { + switch (re.Type) { + case ShaderSourceType::HLSL: + compile_replacement_shader(re, "vs_5_0", &((D3D12_GRAPHICS_PIPELINE_STATE_DESC*)desc)->VS); + break; + case ShaderSourceType::BINARY: + ((D3D12_GRAPHICS_PIPELINE_STATE_DESC*)desc)->VS = CD3DX12_SHADER_BYTECODE(re.Source, re.Length); + break; + } + } + } + if (desc->PS.pShaderBytecode) { + ShaderInfo info = self->get_shader_info((uint32_t*)desc->PS.pShaderBytecode); + self->m_core_create_shader(&info); + ShaderReplacement re = info.Replacement; + if (re.Source) { + switch (re.Type) { + case ShaderSourceType::HLSL: + compile_replacement_shader(re, "ps_5_0", &((D3D12_GRAPHICS_PIPELINE_STATE_DESC*)desc)->PS); + break; + case ShaderSourceType::BINARY: + ((D3D12_GRAPHICS_PIPELINE_STATE_DESC*)desc)->PS = CD3DX12_SHADER_BYTECODE(re.Source, re.Length); + break; + } + } + } + return self->m_d3d_create_graphics_pipeline_hook.call(device, desc, riid, pipeline_state); +} + +HRESULT D3DModule::d3d12_create_compute_pipeline_state_hook(ID3D12Device* device, const D3D12_COMPUTE_PIPELINE_STATE_DESC* desc, REFIID riid, void** pipeline_state) { + const auto self = NativePluginFramework::get_module(); + if (desc->CS.pShaderBytecode) { + ShaderInfo info = self->get_shader_info((uint32_t*)desc->CS.pShaderBytecode); + self->m_core_create_shader(&info); + ShaderReplacement re = info.Replacement; + if (re.Source) { + switch (re.Type) { + case ShaderSourceType::HLSL: + compile_replacement_shader(re, "cs_5_0", &((D3D12_COMPUTE_PIPELINE_STATE_DESC*)desc)->CS); + break; + case ShaderSourceType::BINARY: + ((D3D12_COMPUTE_PIPELINE_STATE_DESC*)desc)->CS = CD3DX12_SHADER_BYTECODE(re.Source, re.Length); + break; + } + } + } + return self->m_d3d_create_compute_pipeline_hook.call(device, desc, riid, pipeline_state); +} + HRESULT D3DModule::d3d11_present_hook(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags) { const auto self = NativePluginFramework::get_module(); const auto prm = NativePluginFramework::get_module(); @@ -636,6 +731,75 @@ void D3DModule::d3d11_present_hook_core(IDXGISwapChain* swap_chain, const std::s } } +HRESULT D3DModule::d3d11_create_vertex_shader_hook(ID3D11Device* device, const void* shader_bytecode, SIZE_T bytecode_length, ID3D11ClassLinkage* class_linkage, ID3D11VertexShader** vertex_shader) { + const auto self = NativePluginFramework::get_module(); + ShaderInfo info = self->get_shader_info((uint32_t*)shader_bytecode); + self->m_core_create_shader(&info); + ShaderReplacement re = info.Replacement; + if (re.Source) { + CD3DX12_SHADER_BYTECODE sbc; + switch (re.Type) { + case ShaderSourceType::HLSL: + if (compile_replacement_shader(re, "vs_5_0", &sbc)) { + shader_bytecode = sbc.pShaderBytecode; + bytecode_length = sbc.BytecodeLength; + } + break; + case ShaderSourceType::BINARY: + shader_bytecode = re.Source; + bytecode_length = re.Length; + break; + } + } + return self->m_d3d_create_vertex_shader_hook.call(device, shader_bytecode, bytecode_length, class_linkage, vertex_shader); +} + +HRESULT D3DModule::d3d11_create_pixel_shader_hook(ID3D11Device* device, const void* shader_bytecode, SIZE_T bytecode_length, ID3D11ClassLinkage* class_linkage, ID3D11PixelShader** pixel_shader) { + const auto self = NativePluginFramework::get_module(); + ShaderInfo info = self->get_shader_info((uint32_t*)shader_bytecode); + self->m_core_create_shader(&info); + ShaderReplacement re = info.Replacement; + if (re.Source) { + CD3DX12_SHADER_BYTECODE sbc; + switch (re.Type) { + case ShaderSourceType::HLSL: + if (compile_replacement_shader(re, "ps_5_0", &sbc)) { + shader_bytecode = sbc.pShaderBytecode; + bytecode_length = sbc.BytecodeLength; + } + break; + case ShaderSourceType::BINARY: + shader_bytecode = re.Source; + bytecode_length = re.Length; + break; + } + } + return self->m_d3d_create_pixel_shader_hook.call(device, shader_bytecode, bytecode_length, class_linkage, pixel_shader); +} + +HRESULT D3DModule::d3d11_create_compute_shader_hook(ID3D11Device* device, const void* shader_bytecode, SIZE_T bytecode_length, ID3D11ClassLinkage* class_linkage, ID3D11ComputeShader** compute_shader) { + const auto self = NativePluginFramework::get_module(); + ShaderInfo info = self->get_shader_info((uint32_t*)shader_bytecode); + self->m_core_create_shader(&info); + ShaderReplacement re = info.Replacement; + if (re.Source) { + CD3DX12_SHADER_BYTECODE sbc; + switch (re.Type) { + case ShaderSourceType::HLSL: + if (compile_replacement_shader(re, "cs_5_0", &sbc)) { + shader_bytecode = sbc.pShaderBytecode; + bytecode_length = sbc.BytecodeLength; + } + break; + case ShaderSourceType::BINARY: + shader_bytecode = re.Source; + bytecode_length = re.Length; + break; + } + } + return self->m_d3d_create_compute_shader_hook.call(device, shader_bytecode, bytecode_length, class_linkage, compute_shader); +} + extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT D3DModule::my_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { diff --git a/mhw-cs-plugin-loader/D3DModule.h b/mhw-cs-plugin-loader/D3DModule.h index 867eefd..8c69812 100644 --- a/mhw-cs-plugin-loader/D3DModule.h +++ b/mhw-cs-plugin-loader/D3DModule.h @@ -13,6 +13,7 @@ #include #include +#include class D3DModule final : public NativeModule { template using ComPtr = Microsoft::WRL::ComPtr; @@ -42,9 +43,14 @@ class D3DModule final : public NativeModule { static HRESULT d3d12_present_hook(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags); void d3d12_present_hook_core(IDXGISwapChain* swap_chain, const std::shared_ptr& prm); static UINT64 d3d12_signal_hook(ID3D12CommandQueue* command_queue, ID3D12Fence* fence, UINT64 value); + static HRESULT d3d12_create_graphics_pipeline_state_hook(ID3D12Device* device, const D3D12_GRAPHICS_PIPELINE_STATE_DESC* desc, REFIID riid, void** pipeline_state); + static HRESULT d3d12_create_compute_pipeline_state_hook(ID3D12Device* device, const D3D12_COMPUTE_PIPELINE_STATE_DESC* desc, REFIID riid, void** pipeline_state); static HRESULT d3d11_present_hook(IDXGISwapChain* swap_chain, UINT sync_interval, UINT flags); void d3d11_present_hook_core(IDXGISwapChain* swap_chain, const std::shared_ptr& prm) const; + static HRESULT d3d11_create_vertex_shader_hook(ID3D11Device* device, const void* shader_bytecode, SIZE_T bytecode_length, ID3D11ClassLinkage* class_linkage, ID3D11VertexShader** vertex_shader); + static HRESULT d3d11_create_pixel_shader_hook(ID3D11Device* device, const void* shader_bytecode, SIZE_T bytecode_length, ID3D11ClassLinkage* class_linkage, ID3D11PixelShader** pixel_shader); + static HRESULT d3d11_create_compute_shader_hook(ID3D11Device* device, const void* shader_bytecode, SIZE_T bytecode_length, ID3D11ClassLinkage* class_linkage, ID3D11ComputeShader** compute_shader); static HRESULT d3d_resize_buffers_hook(IDXGISwapChain* swap_chain, UINT buffer_count, UINT w, UINT h, DXGI_FORMAT format, UINT flags); static LRESULT my_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); @@ -64,6 +70,22 @@ class D3DModule final : public NativeModule { ImFont* Font; }; + enum ShaderSourceType { + HLSL = 0, + BINARY + }; + + struct ShaderReplacement { + int Type; + unsigned char* Source; + int Length; + }; + + struct ShaderInfo { + char DxbcHash[36]; + ShaderReplacement Replacement; + }; + private: static inline bool m_is_d3d12 = false; bool m_is_initialized = false; @@ -76,8 +98,25 @@ class D3DModule final : public NativeModule { safetyhook::InlineHook m_d3d_signal_hook; safetyhook::InlineHook m_d3d_resize_buffers_hook; + safetyhook::InlineHook m_d3d_create_graphics_pipeline_hook; + safetyhook::InlineHook m_d3d_create_compute_pipeline_hook; + safetyhook::InlineHook m_d3d_create_vertex_shader_hook; + safetyhook::InlineHook m_d3d_create_pixel_shader_hook; + safetyhook::InlineHook m_d3d_create_compute_shader_hook; + std::unique_ptr m_texture_manager; + static ShaderInfo get_shader_info(uint32_t* dxbc) { + ShaderInfo info; + std::string hash = std::format("{:08x}-{:08x}-{:08x}-{:08x}", dxbc[1], dxbc[2], dxbc[3], dxbc[4]); + std::memcpy(info.DxbcHash, hash.c_str(), 35); + info.DxbcHash[35] = '\0'; + info.Replacement.Source = nullptr; + return info; + } + + static bool compile_replacement_shader(ShaderReplacement& re, const char* target, D3D12_SHADER_BYTECODE* out); + #pragma region D3D12 ID3D12Device* m_d3d12_device = nullptr; @@ -110,6 +149,7 @@ class D3DModule final : public NativeModule { ImGuiContext*(*m_core_initialize_imgui)(MtSize viewport_size, MtSize window_size, bool d3d12, const char* menu_key) = nullptr; ImDrawData*(*m_core_imgui_render)() = nullptr; void(*m_core_render)() = nullptr; + void(*m_core_create_shader)(ShaderInfo* info) = nullptr; int(*m_core_get_custom_fonts)(CustomFont** out_fonts) = nullptr; void(*m_core_resolve_custom_fonts)() = nullptr; void* (*m_get_singleton)(const char* name) = nullptr;