SA:MP ASI ASP | Aspect Ratio - без увеличенного FOV

yorenov

Участник
Автор темы
63
57
Версия SA-MP
  1. 0.3.7-R3
Автор скрипта: @wojciech?
Автор переписи на АСИ плагин - я

Не люблю ЛУА, поэтому решил переписать на C++, логично?
Работает на SAMP 0.3.7 R3 (r1 поддержку не добавлял, там сампапи регает команду + вывод сообщений в чат, если зайдет многим - сделаю и для r1)

Зависимостей для юзера не имеет, полная копия функционала от
https://www.blast.hk/threads/222328/

radmir-2025-11-14-10_52-37.jpg
radmir-2025-11-14-10_52-44.jpg


source:
C++:
#include <Windows.h>
#include <kthook/kthook.hpp>
#include <rakhook/rakhook.hpp>
#include <fstream>
#include <string>
#include <map>
#include <memory>
#include <cstdio>
#include <utility>

#include "sampapi/0.3.7-R3-1/CChat.h"
#include "sampapi/0.3.7-R3-1/CInput.h"

enum eCamMode {
    MODE_SNIPER = 7,
    MODE_BEHINDCAR = 4,
    MODE_CAM_ON_A_STRING = 16,
    MODE_BEHINDBOAT = 28,
    MODE_AIMWEAPON_FROMCAR = 19
};

struct CCam {
    char _padding[0x4C];
    int nMode;
    float fFOV;
};

float* fAspectRatio = reinterpret_cast<float*>(0xC3EFA4);
float oldFOV = 70.0f;

class SimpleIni {
private:
    std::map<std::string, std::map<std::string, std::string>> data;
    std::string filename;

public:
    explicit SimpleIni(std::string  file) : filename(std::move(file)) {}

    bool Load() {
        std::ifstream file(filename);
        if (!file.is_open()) {
            return false;
        }

        std::string line;
        std::string currentSection = "main";

        while (std::getline(file, line)) {
            line.erase(0, line.find_first_not_of(" \t"));
            line.erase(line.find_last_not_of(" \t") + 1);

            if (line.empty() || line[0] == ';' || line[0] == '#') {
                continue;
            }

            if (line[0] == '[' && line[line.length() - 1] == ']') {
                currentSection = line.substr(1, line.length() - 2);
                continue;
            }

            size_t equalsPos = line.find('=');
            if (equalsPos != std::string::npos) {
                std::string key = line.substr(0, equalsPos);
                std::string value = line.substr(equalsPos + 1);

                key.erase(0, key.find_first_not_of(" \t"));
                key.erase(key.find_last_not_of(" \t") + 1);
                value.erase(0, value.find_first_not_of(" \t"));
                value.erase(value.find_last_not_of(" \t") + 1);

                data[currentSection][key] = value;
            }
        }

        file.close();
        return true;
    }

    bool Save() {
        std::ofstream file(filename);
        if (!file.is_open()) {
            return false;
        }

        for (const auto& section : data) {
            file << "[" << section.first << "]\n";
            for (const auto& keyValue : section.second) {
                file << keyValue.first << "=" << keyValue.second << "\n";
            }
            file << "\n";
        }

        file.close();
        return true;
    }

    float GetFloat(const std::string& section, const std::string& key, float defaultValue) {
        auto sectionIt = data.find(section);
        if (sectionIt != data.end()) {
            auto keyIt = sectionIt->second.find(key);
            if (keyIt != sectionIt->second.end()) {
                try {
                    return std::stof(keyIt->second);
                } catch (...) {
                    return defaultValue;
                }
            }
        }
        return defaultValue;
    }

    void SetFloat(const std::string& section, const std::string& key, float value) {
        data[section][key] = std::to_string(value);
    }

    void SetDefaultValues() {
        if (!data["main"].contains("asp")) {
            data["main"]["asp"] = "1";
        }
    }
};

std::unique_ptr<SimpleIni> config;

void PrintStringNow(const char* text, const unsigned int time) {
    reinterpret_cast<void(__cdecl*)(const char *, unsigned int, unsigned short, bool)>(0x69F1E0)(text, time, 0, false);
    if (const auto chat = sampapi::v037r3::RefChat()) {
        chat->AddMessage(-1, text);
    }
}

void EnsureConfigExists() {
    config = std::make_unique<SimpleIni>("ASI_BASE_ASPECT_RATIO.ini");

    config->Load();
    config->SetDefaultValues();
    config->Save();
}

void AspectRatioCommand(const char* param) {
    char* endptr;
    const float newAspect = strtof(param, &endptr);
    if (param == endptr) {
        PrintStringNow("Use /asp [value]", 1000);
        return;
    }

    if (newAspect < 0.01f || newAspect > 10.0f) {
        PrintStringNow("Invalid value! Use values between 0.01 and 10.0", 1000);
        return;
    }

    *fAspectRatio = newAspect;
    config->SetFloat("main", "asp", newAspect);
    config->Save();

    char buffer[64];
    sprintf_s(buffer, "Aspect Ratio: %.2f", newAspect);
    PrintStringNow(buffer, 1000);
}

bool ApplyMemoryPatch(void* address, const unsigned char* patch, size_t patchSize) {
    DWORD oldProtect;

    if (!VirtualProtect(address, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect)) {
        return false;
    }

    memcpy(address, patch, patchSize);

    DWORD temp;
    if (!VirtualProtect(address, patchSize, oldProtect, &temp)) {
        return false;
    }

    return true;
}

using CTimer_UpdateSignature = void(__cdecl*)();
kthook::kthook_simple<CTimer_UpdateSignature> CTimerHook{};

using CCam_Process_Sig = int(__thiscall*)(CCam*);
kthook::kthook_simple<CCam_Process_Sig> CCam_Process_Hook;

void CTimer_Update(const decltype(CTimerHook)& hook) {
    static bool init{ false };
    if (!init && rakhook::initialize()) {
        EnsureConfigExists();

        *fAspectRatio = config->GetFloat("main", "asp", 1.0f);

        const unsigned char nop6[] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }; // 6 NOP
        constexpr unsigned char nop2[] = { 0x90, 0x90 }; // 2 NOP

        ApplyMemoryPatch(reinterpret_cast<void *>(0x6FF452), nop6, sizeof(nop6));
        ApplyMemoryPatch(reinterpret_cast<void *>(0x524B7F), nop2, sizeof(nop2));

        sampapi::v037r3::RefInputBox()->AddCommand("asp", AspectRatioCommand);

        CCam_Process_Hook.set_dest(reinterpret_cast<void*>(0x526FC0));
        CCam_Process_Hook.set_cb([&](const auto& hook, CCam* cam) -> int {
            int result = 0;

            if (cam->nMode != MODE_SNIPER) {
                cam->fFOV = oldFOV;
                result = hook.get_trampoline()(cam);

                if (cam->nMode == MODE_BEHINDCAR ||
                    cam->nMode == MODE_CAM_ON_A_STRING ||
                    cam->nMode == MODE_BEHINDBOAT ||
                    cam->nMode == MODE_AIMWEAPON_FROMCAR) {
                    oldFOV = cam->fFOV;
                } else {
                    oldFOV = 70.0f;
                }

                float newFOV = (cam->fFOV * (*fAspectRatio)) * 0.8f;
                if (newFOV > 120.0f) newFOV = 120.0f;
                cam->fFOV = newFOV;
            } else {
                result = hook.get_trampoline()(cam);
            }

            return result;
        });
        CCam_Process_Hook.install();

        PrintStringNow("Aspect Ratio mod loaded successfully!", 1000);
        init = true;
    }
    hook.get_trampoline()();
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
        case DLL_PROCESS_ATTACH: {
            DisableThreadLibraryCalls(hModule);
            CTimerHook.set_dest(0x561B10);
            CTimerHook.set_cb(&CTimer_Update);
            CTimerHook.install();
            break;
        }
        case DLL_PROCESS_DETACH: {
            CTimerHook.remove();
            CCam_Process_Hook.remove();
            if (config) {
                config.reset();
            }
            break;
        }
        default: {
            break;
        }
    }
    return TRUE;
}
Жду разбор ошибок моего кода :)
 

Вложения

  • ASI_BASE_ASPECT_RATIO.asi
    1 MB · Просмотры: 63