#include "../CFramework/Cheat/SDK/GameSDK/GameSDK.h" #include "../CFramework/Cheat/SnapshotHealth.h" #include #include #include #include #include #include #include #include #include #include class SparseMemoryReader final : public IGameMemoryReader { public: explicit SparseMemoryReader(const uintptr_t baseAddress = 0x10000000ULL) : m_baseAddress(baseAddress) { } bool TryReadBuffer(const uintptr_t address, void* buffer, const std::size_t size) const noexcept override { ++m_readCounts[address]; auto failure = m_failuresRemaining.find(address); if (failure != m_failuresRemaining.end() && failure->second > 0) { --failure->second; return false; } if (!Memory::IsUserAddress(address) || !buffer || size == 0) return false; for (std::size_t i = 0; i < size; ++i) { if (m_bytes.find(address + i) == m_bytes.end()) return false; } auto* output = static_cast(buffer); for (std::size_t i = 0; i < size; ++i) output[i] = m_bytes.at(address + i); return true; } bool ReadString(const uintptr_t address, void* buffer, const std::size_t size) const override { ++m_readCounts[address]; if (!Memory::IsUserAddress(address) || !buffer || size == 0) return false; const auto value = m_strings.find(address); if (value == m_strings.end()) return false; std::memset(buffer, 0, size); const std::size_t copyLength = (std::min)(size - 1, value->second.size()); std::memcpy(buffer, value->second.data(), copyLength); return true; } uintptr_t GetBaseAddress() const noexcept override { return m_baseAddress; } template void Write(const uintptr_t address, const T& value) { static_assert(std::is_trivially_copyable_v, "synthetic memory values must be trivially copyable"); const auto* bytes = reinterpret_cast(&value); for (std::size_t i = 0; i < sizeof(T); ++i) m_bytes[address + i] = bytes[i]; } void WriteString(const uintptr_t address, std::string value) { m_strings[address] = std::move(value); } void FailNextReads(const uintptr_t address, const std::size_t count) { m_failuresRemaining[address] = count; } std::size_t ReadCount(const uintptr_t address) const { const auto count = m_readCounts.find(address); return count == m_readCounts.end() ? 0 : count->second; } private: uintptr_t m_baseAddress; std::unordered_map m_bytes; std::unordered_map m_strings; mutable std::unordered_map m_failuresRemaining; mutable std::unordered_map m_readCounts; }; namespace { constexpr uintptr_t kNodeA = 0x30000000ULL; constexpr uintptr_t kNodeB = 0x30010000ULL; constexpr uintptr_t kObjectA = 0x31000000ULL; constexpr uintptr_t kObjectB = 0x31010000ULL; constexpr uintptr_t kUnreadableNode = 0x32000000ULL; void WriteNode(SparseMemoryReader& memory, const uintptr_t node, const uintptr_t next, const uintptr_t object, const char* name) { BaseObject value{}; value.nextObjectLink = next; value.object = object; memory.Write(node, value); const uintptr_t nameAddress = object + 0x800; memory.Write(object + offset::UnityCamera::ObjectName, nameAddress); if (name) memory.WriteString(nameAddress, name); } enum class GomLayout { ActiveNodesIndirect, ActiveNodesDirect, ActiveObjectsDirect, ActiveObjectsIndirect }; struct ReadyWorldFixture { uintptr_t gom{ 0 }; uintptr_t activeNode{ 0 }; uintptr_t gameWorld{ 0 }; uintptr_t localGameWorld{ 0 }; }; ReadyWorldFixture ConfigureReadyWorld(SparseMemoryReader& memory, const GomLayout layout) { const uintptr_t base = memory.GetBaseAddress(); const uintptr_t gomAddress = 0x40000000ULL; const uintptr_t activeNode = 0x41000000ULL; const uintptr_t activeSlot = 0x42000000ULL; const uintptr_t lastSlot = 0x42010000ULL; const uintptr_t gameWorld = 0x43000000ULL; const uintptr_t component0 = 0x44000000ULL; const uintptr_t component1 = 0x44010000ULL; const uintptr_t localGameWorld = 0x45000000ULL; memory.Write(base + offset::dwGameObjectManager, gomAddress); GameObjectManager gom{}; switch (layout) { case GomLayout::ActiveNodesIndirect: gom.ActiveNodes = activeSlot; gom.LastActiveNode = lastSlot; memory.Write(activeSlot, activeNode); memory.Write(lastSlot, activeNode); break; case GomLayout::ActiveNodesDirect: gom.ActiveNodes = activeNode; gom.LastActiveNode = activeNode; break; case GomLayout::ActiveObjectsDirect: gom.activeObjects = activeNode; gom.lastActiveObject = activeNode; break; case GomLayout::ActiveObjectsIndirect: gom.activeObjects = activeSlot; gom.lastActiveObject = lastSlot; memory.Write(activeSlot, activeNode); memory.Write(lastSlot, activeNode); break; } memory.Write(gomAddress, gom); WriteNode(memory, activeNode, 0, gameWorld, "GameWorld"); memory.Write(gameWorld + offset::UnityGameWorld::Component0, component0); memory.Write(component0 + offset::UnityGameWorld::Component1, component1); memory.Write(component1 + offset::UnityGameWorld::LocalGameWorld, localGameWorld); return { gomAddress, activeNode, gameWorld, localGameWorld }; } bool VerifyReadyLayout(const GomLayout layout) { SparseMemoryReader memory; const ReadyWorldFixture fixture = ConfigureReadyWorld(memory, layout); CGame game(memory); if (!game.Refresh()) return false; const GameStateSnapshot state = game.GetState(); return state.refreshStatus == GameRefreshStatus::Ready && state.initialized && state.inRaid && !state.cameraValid && state.refreshAttempts == 1 && state.consecutiveRefreshFailures == 0 && state.gameObjectManager == fixture.gom && state.activeNodeList == fixture.activeNode && state.gameWorld == fixture.gameWorld && state.localGameWorld == fixture.localGameWorld; } bool Near(const float lhs, const float rhs) { return std::fabs(lhs - rhs) < 0.001f; } class TestRunner { public: template void Run(const char* name, Test&& test) { bool passed = false; try { passed = test(); } catch (const std::exception& error) { std::cout << "[FAIL] " << name << " (exception: " << error.what() << ")\n"; ++m_failed; return; } catch (...) { std::cout << "[FAIL] " << name << " (unknown exception)\n"; ++m_failed; return; } std::cout << (passed ? "[PASS] " : "[FAIL] ") << name << '\n'; passed ? ++m_passed : ++m_failed; } int Finish() const { std::cout << "\nSummary: " << m_passed << " PASS, " << m_failed << " FAIL\n"; return m_failed == 0 ? 0 : 1; } private: int m_passed{ 0 }; int m_failed{ 0 }; }; } int main() { TestRunner tests; tests.Run("ObjectList target at first node", [] { SparseMemoryReader memory; WriteNode(memory, kNodeA, 0, kObjectA, "GameWorld"); return GetObjectFromList(memory, kNodeA, kNodeA, "GameWorld") == kObjectA; }); tests.Run("ObjectList accepts last=0", [] { SparseMemoryReader memory; WriteNode(memory, kNodeA, kNodeB, kObjectA, "Other"); WriteNode(memory, kNodeB, 0, kObjectB, "GameWorld"); return GetObjectFromList(memory, kNodeA, 0, "GameWorld") == kObjectB; }); tests.Run("ObjectList accepts unreadable last sentinel", [] { SparseMemoryReader memory; WriteNode(memory, kNodeA, kNodeB, kObjectA, "Other"); WriteNode(memory, kNodeB, 0, kObjectB, "GameWorld"); return GetObjectFromList(memory, kNodeA, kUnreadableNode, "GameWorld") == kObjectB; }); tests.Run("ObjectList continues after name read failure", [] { SparseMemoryReader memory; WriteNode(memory, kNodeA, kNodeB, kObjectA, nullptr); WriteNode(memory, kNodeB, 0, kObjectB, "GameWorld"); return GetObjectFromList(memory, kNodeA, 0, "GameWorld") == kObjectB; }); tests.Run("ObjectList stops self-loop", [] { SparseMemoryReader memory; WriteNode(memory, kNodeA, kNodeA, kObjectA, "Other"); return GetObjectFromList(memory, kNodeA, 0, "GameWorld") == 0 && memory.ReadCount(kNodeA) == 1; }); tests.Run("ObjectList stops two-node cycle", [] { SparseMemoryReader memory; WriteNode(memory, kNodeA, kNodeB, kObjectA, "OtherA"); WriteNode(memory, kNodeB, kNodeA, kObjectB, "OtherB"); return GetObjectFromList(memory, kNodeA, 0, "GameWorld") == 0 && memory.ReadCount(kNodeA) + memory.ReadCount(kNodeB) == 2; }); tests.Run("ObjectList target absent", [] { SparseMemoryReader memory; WriteNode(memory, kNodeA, kNodeB, kObjectA, "OtherA"); WriteNode(memory, kNodeB, 0, kObjectB, "OtherB"); return GetObjectFromList(memory, kNodeA, 0, "GameWorld") == 0; }); tests.Run("GOM ActiveNodes indirect", [] { return VerifyReadyLayout(GomLayout::ActiveNodesIndirect); }); tests.Run("GOM ActiveNodes direct", [] { return VerifyReadyLayout(GomLayout::ActiveNodesDirect); }); tests.Run("GOM activeObjects direct", [] { return VerifyReadyLayout(GomLayout::ActiveObjectsDirect); }); tests.Run("GOM activeObjects indirect", [] { return VerifyReadyLayout(GomLayout::ActiveObjectsIndirect); }); tests.Run("CGame recovers after initial GOM failures", [] { SparseMemoryReader memory; ConfigureReadyWorld(memory, GomLayout::ActiveNodesIndirect); const uintptr_t gomRoot = memory.GetBaseAddress() + offset::dwGameObjectManager; memory.FailNextReads(gomRoot, 3); CGame game(memory); for (uint64_t attempt = 1; attempt <= 3; ++attempt) { if (game.Refresh()) return false; const GameStateSnapshot failed = game.GetState(); if (failed.refreshStatus != GameRefreshStatus::GomUnavailable || failed.refreshAttempts != attempt || failed.consecutiveRefreshFailures != attempt) { return false; } } if (!game.Refresh()) return false; const GameStateSnapshot recovered = game.GetState(); return recovered.refreshStatus == GameRefreshStatus::Ready && recovered.inRaid && recovered.refreshAttempts == 4 && recovered.consecutiveRefreshFailures == 0; }); tests.Run("Snapshot generation and freshness liveness", [] { using namespace std::chrono; const auto start = steady_clock::time_point(milliseconds(1000)); const bool worldReady[] = { false, false, true, false }; SnapshotLiveness liveness{}; for (std::size_t i = 0; i < std::size(worldReady); ++i) { (void)worldReady[i]; liveness = AdvanceSnapshotLiveness(liveness.generation, start + milliseconds(33 * i)); } return liveness.generation == 4 && IsSnapshotFresh(liveness.capturedAt, liveness.capturedAt + milliseconds(250)) && !IsSnapshotFresh(liveness.capturedAt, liveness.capturedAt + milliseconds(251)) && GetSnapshotAgeMs(liveness.capturedAt, liveness.capturedAt + milliseconds(251)) == 251 && !IsSnapshotFresh(steady_clock::time_point{}, start); }); tests.Run("Camera unavailable keeps world Ready", [] { SparseMemoryReader memory; ConfigureReadyWorld(memory, GomLayout::ActiveNodesDirect); CGame game(memory); if (!game.Refresh() || !game.Refresh()) return false; const GameStateSnapshot state = game.GetState(); return state.refreshStatus == GameRefreshStatus::Ready && state.inRaid && !state.cameraValid && state.refreshAttempts == 2 && state.consecutiveRefreshFailures == 0; }); tests.Run("W2S projects visible point", [] { const Matrix projection( 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f); Vector2 output{}; return WorldToScreen(projection, Vector2(1920.f, 1080.f), Vector3(0.f, 0.f, 1.f), output) && Near(output.x, 960.f) && Near(output.y, 540.f); }); tests.Run("W2S rejects point behind camera", [] { const Matrix projection( 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f); Vector2 output{}; return !WorldToScreen(projection, Vector2(1920.f, 1080.f), Vector3(0.f, 0.f, -1.f), output); }); return tests.Finish(); }