fix this #define NOMINMAX

#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <cstdarg>
#include <memory>
#include <cstdint>
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <map>

// Struct to ensure HANDLEs are properly closed
struct HandleCloser {
    void operator()(HANDLE handle) const {
        if (handle && handle != INVALID_HANDLE_VALUE) {
            CloseHandle(handle);
        }
    }
};
using UniqueHandle = std::unique_ptr<std::remove_pointer<HANDLE>::type, HandleCloser>;

// Function to log messages to a log file only
void log_message(std::ofstream& log_file, const char* format, ...) {
    va_list args;
    va_start(args, format);
    char buffer[2048]; // Increased buffer size for longer messages
    vsnprintf(buffer, sizeof(buffer), format, args);
    va_end(args);

    log_file << buffer;
    log_file.flush();
}

// Function to retrieve the Process ID (PID) by process name
DWORD get_process_id_by_name(const std::string& process_name, std::ofstream& log_file) {
    UniqueHandle hSnapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
    if (!hSnapshot || hSnapshot.get() == INVALID_HANDLE_VALUE) {
        log_message(log_file, "CreateToolhelp32Snapshot failed with error: %lu\n", GetLastError());
        return 0;
    }

    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    if (!Process32First(hSnapshot.get(), &pe32)) {
        log_message(log_file, "Process32First failed with error: %lu\n", GetLastError());
        return 0;
    }

    do {
        std::wstring ws(pe32.szExeFile);
        std::string exeFile(ws.begin(), ws.end());
        if (_stricmp(exeFile.c_str(), process_name.c_str()) == 0) {
            return pe32.th32ProcessID;
        }
    } while (Process32Next(hSnapshot.get(), &pe32));

    return 0;
}

// Function to retrieve the base address of the main module of the process
uintptr_t get_base_address(DWORD pid, const std::string& process_name, std::ofstream& log_file) {
    UniqueHandle hSnapshot(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid));
    if (!hSnapshot || hSnapshot.get() == INVALID_HANDLE_VALUE) {
        log_message(log_file, "CreateToolhelp32Snapshot for modules failed with error: %lu\n", GetLastError());
        return 0;
    }

    MODULEENTRY32 me32;
    me32.dwSize = sizeof(MODULEENTRY32);
    if (!Module32First(hSnapshot.get(), &me32)) {
        log_message(log_file, "Module32First failed with error: %lu\n", GetLastError());
        return 0;
    }

    do {
        // Convert module name to std::string for comparison
        std::wstring ws(me32.szModule);
        std::string moduleName(ws.begin(), ws.end());

        // Compare the module name with the process name
        if (_stricmp(moduleName.c_str(), process_name.c_str()) == 0) {
            log_message(log_file, "Main module %s found at base address 0x%llX\n", moduleName.c_str(),
                reinterpret_cast<unsigned long long>(me32.modBaseAddr));
            return reinterpret_cast<uintptr_t>(me32.modBaseAddr);
        }
    } while (Module32Next(hSnapshot.get(), &me32));

    log_message(log_file, "Main module not found in the module list.\n");
    return 0;
}

// Struct to store section information
struct Section {
    std::string name;
    uintptr_t start;
    uintptr_t end;
};

// Function to parse PE headers and extract sections
bool get_sections(HANDLE hProcess, uintptr_t base_addr, std::vector<Section>& sections, std::ofstream& log_file) {
    // Read the DOS header
    IMAGE_DOS_HEADER dosHeader;
    SIZE_T bytesRead;
    if (!ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(base_addr), &dosHeader, sizeof(dosHeader), &bytesRead) || bytesRead != sizeof(dosHeader)) {
        log_message(log_file, "Failed to read DOS header. Error: %lu\n", GetLastError());
        return false;
    }

    if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) {
        log_message(log_file, "Invalid DOS signature.\n");
        return false;
    }

    uintptr_t ntHeaderAddr = base_addr + dosHeader.e_lfanew;

    DWORD signature;
    if (!ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(ntHeaderAddr), &signature, sizeof(signature), &bytesRead) || bytesRead != sizeof(signature)) {
        log_message(log_file, "Failed to read NT headers signature. Error: %lu\n", GetLastError());
        return false;
    }

    if (signature != IMAGE_NT_SIGNATURE) {
        log_message(log_file, "Invalid NT signature.\n");
        return false;
    }

    IMAGE_FILE_HEADER fileHeader;
    if (!ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(ntHeaderAddr + sizeof(DWORD)), &fileHeader, sizeof(fileHeader), &bytesRead) || bytesRead != sizeof(fileHeader)) {
        log_message(log_file, "Failed to read IMAGE_FILE_HEADER. Error: %lu\n", GetLastError());
        return false;
    }

    WORD sizeOfOptionalHeader = fileHeader.SizeOfOptionalHeader;

    uintptr_t optionalHeaderAddr = ntHeaderAddr + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER);

    WORD magic;
    if (!ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(optionalHeaderAddr), &magic, sizeof(magic), &bytesRead) || bytesRead != sizeof(magic)) {
        log_message(log_file, "Failed to read Optional Header Magic. Error: %lu\n", GetLastError());
        return false;
    }

    bool is64 = false;
    if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
        is64 = true;
    }
    else if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
        is64 = false;
    }
    else {
        log_message(log_file, "Unknown Optional Header Magic: 0x%X.\n", magic);
        return false;
    }

    // Read the entire Optional Header
    std::vector<BYTE> optionalHeaderData(sizeOfOptionalHeader);
    if (!ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(optionalHeaderAddr), optionalHeaderData.data(), sizeOfOptionalHeader, &bytesRead) || bytesRead != sizeOfOptionalHeader) {
        log_message(log_file, "Failed to read Optional Header. Error: %lu\n", GetLastError());
        return false;
    }

    uintptr_t sectionHeaderAddr = optionalHeaderAddr + sizeOfOptionalHeader;

    for (int i = 0; i < fileHeader.NumberOfSections; ++i) {
        IMAGE_SECTION_HEADER sectionHeader;
        if (!ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(sectionHeaderAddr + i * sizeof(IMAGE_SECTION_HEADER)), &sectionHeader, sizeof(sectionHeader), &bytesRead) || bytesRead != sizeof(sectionHeader)) {
            log_message(log_file, "Failed to read IMAGE_SECTION_HEADER for section %d. Error: %lu\n", i + 1, GetLastError());
            return false;
        }

        Section sec;
        char secName[9] = { 0 };
        memcpy(secName, sectionHeader.Name, 8);
        sec.name = std::string(secName);

        sec.start = base_addr + sectionHeader.VirtualAddress;
        sec.end = sec.start + sectionHeader.Misc.VirtualSize;

        sections.push_back(sec);
        log_message(log_file, "Section %s: Start=0x%llX, End=0x%llX (Relative: Start=0x%llX, End=0x%llX)\n",
            sec.name.c_str(),
            static_cast<unsigned long long>(sec.start),
            static_cast<unsigned long long>(sec.end),
            static_cast<unsigned long long>(sec.start - base_addr),
            static_cast<unsigned long long>(sec.end - base_addr));
    }

    return true;
}

// Function to search for a string within a specified memory section
uintptr_t find_string_in_section(HANDLE hProcess, uintptr_t section_start, uintptr_t section_end, const std::string& string_to_search, const std::string& section_name, std::ofstream& log_file, uintptr_t base_addr) {
    uintptr_t string_addr = 0;
    uintptr_t addr = section_start;
    size_t string_length = string_to_search.length();

    while (addr < section_end) {
        MEMORY_BASIC_INFORMATION mbi;
        if (!VirtualQueryEx(hProcess, reinterpret_cast<LPCVOID>(addr), &mbi, sizeof(mbi))) {
            log_message(log_file, "VirtualQueryEx failed at address 0x%llX with error: %lu\n",
                static_cast<unsigned long long>(addr), GetLastError());
            break;
        }

        if (mbi.State == MEM_COMMIT &&
            (mbi.Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READONLY | PAGE_EXECUTE_READ))) {

            SIZE_T regionSize = mbi.RegionSize;
            if (addr + regionSize > section_end) {
                regionSize = section_end - addr;
            }

            std::vector<char> buffer(regionSize);
            SIZE_T bytesReadMem;
            if (ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(addr), buffer.data(), regionSize, &bytesReadMem) && bytesReadMem > 0) {
                for (SIZE_T i = 0; i < bytesReadMem - string_length + 1; ++i) {
                    if (std::memcmp(buffer.data() + i, string_to_search.c_str(), string_length) == 0) {
                        string_addr = addr + i;
                        return string_addr;
                    }
                }
            }

            addr += regionSize;
        }
        else {
            addr += mbi.RegionSize;
        }
    }

    return 0;
}

int main() {
    std::string process_name;
    std::cout << "Enter the process name (e.g., notepad.exe): ";
    std::getline(std::cin, process_name);

    // Map of game settings and their types
    const std::vector<std::string> gameSettings = {
        "fVATSMaxChance", "fVATSChancePerceptionMultiplier", "fVATSMaxEngageDistance",
        "fVATSChanceRaycastVisibilityMininmum", "fVATSPingCost", "fVATSCriticalChargeBase",
        "fVATSCriticalChargeBonusMult", "fVATSCriticalChargeLimit", "fVATSCriticalChargeMult",
        "fPlayerAutoReloadDelaySec", "bDisablePlayerCollision", "fAVDCarryWeightBase",
        "fAbsolutePlayerInventoryWeightLimit", "fInvisibilityMaxRefraction",
        "fInvisibilityMinRefraction", "fJetpackThrustInitial", "fJetpackThrustSustained",
        "fJetpackTimeToSustained", "bJetpackInitialThrustOnlyOnTakeoff",
        "fAimStabilityCrouchedScopeMult", "fPropModScaleWeapDurability",
        "fLockpickSkillSweetSpotBase", "fSweetSpotEasy", "fSweetSpotAverage",
        "fSweetSpotHard", "fSweetSpotVeryHard", "iHackingMinWords", "iHackingMaxWords",
        "fCombatMusicNearCombatInnerRadius", "fCombatMusicNearCombatOuterRadius",
        "fCombatMusicPlayerTargetedThreatRatio", "pkKeywords", "fAttackActionPointCost",
        "fspeed", "fweight", "iDurability", "ihealth", "bHoldInputToPower",
        "fMinPowerPerShot", "poAmmo", "paAimModel", "ppOverrideProjectile",
        "uNumProjectiles", "vaActorValues"
    };

    std::map<std::string, std::string> settingTypes = {
        {"fVATSMaxChance", "float"},
        {"fVATSChancePerceptionMultiplier", "float"},
        {"fVATSMaxEngageDistance", "float"},
        {"fVATSChanceRaycastVisibilityMininmum", "float"},
        {"fVATSPingCost", "float"},
        {"fVATSCriticalChargeBase", "float"},
        {"fVATSCriticalChargeBonusMult", "float"},
        {"fVATSCriticalChargeLimit", "float"},
        {"fVATSCriticalChargeMult", "float"},
        {"fPlayerAutoReloadDelaySec", "float"},
        {"bDisablePlayerCollision", "bool"},
        {"fAVDCarryWeightBase", "float"},
        {"fAbsolutePlayerInventoryWeightLimit", "float"},
        {"fInvisibilityMaxRefraction", "float"},
        {"fInvisibilityMinRefraction", "float"},
        {"fJetpackThrustInitial", "float"},
        {"fJetpackThrustSustained", "float"},
        {"fJetpackTimeToSustained", "float"},
        {"bJetpackInitialThrustOnlyOnTakeoff", "bool"},
        {"fAimStabilityCrouchedScopeMult", "float"},
        {"fPropModScaleWeapDurability", "float"},
        {"fLockpickSkillSweetSpotBase", "float"},
        {"fSweetSpotEasy", "float"},
        {"fSweetSpotAverage", "float"},
        {"fSweetSpotHard", "float"},
        {"fSweetSpotVeryHard", "float"},
        {"iHackingMinWords", "int"},
        {"iHackingMaxWords", "int"},
        {"fCombatMusicNearCombatInnerRadius", "float"},
        {"fCombatMusicNearCombatOuterRadius", "float"},
        {"fCombatMusicPlayerTargetedThreatRatio", "float"},
        {"pkKeywords", "pointer"},
        {"fAttackActionPointCost", "float"},
        {"fspeed", "float"},
        {"fweight", "float"},
        {"iDurability", "int"},
        {"ihealth", "int"},
        {"bHoldInputToPower", "bool"},
        {"fMinPowerPerShot", "float"},
        {"poAmmo", "pointer"},
        {"paAimModel", "pointer"},
        {"ppOverrideProjectile", "pointer"},
        {"uNumProjectiles", "unsigned int"},
        {"ActorValues", "array"}
    };

    std::ofstream log_file("memscan_detailed.log", std::ios::out);
    if (!log_file.is_open()) {
        std::cerr << "Could not open log file for writing\n";
        return 1;
    }

    DWORD pid = get_process_id_by_name(process_name, log_file);
    if (pid == 0) {
        log_message(log_file, "Process %s not found.\n", process_name.c_str());
        std::cerr << "Process not found. Check the process name and ensure it's running.\n";
        return 1;
    }
    log_message(log_file, "Found process %s with PID: %u\n", process_name.c_str(), pid);
    std::cout << "Found process " << process_name << " with PID: " << pid << "\n";

    uintptr_t base_addr = get_base_address(pid, process_name, log_file);
    if (base_addr == 0) {
        log_message(log_file, "Failed to get base address for process ID %u.\n", pid);
        std::cerr << "Failed to get base address.\n";
        return 1;
    }
    log_message(log_file, "Base address of %s: 0x%llX\n", process_name.c_str(), static_cast<unsigned long long>(base_addr));
    std::cout << "Base address of " << process_name << ": 0x" << std::hex << base_addr << std::dec << "\n";

    UniqueHandle hProcess(OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid));
    if (!hProcess) {
        log_message(log_file, "Failed to open process with PID %u. Error: %lu\n", pid, GetLastError());
        std::cerr << "Failed to open process. Ensure you have the necessary permissions.\n";
        return 1;
    }
    log_message(log_file, "Successfully opened process handle.\n");

    std::vector<Section> all_sections;
    if (!get_sections(hProcess.get(), base_addr, all_sections, log_file)) {
        log_message(log_file, "Failed to retrieve sections.\n");
        std::cerr << "Failed to retrieve sections.\n";
        return 1;
    }

    for (const auto& section : all_sections) {
        log_message(log_file, "Section %s: Start=0x%llX, End=0x%llX (Relative: Start=0x%llX, End=0x%llX)\n",
            section.name.c_str(),
            static_cast<unsigned long long>(section.start),
            static_cast<unsigned long long>(section.end),
            static_cast<unsigned long long>(section.start - base_addr),
            static_cast<unsigned long long>(section.end - base_addr));
    }

    // Map to store found settings and their relative offsets
    std::map<std::string, uintptr_t> foundSettings;
    for (const auto& setting : gameSettings) {
        foundSettings[setting] = 0; // Initialize with 0 offset
    }

    // For each game setting string, search in the sections
    for (const auto& setting : gameSettings) {
        bool found = false;
        for (const auto& section : all_sections) {
            uintptr_t address = find_string_in_section(hProcess.get(), section.start, section.end, setting, section.name, log_file, base_addr);
            if (address != 0) {
                uintptr_t relativeAddress = address - base_addr;
                foundSettings[setting] = relativeAddress;
                log_message(log_file, "Found \"%s\" at relative address 0x%llX (absolute address: 0x%llX) in section %s\n",
                    setting.c_str(),
                    static_cast<unsigned long long>(relativeAddress),
                    static_cast<unsigned long long>(address),
                    section.name.c_str());
                found = true;
                break; // If found in one section, no need to search other sections
            }
        }
        if (!found) {
            log_message(log_file, "Did not find \"%s\" in any section.\n", setting.c_str());
        }
    }

    // Output the results in the desired format
    log_message(log_file, "\n$gameSettings = [\n");
    std::cout << "\n$gameSettings = [\n";
    for (const auto& setting : gameSettings) {
        uintptr_t offset = foundSettings[setting];
        std::string settingType = settingTypes[setting];

        if (offset != 0) {
            log_message(log_file, "    \"%s\" => 0x%llX, // %s\n", setting.c_str(), static_cast<unsigned long long>(offset), settingType.c_str());
            std::cout << "    \"" << setting << "\" => 0x" << std::hex << offset << ", // " << settingType << "\n";
        }
        else {
            log_message(log_file, "    // \"%s\" not found\n", setting.c_str());
            std::cout << "    // \"" << setting << "\" not found\n";
        }
    }
    log_message(log_file, "];\n");
    std::cout << "];\n";

    std::cout << "Press Enter to exit...";
    std::cin.get();

    return 0;
}