#pragma once #include "base/HashTable.hpp" #include "function_types.hpp" #include "gta/fwddec.hpp" #include "gta_pointers.hpp" #include "memory/batch.hpp" #include "memory/byte_patch.hpp" #include "memory/module.hpp" #include "sc_pointers.hpp" #include "services/gta_data/cache_file.hpp" #include "socialclub/ScInfo.hpp" #include "util/compile_time_helpers.hpp" namespace big { class pointers { private: template void write_to_cache_or_read_from_cache(cache_file& cache_file, const memory::module& mem_region) { static_assert(batch_hash > 0); constexpr size_t field_count = (offset_of_cache_end_field - offset_of_cache_begin_field) / sizeof(void*); constexpr auto cache_version = batch_hash + field_count; cache_file.set_cache_version(cache_version); const uintptr_t pointer_to_cacheable_data_start = reinterpret_cast(this) + offset_of_cache_begin_field; if (!is_pointers_cache_up_to_date(cache_file, mem_region)) { run_batch(batch, mem_region); const uintptr_t pointer_to_cacheable_data_end = reinterpret_cast(this) + offset_of_cache_end_field; write_pointers_to_cache(cache_file, pointer_to_cacheable_data_start, pointer_to_cacheable_data_end, mem_region); } else { load_pointers_from_cache(cache_file, pointer_to_cacheable_data_start, mem_region); } cache_file.free(); } void load_pointers_from_cache(const cache_file& cache_file, const uintptr_t pointer_to_cacheable_data_start, const memory::module& mem_region); template void write_pointers_to_cache(cache_file& cache_file, const uintptr_t pointer_to_cacheable_data_start, const uintptr_t pointer_to_cacheable_data_end, const memory::module& mem_region) { constexpr size_t data_size = offset_of_cache_end_field - offset_of_cache_begin_field; cache_data cache_data_ptr = std::make_unique(data_size); // multiple things here: // - iterate each cacheable field of the pointers instance // - substract the base module address so that we only keep the offsets // - save that to the cache uintptr_t* cache_data = reinterpret_cast(cache_data_ptr.get()); size_t i = 0; for (uintptr_t field_ptr = pointer_to_cacheable_data_start; field_ptr != pointer_to_cacheable_data_end; field_ptr += sizeof(uintptr_t)) { const uintptr_t field_value = *reinterpret_cast(field_ptr); if (mem_region.contains(memory::handle(field_value))) { const uintptr_t offset = field_value - mem_region.begin().as(); cache_data[i] = offset; } else { LOG(FATAL) << "Just tried to save to cache a pointer supposedly within the " << batch_name.str << " module range but isn't! Offset from start of pointers instance: " << (field_ptr - reinterpret_cast(this)); } i++; } LOG(INFO) << "Pointers cache: saved " << (data_size / sizeof(uintptr_t)) << " fields to the cache"; cache_file.set_data(std::move(cache_data_ptr), data_size); cache_file.set_header_version(mem_region.timestamp()); cache_file.write(); } template bool is_pointers_cache_up_to_date(cache_file& cache_file, const memory::module& mem_region) { cache_file.load(); if (cache_file.up_to_date(mem_region.timestamp())) { LOG(INFO) << batch_name.str << " pointers cache is up to date, using it."; return true; } return false; } static constexpr auto get_gta_batch(); static constexpr auto get_sc_batch(); template void run_batch(const memory::batch& batch, const memory::module& mem_region) { if (!memory::batch_runner::run(batch, mem_region)) { const std::string error_message = std::string("Failed to find some patterns for ") + std::string(batch_name.str); throw std::runtime_error(error_message); } } public: explicit pointers(); ~pointers(); private: cache_file m_gta_pointers_cache; cache_file m_sc_pointers_cache; public: HWND m_hwnd{}; gta_pointers m_gta; socialclub_pointers m_sc; }; inline pointers* g_pointers{}; }