#pragma once #include #include #include #include #include namespace dingosdk::memory { inline constexpr std::uintptr_t highest_user_address = 0x00007fffffffffffULL; inline bool read_bytes(std::uintptr_t address, void* destination, std::size_t size) noexcept { if (!destination || !size || size > highest_user_address || address < 0x10000 || address > highest_user_address - size) return false; SIZE_T count{}; return ReadProcessMemory(GetCurrentProcess(), reinterpret_cast(address), destination, size, &count) && count == size; } template requires std::is_trivially_copyable_v bool read(std::uintptr_t address, T& value) noexcept { return read_bytes(address, &value, sizeof(value)); } // The same read without a kernel transition: a plain copy that stops at an access violation. // ReadProcessMemory above costs a system call (about 1 us) per read, which adds up on paths // that run every game frame. Use peek there, for game objects expected to be readable; a // failed peek costs an exception, so keep read for probing addresses that are often invalid. inline bool peek_bytes(std::uintptr_t address, void* destination, std::size_t size) noexcept { if (!destination || !size || size > highest_user_address || address < 0x10000 || address > highest_user_address - size) return false; __try { std::memcpy(destination, reinterpret_cast(address), size); return true; } __except (EXCEPTION_EXECUTE_HANDLER) { return false; } } template requires std::is_trivially_copyable_v bool peek(std::uintptr_t address, T& value) noexcept { return peek_bytes(address, &value, sizeof(value)); } // Fresh read/write memory within reach of a rel32 jump from an image at base: the first free // spot past its end, for a code stub that a patched instruction jumps to. Null when nothing is // free within 1.75 GB of the image. inline void* allocate_near_image(std::uintptr_t base, std::size_t image_size, std::size_t bytes) noexcept { SYSTEM_INFO information{}; GetSystemInfo(&information); const auto granularity = static_cast(information.dwAllocationGranularity); const auto align_up = [granularity](std::uintptr_t value) { return (value + granularity - 1) & ~(granularity - 1); }; auto cursor = align_up(base + image_size); const auto limit = base + 0x70000000ULL; while (cursor < limit) { MEMORY_BASIC_INFORMATION region{}; if (!VirtualQuery(reinterpret_cast(cursor), ®ion, sizeof(region))) break; const auto region_base = reinterpret_cast(region.BaseAddress); const auto region_end = region_base + region.RegionSize; if (region.State == MEM_FREE) { const auto candidate = align_up(cursor > region_base ? cursor : region_base); if (candidate <= region_end && region_end - candidate >= bytes) if (auto* const result = VirtualAlloc(reinterpret_cast(candidate), bytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) return result; } if (region_end <= cursor) break; cursor = align_up(region_end); } return nullptr; } // A NUL-terminated string copied the same way: the length (terminator written at destination[length]), // or -1 if it is unreadable or has no terminator within capacity bytes. Reads byte by byte, so it never // touches memory past the terminator. inline std::ptrdiff_t peek_cstring(std::uintptr_t address, char* destination, std::size_t capacity) noexcept { if (!destination || !capacity || address < 0x10000 || address > highest_user_address - capacity) return -1; __try { const auto* source = reinterpret_cast(address); for (std::size_t i = 0; i < capacity; ++i) { destination[i] = source[i]; if (destination[i] == '\0') return static_cast(i); } return -1; } __except (EXCEPTION_EXECUTE_HANDLER) { return -1; } } }