mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-17 18:37:07 +02:00
A spatial transaction queue gives every object it tracks a cull ID from a free list made once, when the queue is, at the game's InitialCullIdListSize (40,000 in its config), and never grown. A custom map with more objects (Cardiff) emptied it while loading and the next insert read entry 0xffffffff of the list (crash at 0x489cfe9). - spatial_cull_ids: as each queue makes its list, its size setting is raised to 262,144, one ID for every entity slot the entity pages hold; a larger configured value is kept. 12 bytes an ID per queue. - The contract checks the list maker and how it reads the setting; it is installed at startup with the other world pools.
87 lines
3.4 KiB
C++
87 lines
3.4 KiB
C++
#include "spatial_cull_ids.h"
|
|
|
|
#include "Engine/Core/Hooks/hooks.h"
|
|
#include "Engine/Core/Log/logging.h"
|
|
#include "Engine/Core/Platform/memory.h"
|
|
#include "Engine/Game/Build/addresses.h"
|
|
#include "Engine/Game/Build/20260929/entity_pages.h"
|
|
#include "Engine/Game/Build/20260929/spatial_cull_ids.h"
|
|
|
|
#include <Windows.h>
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <mutex>
|
|
|
|
namespace dingosdk {
|
|
namespace {
|
|
namespace contract = addr::spatial_cull_ids;
|
|
static_assert(spatial_cull_id_target == addr::entity_pages::capacity);
|
|
|
|
using MakeList = void (*)(void* list, void* allocator);
|
|
using SettingsLookup = std::uintptr_t (*)(std::uintptr_t manager, std::uintptr_t type);
|
|
std::atomic<MakeList> original_make_list{};
|
|
std::uintptr_t installed_base{};
|
|
std::mutex install_mutex;
|
|
|
|
bool matches(std::uintptr_t address, const auto& expected) noexcept {
|
|
std::array<std::uint8_t, 32> actual{};
|
|
return expected.size() <= actual.size() && memory::read_bytes(address, actual.data(), expected.size()) &&
|
|
std::equal(expected.begin(), expected.end(), actual.begin());
|
|
}
|
|
|
|
// The list reads the setting right after this, on the same thread; nothing else reads it.
|
|
void make_list(void* list, void* allocator) {
|
|
const auto incoming_error = GetLastError();
|
|
std::uintptr_t manager{};
|
|
if (memory::peek(installed_base + contract::settings_manager, manager) && manager) {
|
|
const auto settings = reinterpret_cast<SettingsLookup>(installed_base + contract::settings_lookup)(
|
|
manager, installed_base + contract::settings_type);
|
|
if (settings) {
|
|
auto* size = reinterpret_cast<std::uint32_t*>(settings + contract::initial_cull_id_list_size);
|
|
if (*size < spatial_cull_id_target) {
|
|
logging::log(logging::Level::info, logging::Channel::world,
|
|
"Spatial cull IDs raised: {} -> {}", *size, spatial_cull_id_target);
|
|
*size = spatial_cull_id_target;
|
|
}
|
|
}
|
|
}
|
|
SetLastError(incoming_error);
|
|
original_make_list.load(std::memory_order_acquire)(list, allocator);
|
|
}
|
|
} // namespace
|
|
|
|
bool start_spatial_cull_ids(std::uintptr_t base, std::string& error) {
|
|
std::lock_guard lock(install_mutex);
|
|
error.clear();
|
|
if (installed_base) {
|
|
if (installed_base == base) return true;
|
|
error = "Spatial cull IDs are already raised on another image";
|
|
return false;
|
|
}
|
|
if (!matches(base + contract::make_list_rva, contract::make_list_entry) ||
|
|
!matches(base + contract::settings_type_load_rva, contract::settings_type_load) ||
|
|
!matches(base + contract::size_read_rva, contract::size_read)) {
|
|
error = "the cull-ID list does not match the supported game";
|
|
return false;
|
|
}
|
|
auto* target = reinterpret_cast<void*>(base + contract::make_list_rva);
|
|
MakeList original{};
|
|
const auto prepared = hook_prepare(target, reinterpret_cast<void*>(&make_list), reinterpret_cast<void**>(&original));
|
|
if (prepared != HookOk || !original) {
|
|
if (prepared == HookOk) hook_remove(target);
|
|
error = "its hook could not be prepared";
|
|
return false;
|
|
}
|
|
original_make_list.store(original, std::memory_order_release);
|
|
installed_base = base;
|
|
if (hook_enable(target) != HookOk) {
|
|
hook_remove(target);
|
|
installed_base = 0;
|
|
error = "its hook could not be attached";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
} // namespace dingosdk
|