- 63
- 57
- Версия SA-MP
-
- 0.3.7-R3
Автор скрипта: @wojciech?
Автор переписи на АСИ плагин - я
Не люблю ЛУА, поэтому решил переписать на C++, логично?
Работает на SAMP 0.3.7 R3 (r1 поддержку не добавлял, там сампапи регает команду + вывод сообщений в чат, если зайдет многим - сделаю и для r1)
Зависимостей для юзера не имеет, полная копия функционала от https://www.blast.hk/threads/222328/
Не люблю ЛУА, поэтому решил переписать на C++, логично?
Работает на SAMP 0.3.7 R3 (r1 поддержку не добавлял, там сампапи регает команду + вывод сообщений в чат, если зайдет многим - сделаю и для r1)
Зависимостей для юзера не имеет, полная копия функционала от https://www.blast.hk/threads/222328/
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;
}