Конкретно этот случай - это опкод, и тут есть свои особенности.
Есть много вариантов, опишу основные.
1. Используя CLEO SDK, написать замену опкоду 02DE.+ самый рациональный способ. Тебе надо заменить стандартный опкод - в CLEO SDK это продумано - ты используешь CLEO SDK.
+ чистый и понятный код.
— обязательно наличие библиотеки CLEO 2.0 (или выше).
— надо писать весь код опкода.
2. Заменить весь (или почти весь) код опкода.Сделать вызов своей функции, с 0x446A93. Функция при этом должна быть обьявлена как fastcall (чтобы в ecx получить структуру скрипта (CRunningScript).
В этой функции пишем весь код опкода, от получения параметров (CollectParameters) до записи результата (UpdateCompareFlag).
Кусок с 0x446A98 по 0x446AE4 заполняем no-op'ами.
void __fastcall OpcodePlayerDrivingTaxiVehicle(CRunningScript *script) {
script->CollectParameters(&script->m_nIp, 1);
bool isTaxiModel = false;
// ...
script->UpdateCompareFlag(isTaxiModel);
}
...
patch::RedirectCall(0x446A93, OpcodePlayerDrivingTaxiVehicle);
patch::Nop(0x446A98, 0x4C); // или сделать jump на 0x446AE4
+ более-менее чистый и понятный код.
— надо писать весь код опкода.
3. Внедрять свой код по конкретному адресу, и заменить кое-какой кусок опкода (тот, где идет проверка id).Тут тоже много вариантов.
3.1. Путём патчинга видоизменяем код в exe.Проверка id начинается тут: 0x446AC5. id у нас записан в регистре ebx. начиная с адреса 0x446AC5, мы можем видоизменить код, чтобы он выглядел так:
push ebx
call CheckIfTaxiModel
jmp 0x446ADC
Но тут также важно проследить, чтобы после вызова функции CheckIfTaxiModel не изменился регистр edi (в этом регистре хранится структура CRunningScript). Если будут проблемы - можно также перед вызовом функции добавить pushad, а после - popad.
Функция CheckIfTaxiModel должна принимать один параметр и возвращать true/false. Соглашение вызова - stdcall (можно сделать cdecl, и добавить 'add esp, 4' после вызова).
bool __stdcall CheckIfTaxiModel(int id) {
bool isTaxiModel = false;
// ...
return isTaxiModel;
}
...
patch::SetUChar(0x446AC5, 0x53); // команда 'push ebx'
patch::RedirectCall(0x446AC6, CheckIfTaxiModel); // call
patch::RedirectJump(0x446ACB, 0x446ADC); // jump
+ не надо переписывать код опкода полностью - напишем только функцию, которая сверяет id и возвращает результат.
— часть кода будет не совсем "прозрачной", а его написание может вызвать трудности (по сути, надо будет продумывать запись ассемблерных инструкций).
— надо следить за регистрами.
3.2. Пишем функцию - ассемблерный код.void __declspec(naked) MyHook_446AC5() { // инжект по адресу 0x446AC5, в ebx записан id модели
__asm {
cmp ebx, 110
jz SET_TRUE
cmp ebx, 128
jz SET_TRUE
cmp ebx, 148
jz SET_TRUE
cmp ebx, <какой-то id>
jz SET_TRUE
jmp END_CHECK
SET_TRUE:
mov al, 1
END_CHECK:
mov ecx, 0x446ADC
jmp ecx
}
}
...
patch::RedirectJump(0x446AC5, MyHook_446AC5);
+ не надо переписывать код опкода полностью - пишем только код, который сверяет id.
— надо писать ассемблерный код.
— в коде будет присутствовать ассемблерный код.
3.3. Новый вид инжекта в plugin-sdkvoid MyCheckIfTaxiModel(int modelId, bool &result) {
result = false;
// ...
}
...
InstallHook<0x446AC5, 0x17, reg<ebx>, reg<eax&>>(MyCheckIfTaxiModel);
// точка внедрения
// размер кода, который мы пропускаем
// получаемые параметры
// регистр ebx - id модели
// регистр eax - для записи (&)
— пока что только в разработке.
PS То, что показано в пункте 3.3 - это, так скажем, "обертка" над тем, что показано в пункте 3.1.
PPS Вариант 3.1.
#include "plugin_III.h"
#include "extensions\ScriptCommands.h"
using namespace plugin;
using namespace plugin::test;
class OpcodeTaxi {
public:
static bool __stdcall CheckIfTaxiModel(int id) {
switch (id) {
case 110:
case 128:
case 148:
case 146:
return true;
}
return false;
}
OpcodeTaxi() {
patch::SetUChar(0x446AC5, 0x53); // команда 'push ebx'
patch::RedirectCall(0x446AC6, CheckIfTaxiModel); // call
patch::RedirectJump(0x446ACB, reinterpret_cast<void *>(0x446ADC)); // jump
Events::processScriptsEvent += [] {
if (ScriptCommand<IS_PLAYER_IN_TAXI>(0))
ScriptCommand<PRINT_NOW>("FEC_INC", 150, 1);
};
}
} myPlugin;