diff --git a/Engine/Game/Build/20260929/render_resource_pool.h b/Engine/Game/Build/20260929/render_resource_pool.h index 463e504..bf6fece 100644 --- a/Engine/Game/Build/20260929/render_resource_pool.h +++ b/Engine/Game/Build/20260929/render_resource_pool.h @@ -29,6 +29,9 @@ inline constexpr auto constructor_entry = hex_bytes( inline constexpr auto index_table_constructor_entry = hex_bytes( "4053555657415641574883ec78c5f9efc033ed488bf948896c2428488d4c2440"); +// mov ecx, 0x2daa00 -> mov ecx, 0x11ab080 (the state owner's size, 0x3efd347). +inline constexpr auto other_renderer_size_before = hex_bytes("b900aa2d00"); +inline constexpr auto other_renderer_size_after = hex_bytes("b980b01a01"); inline constexpr auto vector_begin_before = hex_bytes("488d81b0000000"); inline constexpr auto vector_begin_after = hex_bytes("488b8190000000"); inline constexpr auto vector_bytes_before = hex_bytes("480500001e00"); @@ -92,15 +95,20 @@ inline constexpr std::ptrdiff_t relation_mask_vector_offset = 0x114ab60; inline constexpr std::uintptr_t constructor_rva = 0x03e7f300; // The constructor is the base of two renderers. Only the one constructed here // (vtable 0x64f2c10, 0x11ab080 bytes) has the resource-state and relation-mask -// vectors and the metadata fields: every read of owner+0x2da920, owner+0x114ab60 -// and owner+0x11aae28 is in its code. The other (constructed at 0x030a0a46, -// vtable 0x6414168) is 0x2daa00 bytes (Skate.exe+0x30c64cc): it keeps fields of -// its own at +0x2da920, and the rest are past its end. +// vectors: every read of owner+0x2da920 and owner+0x114ab60 is in its code. The +// other (constructed at 0x030a0a46, vtable 0x6414168, kept at 0x75f2700) keeps +// fields of its own at +0x2da920. Both use the metadata: the patched view +// (0x030e6bff, called from 0x30a7e18 with the other renderer) and append +// (0x03e94fe6, called from 0x30d8ebb) read its pointers at +0x11aae28/+0x11aae30. +// The other renderer was allocated 0x2daa00 bytes, which ends before them, so it +// is allocated the state owner's 0x11ab080 instead (other_renderer_size, above). inline constexpr std::uintptr_t state_owner_call_rva = 0x03f33a46; inline constexpr std::uintptr_t index_table_constructor_rva = 0x03e7e400; inline constexpr std::uintptr_t resource_flags_priority_rva = 0x03f56073; inline constexpr std::uintptr_t resource_flags_visibility_rva = 0x03f5789a; inline constexpr Patch patches[]{ + // The other renderer's allocation, large enough for the metadata pointers. + {0x030c64cc, other_renderer_size_before, other_renderer_size_after}, {0x03e7f33c, vector_begin_before, vector_begin_after}, {0x03e7f351, vector_bytes_before, vector_bytes_after}, {0x03e94e99, capacity_before, capacity_after}, diff --git a/Extension/Assets/native_render_resource_pool.cpp b/Extension/Assets/native_render_resource_pool.cpp index b2ede62..2e47243 100644 --- a/Extension/Assets/native_render_resource_pool.cpp +++ b/Extension/Assets/native_render_resource_pool.cpp @@ -308,13 +308,13 @@ void* __fastcall construct_native_renderer(void* self, void* configuration) { // The patched constructor loads its initial vector pointer from +0x90, // constructs the native default record there, and advances +0x98 itself. store_field(self, vector_begin_offset, buffers.records); - // The metadata fields, like the state vectors, are the state owner's alone: the other - // renderer is 0x2daa00 bytes (Skate.exe+0x30c64cc), and +0x11aae28 is 18 MB past its end. - // Written there for every launch until 2026-10-15, they went into whatever memory lay there - // (a crash in store_field when it was not mapped). + // Both renderers read the metadata through these pointers (render_resource_pool.h). The other + // renderer was allocated 0x2daa00 bytes, and they were written 18 MB past its end, into + // whatever lay there (a crash in store_field when nothing did): it is allocated the state + // owner's size now (the patch at 0x030c64cc), so they are its own. + store_field(self, metadata_begin_offset, buffers.metadata); + store_field(self, metadata_end_offset, metadata_end); if (states) { - store_field(self, metadata_begin_offset, buffers.metadata); - store_field(self, metadata_end_offset, metadata_end); store_field(self, resource_state_vector_offset, buffers.resource_states); store_field(self, relation_mask_vector_offset, relation_mask_begin); } @@ -323,11 +323,9 @@ void* __fastcall construct_native_renderer(void* self, void* configuration) { // The side-array fields are initialized by later renderer setup on the // stock build. Keep the external values authoritative across construction. - if (states) { - store_field(self, metadata_begin_offset, buffers.metadata); - store_field(self, metadata_end_offset, metadata_end); - state_owner.store(reinterpret_cast(self), std::memory_order_release); - } + store_field(self, metadata_begin_offset, buffers.metadata); + store_field(self, metadata_end_offset, metadata_end); + if (states) state_owner.store(reinterpret_cast(self), std::memory_order_release); if (load_field(self, vector_begin_offset) != buffers.records || load_field(self, vector_capacity_offset) != record_end || load_field(self, vector_end_offset) < buffers.records ||