#include "CustomOpcodeSystem.h" #include "ScriptEngine.h" #include "CleoInstance.h" #include "GameVersionManager.h" #include "CPed.h" #include "CVehicle.h" #include "CObject.h" #include "CPool.h" #include "CMarker.h" #include "CHandling.h" #include "CModel.h" #include #include namespace CLEO { // predeclarations of custom opcode handlers OpcodeResult STDCALL opcode_0A8C(CScriptThread *thread); OpcodeResult STDCALL opcode_0A8D(CScriptThread *thread); OpcodeResult STDCALL opcode_0A8E(CScriptThread *thread); OpcodeResult STDCALL opcode_0A8F(CScriptThread *thread); OpcodeResult STDCALL opcode_0A90(CScriptThread *thread); OpcodeResult STDCALL opcode_0A91(CScriptThread *thread); OpcodeResult STDCALL opcode_0A92(CScriptThread *thread); OpcodeResult STDCALL opcode_0A93(CScriptThread *thread); OpcodeResult STDCALL opcode_0A94(CScriptThread *thread); OpcodeResult STDCALL opcode_0A95(CScriptThread *thread); OpcodeResult STDCALL opcode_0A96(CScriptThread *thread); OpcodeResult STDCALL opcode_0A97(CScriptThread *thread); OpcodeResult STDCALL opcode_0A98(CScriptThread *thread); OpcodeResult STDCALL opcode_0A99(CScriptThread *thread); OpcodeResult STDCALL opcode_0A9A(CScriptThread *thread); OpcodeResult STDCALL opcode_0A9B(CScriptThread *thread); OpcodeResult STDCALL opcode_0A9C(CScriptThread *thread); OpcodeResult STDCALL opcode_0A9D(CScriptThread *thread); OpcodeResult STDCALL opcode_0A9E(CScriptThread *thread); OpcodeResult STDCALL opcode_0A9F(CScriptThread *thread); OpcodeResult STDCALL opcode_0AA0(CScriptThread *thread); OpcodeResult STDCALL opcode_0AA1(CScriptThread *thread); OpcodeResult STDCALL opcode_0AA2(CScriptThread *thread); OpcodeResult STDCALL opcode_0AA3(CScriptThread *thread); OpcodeResult STDCALL opcode_0AA4(CScriptThread *thread); OpcodeResult STDCALL opcode_0AA5(CScriptThread *thread); OpcodeResult STDCALL opcode_0AA6(CScriptThread *thread); OpcodeResult STDCALL opcode_0AA7(CScriptThread *thread); OpcodeResult STDCALL opcode_0AA8(CScriptThread *thread); OpcodeResult STDCALL opcode_0AA9(CScriptThread *thread); OpcodeResult STDCALL opcode_0AAA(CScriptThread *thread); OpcodeResult STDCALL opcode_0AAB(CScriptThread *thread); OpcodeResult STDCALL opcode_0AAC(CScriptThread *thread); OpcodeResult STDCALL opcode_0AAD(CScriptThread *thread); OpcodeResult STDCALL opcode_0AAE(CScriptThread *thread); OpcodeResult STDCALL opcode_0AAF(CScriptThread *thread); OpcodeResult STDCALL opcode_0AB0(CScriptThread *thread); OpcodeResult STDCALL opcode_0AB1(CScriptThread *thread); OpcodeResult STDCALL opcode_0AB2(CScriptThread *thread); OpcodeResult STDCALL opcode_0AB3(CScriptThread *thread); OpcodeResult STDCALL opcode_0AB4(CScriptThread *thread); OpcodeResult STDCALL opcode_0AB5(CScriptThread *thread); OpcodeResult STDCALL opcode_0AB6(CScriptThread *thread); OpcodeResult STDCALL opcode_0AB7(CScriptThread *thread); OpcodeResult STDCALL opcode_0AB8(CScriptThread *thread); OpcodeResult STDCALL opcode_0AB9(CScriptThread *thread); OpcodeResult STDCALL opcode_0ABA(CScriptThread *thread); OpcodeResult STDCALL opcode_0ABB(CScriptThread *thread); OpcodeResult STDCALL opcode_0ABC(CScriptThread *thread); OpcodeResult STDCALL opcode_0ABD(CScriptThread *thread); OpcodeResult STDCALL opcode_0ABE(CScriptThread *thread); OpcodeResult STDCALL opcode_0ABF(CScriptThread *thread); OpcodeResult STDCALL opcode_0AC0(CScriptThread *thread); OpcodeResult STDCALL opcode_0AC1(CScriptThread *thread); OpcodeResult STDCALL opcode_0AC2(CScriptThread *thread); OpcodeResult STDCALL opcode_0AC3(CScriptThread *thread); OpcodeResult STDCALL opcode_0AC4(CScriptThread *thread); OpcodeResult STDCALL opcode_0AC5(CScriptThread *thread); OpcodeResult STDCALL opcode_0AC6(CScriptThread *thread); OpcodeResult STDCALL opcode_0AC7(CScriptThread *thread); OpcodeResult STDCALL opcode_0AC8(CScriptThread *thread); OpcodeResult STDCALL opcode_0AC9(CScriptThread *thread); OpcodeResult STDCALL opcode_0ACA(CScriptThread *thread); OpcodeResult STDCALL opcode_0ACB(CScriptThread *thread); OpcodeResult STDCALL opcode_0ACC(CScriptThread *thread); OpcodeResult STDCALL opcode_0ACD(CScriptThread *thread); OpcodeResult STDCALL opcode_0ACE(CScriptThread *thread); OpcodeResult STDCALL opcode_0ACF(CScriptThread *thread); OpcodeResult STDCALL opcode_0AD0(CScriptThread *thread); OpcodeResult STDCALL opcode_0AD1(CScriptThread *thread); OpcodeResult STDCALL opcode_0AD2(CScriptThread *thread); OpcodeResult STDCALL opcode_0AD3(CScriptThread *thread); OpcodeResult STDCALL opcode_0AD4(CScriptThread *thread); OpcodeResult STDCALL opcode_0AD5(CScriptThread *thread); OpcodeResult STDCALL opcode_0AD6(CScriptThread *thread); OpcodeResult STDCALL opcode_0AD7(CScriptThread *thread); OpcodeResult STDCALL opcode_0AD8(CScriptThread *thread); OpcodeResult STDCALL opcode_0AD9(CScriptThread *thread); OpcodeResult STDCALL opcode_0ADA(CScriptThread *thread); OpcodeResult STDCALL opcode_0ADB(CScriptThread *thread); OpcodeResult STDCALL opcode_0ADC(CScriptThread *thread); OpcodeResult STDCALL opcode_0ADD(CScriptThread *thread); OpcodeResult STDCALL opcode_0ADE(CScriptThread *thread); OpcodeResult STDCALL opcode_0ADF(CScriptThread *thread); OpcodeResult STDCALL opcode_0AE0(CScriptThread *thread); OpcodeResult STDCALL opcode_0AE1(CScriptThread *thread); OpcodeResult STDCALL opcode_0AE2(CScriptThread *thread); OpcodeResult STDCALL opcode_0AE3(CScriptThread *thread); OpcodeResult STDCALL opcode_0AE4(CScriptThread *thread); OpcodeResult STDCALL opcode_0AE5(CScriptThread *thread); OpcodeResult STDCALL opcode_0AE6(CScriptThread *thread); OpcodeResult STDCALL opcode_0AE7(CScriptThread *thread); OpcodeResult STDCALL opcode_0AE8(CScriptThread *thread); OpcodeResult STDCALL opcode_0AE9(CScriptThread *thread); OpcodeResult STDCALL opcode_0AEA(CScriptThread *thread); OpcodeResult STDCALL opcode_0AEB(CScriptThread *thread); OpcodeResult STDCALL opcode_0AEC(CScriptThread *thread); OpcodeResult STDCALL opcode_0AED(CScriptThread *thread); OpcodeResult STDCALL opcode_0AEE(CScriptThread *thread); OpcodeResult STDCALL opcode_0AEF(CScriptThread *thread); CustomOpcodeHandler customOpcodeHandlers[100] = { opcode_0A8C, opcode_0A8D, opcode_0A8E, opcode_0A8F, opcode_0A90, opcode_0A91, opcode_0A92, opcode_0A93, opcode_0A94, opcode_0A95, opcode_0A96, opcode_0A97, opcode_0A98, opcode_0A99, opcode_0A9A, opcode_0A9B, opcode_0A9C, opcode_0A9D, opcode_0A9E, opcode_0A9F, opcode_0AA0, opcode_0AA1, opcode_0AA2, opcode_0AA3, opcode_0AA4, opcode_0AA5, opcode_0AA6, opcode_0AA7, opcode_0AA8, opcode_0AA9, opcode_0AAA, opcode_0AAB, opcode_0AAC, opcode_0AAD, opcode_0AAE, opcode_0AAF, opcode_0AB0, opcode_0AB1, opcode_0AB2, opcode_0AB3, opcode_0AB4, opcode_0AB5, opcode_0AB6, opcode_0AB7, opcode_0AB8, opcode_0AB9, opcode_0ABA, opcode_0ABB, opcode_0ABC, opcode_0ABD, opcode_0ABE, opcode_0ABF, opcode_0AC0, opcode_0AC1, opcode_0AC2, opcode_0AC3, opcode_0AC4, opcode_0AC5, opcode_0AC6, opcode_0AC7, opcode_0AC8, opcode_0AC9, opcode_0ACA, opcode_0ACB, opcode_0ACC, opcode_0ACD, opcode_0ACE, opcode_0ACF, opcode_0AD0, opcode_0AD1, opcode_0AD2, opcode_0AD3, opcode_0AD4, opcode_0AD5, opcode_0AD6, opcode_0AD7, opcode_0AD8, opcode_0AD9, opcode_0ADA, opcode_0ADB, opcode_0ADC, opcode_0ADD, opcode_0ADE, opcode_0ADF, opcode_0AE0, opcode_0AE1, opcode_0AE2, opcode_0AE3, opcode_0AE4, opcode_0AE5, opcode_0AE6, opcode_0AE7, opcode_0AE8, opcode_0AE9, opcode_0AEA, opcode_0AEB, opcode_0AEC, opcode_0AED, opcode_0AEE, opcode_0AEF, }; typedef OpcodeResult THISCALL (*OpcodeHandler)(CScriptThread *thread, unsigned short opcode); OpcodeHandler *oldOpcodeHandlerTable; OpcodeHandler newOpcodeHandlerTable[329]; CustomOpcodeHandler extraOpcodeHandlers[100][300]; // opcode handler for opcodes, defined by user with cleo api OpcodeResult THISCALL extraOpcodeHandler(CScriptThread *thread, unsigned short opcode) { return extraOpcodeHandlers[opcode % 100][opcode / 100 - 28](thread); }; // opcode handler for custom opcodes OpcodeResult THISCALL customOpcodeHandler(CScriptThread *thread, unsigned short opcode) { return customOpcodeHandlers[opcode - 0x0A8C](thread); } CPedPool **pedPool = nullptr; CVehiclePool **vehiclePool = nullptr; CObjectPool **objectPool = nullptr; inline CPedPool& GetPedPool() { return **pedPool; } inline CVehiclePool& GetVehiclePool() { return **vehiclePool; } inline CObjectPool& GetObjectPool() { return **objectPool; } const char * CDECL (*GetUserDirectory)(); float CDECL (*FindGroundZ)(float x, float y); CMarker *radarBlips; CHandling *handling; CPed * CDECL (*GetPlayerPed)(unsigned); CModel **models; void CDECL (*SpawnCar)(unsigned); WORD last_opcode = 0; char last_thread[8] = "none"; ptrdiff_t last_off = -1; char ScriptExecutionLoop() { CScriptThread *thread; OpcodeResult res; asm volatile ("" : "=S"(thread)); do { WORD opcode = *(WORD *)thread->ip; ptrdiff_t off = thread->IsCustom ? thread->ip - thread->baseIp : thread->ip - scmBlock; last_opcode = opcode; last_off = off; memcpy(last_thread, thread->threadName, 8); thread->notFlag = (opcode & 0x8000) != 0; opcode &= 0x7FFF; thread->ip += 2; res = newOpcodeHandlerTable[opcode / 100](thread, opcode); } while (res == OR_CONTINUE); return 0; } CustomOpcodeSystem::~CustomOpcodeSystem() { // TRACE("Last opcode executed %04X at %s:%d", last_opcode, last_thread, last_off); } void CustomOpcodeSystem::inject(CodeInjector& inj) { TRACE("Injecting CustomOpcodeSystem..."); GameVersionManager& gvm = GetThisInstance().versionManager; oldOpcodeHandlerTable = gvm.TranslateMemoryAddress(MA_OPCODE_HANDLER); // add handler for custom opcodes oldOpcodeHandlerTable[27] = customOpcodeHandler; // replace old OpcodeHandlerTable with the new one inj.MemoryWrite(gvm.TranslateMemoryAddress(MA_OPCODE_HANDLER_REF), &newOpcodeHandlerTable[0]); // copy old table to the new std::copy(oldOpcodeHandlerTable, oldOpcodeHandlerTable + 28, newOpcodeHandlerTable); // fill the rest with default handler std::fill(newOpcodeHandlerTable + 28, newOpcodeHandlerTable + 329, extraOpcodeHandler); pedPool = gvm.TranslateMemoryAddress(MA_PED_POOL); vehiclePool = gvm.TranslateMemoryAddress(MA_VEHICLE_POOL); objectPool = gvm.TranslateMemoryAddress(MA_OBJECT_POOL); GetUserDirectory = gvm.TranslateMemoryAddress(MA_GET_USER_DIR_FUNCTION); FindGroundZ = gvm.TranslateMemoryAddress(MA_FIND_GROUND_Z_FUNCTION); radarBlips = gvm.TranslateMemoryAddress(MA_RADAR_BLIPS); handling = gvm.TranslateMemoryAddress(MA_HANDLING); GetPlayerPed = gvm.TranslateMemoryAddress(MA_GET_PLAYER_PED_FUNCTION); models = gvm.TranslateMemoryAddress(MA_MODELS); SpawnCar = gvm.TranslateMemoryAddress(MA_SPAWN_CAR_FUNCTION); if (gvm.GetGameVersion() == GV_US10) { // inj.Nop(0x469FB0, 0x469FFB - 0x469FB0); // inj.ReplaceFunction(ScriptExecutionLoop, 0x469FB0); } } inline CScriptThread& operator>>(CScriptThread& thread, unsigned& uval) { GetScriptParams(&thread, 1); uval = opcodeParams[0].dwParam; return thread; } inline CScriptThread& operator<<(CScriptThread& thread, unsigned uval) { opcodeParams[0].dwParam = uval; SetScriptParams(&thread, 1); return thread; } inline CScriptThread& operator>>(CScriptThread& thread, int& nval) { GetScriptParams(&thread, 1); nval = opcodeParams[0].nParam; return thread; } inline CScriptThread& operator<<(CScriptThread& thread, int nval) { opcodeParams[0].nParam = nval; SetScriptParams(&thread, 1); return thread; } inline CScriptThread& operator>>(CScriptThread& thread, float& fval) { GetScriptParams(&thread, 1); fval = opcodeParams[0].fParam; return thread; } inline CScriptThread& operator<<(CScriptThread& thread, float fval) { opcodeParams[0].fParam = fval; SetScriptParams(&thread, 1); return thread; } inline CScriptThread& operator>>(CScriptThread& thread, RwV3d& vec) { GetScriptParams(&thread, 3); vec.x = opcodeParams[0].fParam; vec.y = opcodeParams[1].fParam; vec.z = opcodeParams[2].fParam; return thread; } inline CScriptThread& operator<<(CScriptThread& thread, const RwV3d& vec) { opcodeParams[0].fParam = vec.x; opcodeParams[1].fParam = vec.y; opcodeParams[2].fParam = vec.z; SetScriptParams(&thread, 3); return thread; } template inline CScriptThread& operator>>(CScriptThread& thread, T *& pval) { GetScriptParams(&thread, 1); pval = reinterpret_cast(opcodeParams[0].pParam); return thread; } template inline CScriptThread& operator<<(CScriptThread& thread, T *pval) { opcodeParams[0].pParam = (void *)(pval); SetScriptParams(&thread, 1); return thread; } inline CScriptThread& operator>>(CScriptThread& thread, memory_pointer& pval) { GetScriptParams(&thread, 1); pval = opcodeParams[0].pParam; return thread; } template inline CScriptThread& operator<<(CScriptThread& thread, memory_pointer pval) { opcodeParams[0].pParam = pval; SetScriptParams(&thread, 1); return thread; } // read string parameter according to convention on strings char *readString(CScriptThread *thread, char* buf = nullptr, BYTE size = 0) { auto paramType = *thread->ip; if (!paramType) return nullptr; if (paramType >= 1 && paramType <= 8) { // process parameter as a pointer to string GetScriptParams(thread, 1); return opcodeParams[0].szParam; } else { // process as scm string if (!buf) { static char result[128]; std::fill(result, result + 128, '\0'); GetScriptStringParam(thread, result, 100); return result; } else { size = size > 100 || size ? 100 : size; std::fill(buf, buf + size, '\0'); GetScriptStringParam(thread, buf, size); return buf; } } } // perform 'sprintf'-operation for parameters, passed through SCM int format(CScriptThread *thread, char *str, size_t len, const char *format) { unsigned int written = 0; const char *iter = format; char bufa[256], fmtbufa[64], *fmta; while (*iter) { while (*iter && *iter != '%') { if (written++ >= len) return -1; *str++ = *iter++; } if (*iter == '%') { if (iter[1] == '%') { if (written++ >= len) return -1; *str++ = '%'; /* "%%"->'%' */ iter += 2; continue; } //get flags and width specifier fmta = fmtbufa; *fmta++ = *iter++; while (*iter == '0' || *iter == '+' || *iter == '-' || *iter == ' ' || *iter == '*' || *iter == '#') { if (*iter == '*') { char *buffiter = bufa; //get width GetScriptParams(thread, 1); _itoa(opcodeParams[0].dwParam, buffiter, 10); while (*buffiter) *fmta++ = *buffiter++; } else *fmta++ = *iter; iter++; } //get immidiate width value while (isdigit(*iter)) *fmta++ = *iter++; //get precision if (*iter == '.') { *fmta++ = *iter++; if (*iter == '*') { char *buffiter = bufa; GetScriptParams(thread, 1); _itoa(opcodeParams[0].dwParam, buffiter, 10); while (*buffiter) *fmta++ = *buffiter++; } else while (isdigit(*iter)) *fmta++ = *iter++; } //get size if (*iter == 'h' || *iter == 'l') *fmta++ = *iter++; switch (*iter) { case 's': { static const char none[] = "(null)"; const char *astr = readString(thread); const char *striter = astr ? astr : none; while (*striter) { if (written++ >= len) return -1; *str++ = *striter++; } iter++; break; } case 'c': if (written++ >= len) return -1; GetScriptParams(thread, 1); *str++ = (char)opcodeParams[0].nParam; iter++; break; default: { /* For non wc types, use system sprintf and append to wide char output */ /* FIXME: for unrecognised types, should ignore % when printing */ char *bufaiter = bufa; if (*iter == 'p' || *iter == 'P') { GetScriptParams(thread, 1); sprintf(bufaiter, "%08X", opcodeParams[0].dwParam); } else { *fmta++ = *iter; *fmta = '\0'; if (*iter == 'a' || *iter == 'A' || *iter == 'e' || *iter == 'E' || *iter == 'f' || *iter == 'F' || *iter == 'g' || *iter == 'G') { GetScriptParams(thread, 1); sprintf(bufaiter, fmtbufa, opcodeParams[0].fParam); } else { GetScriptParams(thread, 1); sprintf(bufaiter, fmtbufa, opcodeParams[0].pParam); } } while (*bufaiter) { if (written++ >= len) return -1; *str++ = *bufaiter++; } iter++; break; } } } } if (written >= len) return -1; *str++ = 0; return (int)written; } inline void __impl_RetrieveScriptParam(SCRIPT_VAR*) { } template inline void __impl_RetrieveScriptParam(SCRIPT_VAR *, unsigned&, Params&...); template inline void __impl_RetrieveScriptParam(SCRIPT_VAR *, int&, Params&...); template inline void __impl_RetrieveScriptParam(SCRIPT_VAR *, float&, Params&...); template inline void __impl_RetrieveScriptParam(SCRIPT_VAR *, ThisParam *&, Params&...); template inline void __impl_RetrieveScriptParam(SCRIPT_VAR *var, unsigned& thisParam, Params&... restParams) { thisParam = var->dwParam; __impl_RetrieveScriptParam(var + 1, restParams...); } template inline void __impl_RetrieveScriptParam(SCRIPT_VAR *var, int& thisParam, Params&... restParams) { thisParam = var->nParam; __impl_RetrieveScriptParam(var + 1, restParams...); } template inline void __impl_RetrieveScriptParam(SCRIPT_VAR *var, float& thisParam, Params&... restParams) { thisParam = var->fParam; __impl_RetrieveScriptParam(var + 1, restParams...); } template inline void __impl_RetrieveScriptParam(SCRIPT_VAR *var, ThisParam *& thisParam, Params&... restParams) { thisParam = reinterpret_cast(var->pParam); __impl_RetrieveScriptParam(var + 1, restParams...); } template inline void RetrieveScriptParams(CScriptThread *thread, Params&... params) { GetScriptParams(thread, sizeof...(params)); __impl_RetrieveScriptParam(opcodeParams, params...); } inline void ThreadJump(CScriptThread *thread, int off) { thread->ip = off < 0 ? thread->baseIp - off : scmBlock + off; } inline void SkipUnusedParameters(CScriptThread *thread) { while (*thread->ip) GetScriptParams(thread, 1); // skip parameters ++thread->ip; } struct ScmFunction { unsigned short prevScmFunctionId, thisScmFunctionId; BYTE *retnAddress; SCRIPT_VAR savedTls[32]; static const size_t store_size = 0x400; static ScmFunction *Store[store_size]; static size_t allocationPlace; // contains an index of last allocated object void *operator new(size_t size) { size_t start_search = allocationPlace; while (Store[allocationPlace]) // find first unused position in store { if (++allocationPlace >= store_size) allocationPlace = 0; // end of store reached if (allocationPlace == start_search) throw std::bad_alloc(); // the store is filled up } ScmFunction *obj = reinterpret_cast(::operator new(size)); Store[allocationPlace] = obj; return obj; } void operator delete(void *mem) { Store[reinterpret_cast(mem)->thisScmFunctionId] = nullptr; ::operator delete(mem); } ScmFunction(CScriptThread *thread) : prevScmFunctionId(thread->scmFunction), retnAddress(thread->ip) { std::copy(thread->tls, thread->tls + 32, savedTls); SCRIPT_VAR fill_val; fill_val.dwParam = 0; std::fill(thread->tls, thread->tls + 32, fill_val); // fill with zeros thread->scmFunction = thisScmFunctionId = allocationPlace; } void Return(CScriptThread *thread) { std::copy(savedTls, savedTls + 32, thread->tls); thread->ip = retnAddress; thread->scmFunction = prevScmFunctionId; } }; ScmFunction *ScmFunction::Store[store_size] = { /* default initializer - nullptr */ }; size_t ScmFunction::allocationPlace = 0; void ResetScmFunctionStore() { for (ScmFunction *scmFunc : ScmFunction::Store) if (scmFunc) delete scmFunc; ScmFunction::allocationPlace = 0; } /************************************************************************/ /* Opcode definitions */ /************************************************************************/ //0A8C=4,write_memory %1d% size %2d% value %3d% virtual_protect %4d% OpcodeResult STDCALL opcode_0A8C(CScriptThread *thread) { GetScriptParams(thread, 4); void *Address = opcodeParams[0].pParam; unsigned size = opcodeParams[1].dwParam; unsigned value = opcodeParams[2].dwParam; bool vp = opcodeParams[3].dwParam != 0; switch (size) { default: GetThisInstance().codeInjector.MemoryWrite(Address, (BYTE)value, vp, size); break; case 2: GetThisInstance().codeInjector.MemoryWrite(Address, (WORD)value, vp); break; case 4: GetThisInstance().codeInjector.MemoryWrite(Address, value, vp); break; } return OR_CONTINUE; } //0A8D=4,%4d% = read_memory %1d% size %2d% virtual_protect %3d% OpcodeResult STDCALL opcode_0A8D(CScriptThread *thread) { GetScriptParams(thread, 3); unsigned& value = opcodeParams[0].dwParam; void *Address = opcodeParams[0].pParam; unsigned size = opcodeParams[1].dwParam; bool vp = opcodeParams[2].dwParam; switch (size) { case 1: GetThisInstance().codeInjector.MemoryRead(Address, *(BYTE *)&value, vp); break; case 2: GetThisInstance().codeInjector.MemoryRead(Address, *(WORD *)&value, vp); break; case 4: GetThisInstance().codeInjector.MemoryRead(Address, value, vp); break; default: TRACE("[0A8D] Unallowed size %u", size); } SetScriptParams(thread, 1); return OR_CONTINUE; } //0A8E=3,%3d% = %1d% + %2d% ; int OpcodeResult STDCALL opcode_0A8E(CScriptThread *thread) { GetScriptParams(thread, 2); opcodeParams[0].nParam += opcodeParams[1].nParam; SetScriptParams(thread, 1); return OR_CONTINUE; } //0A8F=3,%3d% = %1d% - %2d% ; int OpcodeResult STDCALL opcode_0A8F(CScriptThread *thread) { GetScriptParams(thread, 2); opcodeParams[0].nParam -= opcodeParams[1].nParam; SetScriptParams(thread, 1); return OR_CONTINUE; } //0A90=3,%3d% = %1d% * %2d% ; int OpcodeResult STDCALL opcode_0A90(CScriptThread *thread) { GetScriptParams(thread, 2); opcodeParams[0].nParam *= opcodeParams[1].nParam; SetScriptParams(thread, 1); return OR_CONTINUE; } //0A91=3,%3d% = %1d% / %2d% ; int OpcodeResult STDCALL opcode_0A91(CScriptThread *thread) { GetScriptParams(thread, 2); opcodeParams[0].nParam /= opcodeParams[1].nParam; SetScriptParams(thread, 1); return OR_CONTINUE; } //0A92=-1,create_custom_thread %1d% OpcodeResult STDCALL opcode_0A92(CScriptThread *thread) { const char *script_name = readString(thread); TRACE("[0A92] Starting new custom script %s from thread named %s", script_name, thread->threadName); char cwd[MAX_PATH]; _getcwd(cwd, sizeof(cwd)); chdir(cleo_dir); auto cs = new CCustomScript(script_name); SetScriptCondResult(thread, cs->is_ok()); if (cs->is_ok()) { GetThisInstance().scriptEngine.AddCustomScript(cs); TransmitScriptParams(thread, cs); } else { delete cs; SkipUnusedParameters(thread); TRACE("[0A92] Failed."); } chdir(cwd); return OR_CONTINUE; } //0A93=0,end_custom_thread OpcodeResult STDCALL opcode_0A93(CScriptThread *thread) { CCustomScript *cs = reinterpret_cast(thread); if (thread->missionFlag || !thread->IsCustom) { TRACE("[0A93] Incorrect usage of opcode from thread %s", thread->threadName); return OR_CONTINUE; } GetThisInstance().scriptEngine.RemoveCustomScript(cs); delete cs; return OR_INTERRUPT; } //0A94=-1,create_custom_mission %1d% OpcodeResult STDCALL opcode_0A94(CScriptThread *thread) { char script_name[MAX_PATH]; readString(thread, script_name); strcat(script_name, ".cm"); // add custom mission extension TRACE("[0A94] Starting new custom mission %s from thread named %s", script_name, thread->threadName); char cwd[MAX_PATH]; _getcwd(cwd, sizeof(cwd)); chdir(cleo_dir); auto cs = new CCustomScript(script_name, true); SetScriptCondResult(thread, cs->is_ok()); if (cs->is_ok()) { GetThisInstance().scriptEngine.AddCustomScript(cs); TransmitScriptParams(thread, cs); } else { delete cs; SkipUnusedParameters(thread); TRACE("[0A94] Failed."); } chdir(cwd); return OR_CONTINUE; } //0A95=0,enable_thread_saving OpcodeResult STDCALL opcode_0A95(CScriptThread *thread) { reinterpret_cast(thread)->enable_saving(); return OR_CONTINUE; } //0A96=2,%2d% = actor %1d% struct OpcodeResult opcode_0A96(CScriptThread *thread) { unsigned handle; *thread >> handle; *thread << GetPedPool().atHandle(handle); return OR_CONTINUE; } //0A97=2,%2d% = car %1d% struct OpcodeResult opcode_0A97(CScriptThread *thread) { unsigned handle; *thread >> handle; *thread << GetVehiclePool().atHandle(handle); return OR_CONTINUE; } //0A98=2,%2d% = object %1d% struct OpcodeResult opcode_0A98(CScriptThread *thread) { unsigned handle; *thread >> handle; *thread << GetObjectPool().atHandle(handle); return OR_CONTINUE; } //0A99=1,chdir %1b:userdir/rootdir% OpcodeResult opcode_0A99(CScriptThread *thread) { auto paramType = *thread->ip; if (paramType >= 1 && paramType <= 8) { // integer param unsigned param; *thread >> param; chdir(param ? GetUserDirectory() : ""); } else { // string param char buf[MAX_PATH]; std::fill(buf, buf + sizeof(buf), '\0'); GetScriptStringParam(thread, buf, 100); chdir(buf); } return OR_CONTINUE; } //0A9A=3,%3d% = openfile %1d% mode %2d% // IF and SET OpcodeResult opcode_0A9A(CScriptThread *thread) { const char *fname = readString(thread); auto paramType = *thread->ip; char mode[0x10]; if (paramType >= 1 && paramType <= 8) { // integer param (for backward compatibility with CLEO 3) union { unsigned uParam; char strParam[4]; }param; *thread >> param.uParam; strcpy(mode, param.strParam); } else // string param GetScriptStringParam(thread, mode, sizeof(mode)); auto file = fopen(fname, mode); *thread << file; SetScriptCondResult(thread, file != nullptr); if (file) GetThisInstance().opcodeSystem.openedFiles.insert(file); return OR_CONTINUE; } //0A9B=1,closefile %1d% OpcodeResult opcode_0A9B(CScriptThread *thread) { FILE *file; *thread >> file; fclose(file); GetThisInstance().opcodeSystem.openedFiles.erase(file); return OR_CONTINUE; } //0A9C=2,%2d% = file %1d% size OpcodeResult opcode_0A9C(CScriptThread *thread) { FILE *file; *thread >> file; auto savedPos = ftell(file); fseek(file, 0, SEEK_END); *thread << static_cast(ftell(file)); fseek(file, savedPos, SEEK_SET); return OR_CONTINUE; } //0A9D=3,readfile %1d% size %2d% to %3d% OpcodeResult opcode_0A9D(CScriptThread *thread) { FILE *file; unsigned size; void *buf; RetrieveScriptParams(thread, file, size); buf = GetScriptParamPointer(thread); fread(buf, size, 1, file); return OR_CONTINUE; } //0A9E=3,writefile %1d% size %2d% from %3d% OpcodeResult opcode_0A9E(CScriptThread *thread) { FILE *file; unsigned size; const void *buf; RetrieveScriptParams(thread, file, size); buf = GetScriptParamPointer(thread); fwrite(buf, size, 1, file); fflush(file); return OR_CONTINUE; } //0A9F=1,%1d% = current_thread_pointer OpcodeResult opcode_0A9F(CScriptThread *thread) { *thread << thread; return OR_CONTINUE; } //0AA0=1,gosub_if_false %1p% OpcodeResult opcode_0AA0(CScriptThread *thread) { int off; *thread >> off; if (thread->condResult) return OR_CONTINUE; // save pointer to the next instruction on the stack thread->gosub_stack[thread->stack_index++] = thread->ip; // jump by pointer ThreadJump(thread, off); return OR_CONTINUE; } //0AA1=0,return_if_false OpcodeResult opcode_0AA1(CScriptThread *thread) { if (thread->condResult) return OR_CONTINUE; // jump back to the location saved on stack thread->ip = thread->gosub_stack[--thread->stack_index]; return OR_CONTINUE; } //0AA2=2,%2h% = load_library %1d% // IF and SET OpcodeResult opcode_0AA2(CScriptThread *thread) { auto libHandle = LoadLibrary(readString(thread)); *thread << libHandle; SetScriptCondResult(thread, libHandle != nullptr); if (libHandle) GetThisInstance().opcodeSystem.nativeLibs.insert(libHandle); return OR_CONTINUE; } //0AA3=1,free_library %1h% OpcodeResult opcode_0AA3(CScriptThread *thread) { HMODULE libHandle; *thread >> libHandle; FreeLibrary(libHandle); GetThisInstance().opcodeSystem.nativeLibs.erase(libHandle); return OR_CONTINUE; } //0AA4=3,%3d% = get_proc_address %1d% library %2d% // IF and SET OpcodeResult opcode_0AA4(CScriptThread *thread) { char *funcName = readString(thread); HMODULE libHandle; *thread >> libHandle; void *funcAddr = (void *)GetProcAddress(libHandle, funcName); *thread << funcAddr; SetScriptCondResult(thread, funcAddr != nullptr); return OR_CONTINUE; } //0AA5=-1,call %1d% num_params %2h% pop %3h% OpcodeResult opcode_0AA5(CScriptThread *thread) { static char textParams[5][100]; unsigned currTextParam = 0; void (*func)(); unsigned numParams; unsigned stackAlign; RetrieveScriptParams(thread, func, numParams, stackAlign); stackAlign *= 4; SCRIPT_VAR *arguments = new SCRIPT_VAR[numParams], *arguments_end = arguments + numParams; // retrieve parameters for (SCRIPT_VAR *arg = arguments; arg != arguments_end; ++arg) { switch (*thread->ip) { case imm32f: case imm32: case imm16: case imm8: case globalVar: case localVar: case globalArr: case localArr: *thread >> (*arg).dwParam; break; case globalVarVString: case localVarVString: case globalVarSString: case localVarSString: (*arg).pParam = GetScriptParamPointer(thread); break; case vstring: case sstring: (*arg).szParam = readString(thread, textParams[currTextParam++], 100); } } // call function asm volatile ( "loop_0AA5:\n" "cmp %2,%3\n" "je loop_end_0AA5\n" "pushl (%2)\n" "addl $4,%2\n" "jmp loop_0AA5\n" "loop_end_0AA5:\n" "call *%0\n" "addl %1,%%esp" : // no output parameters : "r"(func), "m"(stackAlign), "r"(arguments), "m"(arguments_end) : "%eax", "%edx", "%ecx"); if (arguments) delete[] arguments; SkipUnusedParameters(thread); return OR_CONTINUE; } //0AA6=-1,call_method %1d% struct %2d% num_params %3h% pop %4h% OpcodeResult opcode_0AA6(CScriptThread *thread) { static char textParams[5][100]; unsigned currTextParam = 0; void (*func)(); void *struc; unsigned numParams; unsigned stackAlign; RetrieveScriptParams(thread, func, struc, numParams, stackAlign); stackAlign *= 4; SCRIPT_VAR *arguments = new SCRIPT_VAR[numParams], *arguments_end = arguments + numParams; // retrieve parameters for (SCRIPT_VAR *arg = arguments; arg != arguments_end; ++arg) { switch (*thread->ip) { case imm32f: case imm32: case imm16: case imm8: case globalVar: case localVar: case globalArr: case localArr: *thread >> (*arg).dwParam; break; case globalVarVString: case localVarVString: case globalVarSString: case localVarSString: (*arg).pParam = GetScriptParamPointer(thread); break; case vstring: case sstring: (*arg).szParam = readString(thread, textParams[currTextParam++], 100); } } // call function asm volatile ("loop_0AA6:\n" "cmp %2,%3\n" "je loop_end_0AA6\n" "pushl (%2)\n" "addl $4,%2\n" "jmp loop_0AA6\n" "loop_end_0AA6:\n" "movl %4,%%ecx\n" "call *%0\n" "addl %1,%%esp" : // no output parameters : "r"(func), "m"(stackAlign), "r"(arguments), "m"(arguments_end), "m"(struc) : "%eax", "%edx", "%ecx"); if (arguments) delete[] arguments; SkipUnusedParameters(thread); return OR_CONTINUE; } //0AA7=-1,call_function %1d% num_params %2h% pop %3h% OpcodeResult opcode_0AA7(CScriptThread *thread) { static char textParams[5][100]; unsigned currTextParam = 0; void (*func)(); unsigned numParams; unsigned stackAlign; RetrieveScriptParams(thread, func, numParams, stackAlign); stackAlign *= 4; SCRIPT_VAR *arguments = new SCRIPT_VAR[numParams], *arguments_end = arguments + numParams; // retrieve parameters for (SCRIPT_VAR *arg = arguments; arg != arguments_end; ++arg) { switch (*thread->ip) { case imm32f: case imm32: case imm16: case imm8: case globalVar: case localVar: case globalArr: case localArr: *thread >> (*arg).dwParam; break; case globalVarVString: case localVarVString: case globalVarSString: case localVarSString: (*arg).pParam = GetScriptParamPointer(thread); break; case vstring: case sstring: (*arg).szParam = readString(thread, textParams[currTextParam++], 100); } } unsigned result; // call function asm volatile ( "loop_0AA7:\n" "cmp %3,%4\n" "je loop_end_0AA7\n" "pushl (%3)\n" "addl $4,%3\n" "jmp loop_0AA7\n" "loop_end_0AA7:\n" "call *%1\n" "addl %2,%%esp" : "=a"(result) : "r"(func), "m"(stackAlign), "r"(arguments), "m"(arguments_end) : "%edx", "%ecx"); if (arguments) delete[] arguments; *thread << result; SkipUnusedParameters(thread); return OR_CONTINUE; } //0AA8=-1,call_function_method %1d% struct %2d% num_params %3h% pop %4h% OpcodeResult opcode_0AA8(CScriptThread *thread) { static char textParams[5][100]; unsigned currTextParam = 0; void (*func)(); void *struc; unsigned numParams; unsigned stackAlign; RetrieveScriptParams(thread, func, struc, numParams, stackAlign); stackAlign *= 4; SCRIPT_VAR *arguments = new SCRIPT_VAR[numParams], *arguments_end = arguments + numParams; // retrieve parameters for (SCRIPT_VAR *arg = arguments; arg != arguments_end; ++arg) { switch (*thread->ip) { case imm32f: case imm32: case imm16: case imm8: case globalVar: case localVar: case globalArr: case localArr: *thread >> (*arg).dwParam; break; case globalVarVString: case localVarVString: case globalVarSString: case localVarSString: (*arg).pParam = GetScriptParamPointer(thread); break; case vstring: case sstring: (*arg).szParam = readString(thread, textParams[currTextParam++], 100); } } unsigned result; // call function asm volatile ("loop_0AA8:\n" "cmp %3,%4\n" "je loop_end_0AA8\n" "pushl (%3)\n" "addl $4,%3\n" "jmp loop_0AA8\n" "loop_end_0AA8:\n" "movl %5,%%ecx\n" "call *%1\n" "addl %2,%%esp" : "=a"(result) : "r"(func), "m"(stackAlign), "r"(arguments), "m"(arguments_end), "m"(struc) : "%ecx", "%edx"); if (arguments) delete[] arguments; *thread << result; SkipUnusedParameters(thread); return OR_CONTINUE; } //0AA9=0, is_game_version_original OpcodeResult opcode_0AA9(CScriptThread *thread) { SetScriptCondResult(thread, GetThisInstance().versionManager.GetGameVersion() == GV_US10); return OR_CONTINUE; } //0AAA=2,%2d% = thread %1d% pointer OpcodeResult opcode_0AAA(CScriptThread *thread) { char *threadName = readString(thread); threadName[7] = '\0'; CScriptThread *cs = GetThisInstance().scriptEngine.FindThreadByName(threadName); *thread << cs; SetScriptCondResult(thread, cs != nullptr); return OR_CONTINUE; } //0AAB=1, file_exists %1d% OpcodeResult opcode_0AAB(CScriptThread *thread) { DWORD fAttr = GetFileAttributes(readString(thread)); SetScriptCondResult(thread, (fAttr != INVALID_FILE_ATTRIBUTES) && !(fAttr & FILE_ATTRIBUTE_DIRECTORY)); return OR_CONTINUE; } //0AAC=2,%2d% = load_audiostream %1d% //IF and SET OpcodeResult opcode_0AAC(CScriptThread *thread) { auto stream = GetThisInstance().soundSystem.LoadStream(readString(thread)); *thread << stream; SetScriptCondResult(thread,stream != nullptr); return OR_CONTINUE; } //0AAD=2,set_audiostream %1d% perform_action %2d% OpcodeResult opcode_0AAD(CScriptThread *thread) { CAudioStream *stream; int action; *thread >> stream >> action; switch (action) { case 0: stream->Stop(); break; case 1: stream->Play(); break; case 2: stream->Pause(); break; case 3: stream->Resume(); break; default: TRACE("[0AAD] Unknown audiostream's action: %d", action); } return OR_CONTINUE; } //0AAE=1,release_audiostream %1d% OpcodeResult opcode_0AAE(CScriptThread *thread) { CAudioStream *stream; *thread >> stream; GetThisInstance().soundSystem.UnloadStream(stream); return OR_CONTINUE; } //0AAF=2,%2d% = get_audiostream_length %1d% OpcodeResult opcode_0AAF(CScriptThread *thread) { CAudioStream *stream; *thread >> stream; *thread << stream->GetLength(); return OR_CONTINUE; } //0AB0=1, key_pressed %1d% OpcodeResult opcode_0AB0(CScriptThread *thread) { unsigned key; *thread >> key; SetScriptCondResult(thread, HIBYTE(GetKeyState(key)) == 0xFF); return OR_CONTINUE; } //0AB1=-1,call_scm_func %1p% OpcodeResult opcode_0AB1(CScriptThread *thread) { unsigned label, nParams; *thread >> label >> nParams; if (nParams) GetScriptParams(thread, nParams); new ScmFunction(thread); // pass arguments std::copy(opcodeParams, opcodeParams + nParams, thread->tls); // jump to label ThreadJump(thread, label); return OR_CONTINUE; } //0AB2=-1,ret OpcodeResult opcode_0AB2(CScriptThread *thread) { ScmFunction *scmFunc = ScmFunction::Store[thread->scmFunction]; unsigned nRetParams; *thread >> nRetParams; if (nRetParams) GetScriptParams(thread, nRetParams); scmFunc->Return(thread); if (nRetParams) SetScriptParams(thread, nRetParams); SkipUnusedParameters(thread); delete scmFunc; return OR_CONTINUE; } //0AB3=2,var %1d% = %2d% OpcodeResult opcode_0AB3(CScriptThread *thread) { unsigned varId, value; RetrieveScriptParams(thread, varId, value); GetThisInstance().scriptEngine.CleoVariables[varId].dwParam = value; return OR_CONTINUE; } //0AB4=2,%2d% = var %1d% OpcodeResult opcode_0AB4(CScriptThread *thread) { unsigned varId; *thread >> varId; *thread << GetThisInstance().scriptEngine.CleoVariables[varId].dwParam; return OR_CONTINUE; } //0AB5=3,store_actor %1d% closest_vehicle_to %2d% closest_ped_to %3d% OpcodeResult opcode_0AB5(CScriptThread *thread) { unsigned actor; *thread >> actor; auto ped = GetPedPool().atHandle(actor); auto closVeh = ped->intelligence->vehicleScanner.entities[0]; auto closPed = ped->intelligence->pedScanner.entities[0]; *thread << (closVeh ? GetVehiclePool().handleOf(closVeh) : -1) << (closPed ? GetPedPool().handleOf(closPed) : -1); return OR_CONTINUE; } //0AB6=3,store_target_marker_coords_to %1d% %2d% %3d% // IF and SET OpcodeResult opcode_0AB6(CScriptThread *thread) { unsigned hMarker = menuManager->TargetMarkerHandle; CMarker *marker; bool condResult = hMarker && (marker = &radarBlips[LOWORD(hMarker)]) && marker->_Index == HIWORD(hMarker) && marker->Flag; if (condResult) { RwV3d coords(marker->pos); coords.z = FindGroundZ(coords.x, coords.y); *thread << coords; } else GetScriptParams(thread, 3); SetScriptCondResult(thread, condResult); return OR_CONTINUE; } //0AB7=2,get_vehicle %1d% number_of_gears_to %2d% OpcodeResult opcode_0AB7(CScriptThread *thread) { unsigned hVehicle; *thread >> hVehicle; auto veh = GetVehiclePool().atHandle(hVehicle); *thread << handling->autoHandling[veh->modelIndex - 400].transmissionData.nNumberOfGears; return OR_CONTINUE; } //0AB8=2,get_vehicle %1d% current_gear_to %2d% OpcodeResult opcode_0AB8(CScriptThread *thread) { unsigned hVehicle; *thread >> hVehicle; *thread << GetVehiclePool().atHandle(hVehicle)->currentGear; return OR_CONTINUE; } //0AB9=2,get_audiostream %1d% state_to %2d% OpcodeResult opcode_0AB9(CScriptThread *thread) { CAudioStream *stream; *thread >> stream; *thread << stream->GetState(); return OR_CONTINUE; } //0ABA=1,end_custom_thread_named %1d% OpcodeResult opcode_0ABA(CScriptThread *thread) { char *threadName = readString(thread); auto deleted_thread = GetThisInstance().scriptEngine.FindThreadByName(threadName); if (deleted_thread) { GetThisInstance().scriptEngine.RemoveCustomScript(deleted_thread); delete deleted_thread; } return deleted_thread == thread ? OR_INTERRUPT : OR_CONTINUE; } //0ABB=2,%2d% = audiostream %1d% volume OpcodeResult opcode_0ABB(CScriptThread *thread) { CAudioStream *stream; *thread >> stream; *thread << stream->GetVolume(); return OR_CONTINUE; } //0ABC=2,set_audiostream %1d% volume %2d% OpcodeResult opcode_0ABC(CScriptThread *thread) { CAudioStream *stream; float volume; RetrieveScriptParams(thread, stream, volume); stream->SetVolume(volume); return OR_CONTINUE; } //0ABD=1, vehicle %1d% siren_on OpcodeResult opcode_0ABD(CScriptThread *thread) { unsigned hVehicle; *thread >> hVehicle; // test bit 7 SetScriptCondResult(thread, ((GetVehiclePool().atHandle(hVehicle)->flags >> 7) & 1) == 1); return OR_CONTINUE; } //0ABE=1, vehicle %1d% engine_on OpcodeResult opcode_0ABE(CScriptThread *thread) { unsigned hVehicle; *thread >> hVehicle; // test bit 4 SetScriptCondResult(thread, ((GetVehiclePool().atHandle(hVehicle)->flags >> 4) & 1) == 1); return OR_CONTINUE; } //0ABF=2,set_vehicle %1d% engine_state_to %2d% OpcodeResult opcode_0ABF(CScriptThread *thread) { unsigned hVehicle, state; RetrieveScriptParams(thread, hVehicle, state); auto veh = GetVehiclePool().atHandle(hVehicle); // clear/set bit 4 if (state) veh->flags |= 0x10; else veh->flags &= ~0x10; return OR_CONTINUE; } //0AC0=2,loop_audiostream %1d% flag %2d% OpcodeResult opcode_0AC0(CScriptThread *thread) { CAudioStream *stream; unsigned loop; RetrieveScriptParams(thread, stream, loop); stream->Loop(loop); return OR_CONTINUE; } //0AC1=2,%2d% = load_audiostream_with_3d_support %1d% //IF and SET OpcodeResult opcode_0AC1(CScriptThread *thread) { auto stream = GetThisInstance().soundSystem.LoadStream(readString(thread), true); *thread << stream; SetScriptCondResult(thread, stream != nullptr); return OR_CONTINUE; } //0AC2=4,set_3d_audiostream %1d% position %2d% %3d% %4d% OpcodeResult opcode_0AC2(CScriptThread *thread) { CAudioStream *stream; RwV3d pos; *thread >> stream >> pos; stream->Set3dPosition(pos); return OR_CONTINUE; } //0AC3=2,link_3d_audiostream %1d% to_object %2d% OpcodeResult opcode_0AC3(CScriptThread *thread) { CAudioStream *stream; unsigned handle; RetrieveScriptParams(thread, stream, handle); stream->Link(GetObjectPool().atHandle(handle)); return OR_CONTINUE; } //0AC4=2,link_3d_audiostream %1d% to_actor %2d% OpcodeResult opcode_0AC4(CScriptThread *thread) { CAudioStream *stream; unsigned handle; RetrieveScriptParams(thread, stream, handle); stream->Link(GetPedPool().atHandle(handle)); return OR_CONTINUE; } //0AC5=2,link_3d_audiostream %1d% to_vehicle %2d% OpcodeResult opcode_0AC5(CScriptThread *thread) { CAudioStream *stream; unsigned handle; RetrieveScriptParams(thread, stream, handle); stream->Link(GetVehiclePool().atHandle(handle)); return OR_CONTINUE; } //0AC6=2,%2d% = label %1p% offset OpcodeResult opcode_0AC6(CScriptThread *thread) { int label; *thread >> label; *thread << (label < 0 ? thread->baseIp - label : scmBlock + label); return OR_CONTINUE; } //0AC7=2,%2d% = var %1d% offset OpcodeResult opcode_0AC7(CScriptThread *thread) { *thread << GetScriptParamPointer(thread); return OR_CONTINUE; }; //0AC8=2,%2d% = allocate_memory_size %1d% OpcodeResult opcode_0AC8(CScriptThread *thread) { unsigned size; *thread >> size; void *mem = malloc(size); if (mem) { GetThisInstance().opcodeSystem.allocatedMemory.insert(mem); *thread << mem; SetScriptCondResult(thread, true); } else SetScriptCondResult(thread, false); return OR_CONTINUE; }; //0AC9=1,free_allocated_memory %1d% OpcodeResult opcode_0AC9(CScriptThread *thread) { void *mem; *thread >> mem; free(mem); GetThisInstance().opcodeSystem.allocatedMemory.erase(mem); return OR_CONTINUE; }; //0ACA=1,show_text_box %1d% OpcodeResult opcode_0ACA(CScriptThread *thread) { ShowTextBox(readString(thread)); return OR_CONTINUE; }; //0ACB=3,show_styled_text %1d% time %2d% style %3d% OpcodeResult opcode_0ACB(CScriptThread *thread) { const char *text = readString(thread); unsigned time, style; RetrieveScriptParams(thread, time, style); ShowStyledText(text, time, style); return OR_CONTINUE; }; //0ACC=2,show_text_lowpriority %1d% time %2d% OpcodeResult opcode_0ACC(CScriptThread *thread) { const char *text = readString(thread); unsigned time; *thread >> time; ShowTextLowPriority(text, time); return OR_CONTINUE; }; //0ACD=2,show_text_highpriority %1d% time %2d% OpcodeResult opcode_0ACD(CScriptThread *thread) { const char *text = readString(thread); unsigned time; *thread >> time; ShowTextHighPriority(text, time); return OR_CONTINUE; }; //0ACE=-1,show_formatted_text_box %1d% OpcodeResult opcode_0ACE(CScriptThread *thread) { char fmt[100]; char text[100]; readString(thread, fmt, sizeof(fmt)); format(thread, text, sizeof(text), fmt); ShowTextBox(text); SkipUnusedParameters(thread); return OR_CONTINUE; }; //0ACF=-1,show_formatted_styled_text %1d% time %2d% style %3d% OpcodeResult opcode_0ACF(CScriptThread *thread) { char fmt[100]; char text[100]; unsigned time, style; readString(thread, fmt, sizeof(fmt)); RetrieveScriptParams(thread, time, style); format(thread, text, sizeof(text), fmt); ShowStyledText(text, time, style); SkipUnusedParameters(thread); return OR_CONTINUE; }; //0AD0=-1,show_formatted_text_lowpriority %1d% time %2d% OpcodeResult opcode_0AD0(CScriptThread *thread) { char fmt[100]; char text[100]; unsigned time; readString(thread, fmt, sizeof(fmt)); *thread >> time; format(thread, text, sizeof(text), fmt); ShowTextLowPriority(text, time); SkipUnusedParameters(thread); return OR_CONTINUE; }; //0AD1=-1,show_formatted_text_highpriority %1d% time %2d% OpcodeResult opcode_0AD1(CScriptThread *thread) { char fmt[100]; char text[100]; unsigned time; readString(thread, fmt, sizeof(fmt)); *thread >> time; format(thread, text, sizeof(text), fmt); ShowTextHighPriority(text, time); SkipUnusedParameters(thread); return OR_CONTINUE; }; //0AD2=2,%2d% = player %1d% targeted_actor //IF and SET OpcodeResult opcode_0AD2(CScriptThread *thread) { unsigned playerId; *thread >> playerId; CPed *playerPed = GetPlayerPed(playerId); CPed *targetedPed = nullptr; if (playerPed && ((targetedPed = (CPed *)playerPed->targetedEntity) || (targetedPed = (CPed *)playerPed->targetedObject)) && (targetedPed->type & 7) == 3) // type is CPed { *thread << GetPedPool().handleOf(targetedPed); SetScriptCondResult(thread, true); } else { *thread << 0u; SetScriptCondResult(thread, false); } return OR_CONTINUE; }; //0AD3=-1,string %1d% format %2d% OpcodeResult opcode_0AD3(CScriptThread *thread) { char fmt[100], *dst; if (*thread->ip >= 1 && *thread->ip <= 8) *thread >> dst; else dst = (char*)GetScriptParamPointer(thread); readString(thread, fmt, sizeof(fmt)); format(thread, dst, -1ul, fmt); SkipUnusedParameters(thread); return OR_CONTINUE; }; //0AD4=-1,%3d% = scan_string %1d% format %2d% //IF and SET OpcodeResult opcode_0AD4(CScriptThread *thread) { char fmt[100], *src; src = readString(thread); readString(thread, fmt, sizeof(fmt)); size_t cExParams = 0; int *result = (int *)GetScriptParamPointer(thread); SCRIPT_VAR *ExParams[35]; // read extra params while (*thread->ip) ExParams[cExParams++] = GetScriptParamPointer(thread); ++thread->ip; *result = sscanf(src, fmt, /* extra parameters (will be aligned automatically, but the limit of 35 elements maximum exists) */ ExParams[0], ExParams[1], ExParams[2], ExParams[3], ExParams[4], ExParams[5], ExParams[6], ExParams[7], ExParams[8], ExParams[9], ExParams[10], ExParams[11], ExParams[12], ExParams[13], ExParams[14], ExParams[15], ExParams[16], ExParams[17], ExParams[18], ExParams[19], ExParams[20], ExParams[21], ExParams[22], ExParams[23], ExParams[24], ExParams[25], ExParams[26], ExParams[27], ExParams[28], ExParams[29], ExParams[30], ExParams[31], ExParams[32], ExParams[33], ExParams[34]); return OR_CONTINUE; }; //0AD5=3,file %1d% seek %2d% from_origin %3d% //IF and SET OpcodeResult opcode_0AD5(CScriptThread *thread) { FILE *file; int seek, origin; RetrieveScriptParams(thread, file, seek, origin); SetScriptCondResult(thread, fseek(file, seek, origin) == 0); return OR_CONTINUE; }; //0AD6=1,end_of_file %1d% reached OpcodeResult opcode_0AD6(CScriptThread *thread) { FILE *file; *thread >> file; SetScriptCondResult(thread, feof(file) != 0); return OR_CONTINUE; }; //0AD7=3,read_string_from_file %1d% to %2d% size %3d% //IF and SET OpcodeResult opcode_0AD7(CScriptThread *thread) { FILE *file; char *buf; unsigned size; *thread >> file; if (*thread->ip >= 1 && *thread->ip <= 8) *thread >> buf; else buf = (char *)GetScriptParamPointer(thread); *thread >> size; SetScriptCondResult(thread, fgets(buf, size, file) == buf); return OR_CONTINUE; }; //0AD8=2,write_string_to_file %1d% from %2d% //IF and SET OpcodeResult opcode_0AD8(CScriptThread *thread) { FILE *file; *thread >> file; SetScriptCondResult(thread, fputs(readString(thread), file) > 0); fflush(file); return OR_CONTINUE; }; //0AD9=-1,write_formated_text %2d% to_file %1d% OpcodeResult opcode_0AD9(CScriptThread *thread) { char fmt[100]; char text[100]; FILE *file; *thread >> file; readString(thread, fmt, sizeof(fmt)); format(thread, text, sizeof(text), fmt); fputs(text, file); fflush(file); SkipUnusedParameters(thread); return OR_CONTINUE; }; //0ADA=-1,%3d% = scan_file %1d% format %2d% //IF and SET OpcodeResult opcode_0ADA(CScriptThread *thread) { FILE *file; *thread >> file; char *fmt = readString(thread); int *result = (int *)GetScriptParamPointer(thread); size_t cExParams = 0; SCRIPT_VAR *ExParams[35]; // read extra params while (*thread->ip) ExParams[cExParams++] = GetScriptParamPointer(thread); ++thread->ip; *result = fscanf(file, fmt, /* extra parameters (will be aligned automatically, but the limit of 35 elements maximum exists) */ ExParams[0], ExParams[1], ExParams[2], ExParams[3], ExParams[4], ExParams[5], ExParams[6], ExParams[7], ExParams[8], ExParams[9], ExParams[10], ExParams[11], ExParams[12], ExParams[13], ExParams[14], ExParams[15], ExParams[16], ExParams[17], ExParams[18], ExParams[19], ExParams[20], ExParams[21], ExParams[22], ExParams[23], ExParams[24], ExParams[25], ExParams[26], ExParams[27], ExParams[28], ExParams[29], ExParams[30], ExParams[31], ExParams[32], ExParams[33], ExParams[34]); return OR_CONTINUE; } //0ADB=2,%2d% = car_model %1o% name OpcodeResult opcode_0ADB(CScriptThread *thread) { unsigned mi; char *buf; *thread >> mi; auto model = (CVehicleModel *)models[mi]; if (*thread->ip >= 1 && *thread->ip <= 8) *thread >> buf; else buf = (char *)GetScriptParamPointer(thread); memcpy(buf, model->gamename, 8); return OR_CONTINUE; } //0ADC=1, test_cheat %1d% OpcodeResult opcode_0ADC(CScriptThread *thread) { SetScriptCondResult(thread, TestCheat(readString(thread))); return OR_CONTINUE; } //0ADD=1,spawn_car_with_model %1o% at_player_location OpcodeResult opcode_0ADD(CScriptThread *thread) { unsigned mi; *thread >> mi; auto model = (CVehicleModel *)models[mi]; if (model->type != VT_TRAIN && model->type != VT_UNK) SpawnCar(mi); return OR_CONTINUE; } //0ADE=2,%2d% = text_by_GXT_entry %1d% OpcodeResult opcode_0ADE(CScriptThread *thread) { const char *gxt = readString(thread); if (*thread->ip >= 1 && *thread->ip <= 8) *thread << CText__locate(gameTexts, gxt); else strcpy((char *)GetScriptParamPointer(thread), CText__locate(gameTexts, gxt)); return OR_CONTINUE; } //0ADF=2,add_dynamic_GXT_entry %1d% text %2d% OpcodeResult opcode_0ADF(CScriptThread *thread) { char *entryName; char text[100]; entryName = readString(thread); readString(thread, text, sizeof(text)); GetThisInstance().textManager.AddFxt(entryName, text); return OR_CONTINUE; } //0AE0=1,remove_dynamic_GXT_entry %1d% OpcodeResult opcode_0AE0(CScriptThread *thread) { GetThisInstance().textManager.RemoveFxt(readString(thread)); return OR_CONTINUE; } //0AE1=7,%7d% = find_actor_near_point %1d% %2d% %3d% in_radius %4d% find_next %5h% pass_deads %6h% //IF and SET OpcodeResult opcode_0AE1(CScriptThread *thread) { RwV3d center; float radius; unsigned next, pass_deads; static unsigned stat_last_found = 0; CPedPool& pool = GetPedPool(); *thread >> center >> radius >> next >> pass_deads; auto IsPedDead = [](CPed& ped) -> bool { unsigned f_530 = ped._f530; return f_530 >= 54 && f_530 <= 56; }; auto IsPedPlayer = [](CPed& ped) -> bool { return ped.pedType != 0 && ped.pedType != 1; }; unsigned& last_found = thread->IsCustom ? reinterpret_cast(thread)-> get_last_found_actor() : stat_last_found; if (!next) last_found = 0; for (unsigned index = last_found; index < pool.size; ++index) { if (pool.flags[index] & 0x80) continue; // empty slot CPed& obj = pool[index]; if (IsPedPlayer(obj)) continue; // skip player ped if (pass_deads && IsPedDead(obj)) continue; RwV3d& pos = obj.xyz ? obj.xyz->matrix.pos : obj.placement.pos; float dx = pos.x - center.x; float dy = pos.y - center.y; float dz = pos.z - center.z; float distance = sqrt(dx * dx + dy * dy + dz * dz); if (distance <= radius) { last_found = index + 1; // on next opcode call start search from next index // obj.referenceType = 2; // add reference to found actor *thread << pool.handleOf(&obj); SetScriptCondResult(thread, true); return OR_CONTINUE; } } *thread << 0u; SetScriptCondResult(thread, false); return OR_CONTINUE; } //0AE2=7,%7d% = find_vehicle_near_point %1d% %2d% %3d% in_radius %4d% find_next %5h% pass_wrecked %6h% //IF and SET OpcodeResult opcode_0AE2(CScriptThread *thread) { RwV3d center; float radius; unsigned next, pass_wrecked; static unsigned stat_last_found = 0; CVehiclePool& pool = GetVehiclePool(); *thread >> center >> radius >> next >> pass_wrecked; auto IsVehicleWrecked = [](CVehicle& veh) -> bool { if ((veh.type & 0xF8) == 0x28) return 1; return (veh._f42B & 0x40) != 0; }; unsigned& last_found = thread->IsCustom ? reinterpret_cast(thread)-> get_last_found_vehicle() : stat_last_found; if (!next) last_found = 0; for (unsigned index = last_found; index < pool.size; ++index) { if (pool.flags[index] & 0x80) continue; // empty slot CVehicle& obj = pool[index]; if (pass_wrecked && IsVehicleWrecked(obj)) continue; RwV3d& pos = obj.xyz ? obj.xyz->matrix.pos : obj.placement.pos; float dx = pos.x - center.x; float dy = pos.y - center.y; float dz = pos.z - center.z; float distance = sqrt(dx * dx + dy * dy + dz * dz); if (distance <= radius) { last_found = index + 1; // on next opcode call start search from next index // obj.referenceType = 2; // add reference to found actor *thread << pool.handleOf(&obj); SetScriptCondResult(thread, true); return OR_CONTINUE; } } *thread << 0u; SetScriptCondResult(thread, false); return OR_CONTINUE; } //0AE3=6,%6d% = find_object_near_point %1d% %2d% %3d% in_radius %4d% find_next %5h% //IF and SET OpcodeResult opcode_0AE3(CScriptThread *thread) { RwV3d center; float radius; unsigned next; static unsigned stat_last_found = 0; CObjectPool& pool = GetObjectPool(); *thread >> center >> radius >> next; unsigned& last_found = thread->IsCustom ? reinterpret_cast(thread)-> get_last_found_object() : stat_last_found; if (!next) last_found = 0; for (unsigned index = last_found; index < pool.size; ++index) { if (pool.flags[index] & 0x80) continue; // empty slot CObject& obj = pool[index]; RwV3d& pos = obj.xyz ? obj.xyz->matrix.pos : obj.placement.pos; float dx = pos.x - center.x; float dy = pos.y - center.y; float dz = pos.z - center.z; float distance = sqrt(dx * dx + dy * dy + dz * dz); if (distance <= radius) { last_found = index + 1; // on next opcode call start search from next index // obj.referenceType = 2; // add reference to found actor *thread << pool.handleOf(&obj); SetScriptCondResult(thread, true); return OR_CONTINUE; } } *thread << 0u; SetScriptCondResult(thread, false); return OR_CONTINUE; } //0AE4=1, directory_exist %1d% OpcodeResult opcode_0AE4(CScriptThread *thread) { auto fAttr = GetFileAttributes(readString(thread)); SetScriptCondResult(thread, (fAttr != INVALID_FILE_ATTRIBUTES) && (fAttr & FILE_ATTRIBUTE_DIRECTORY)); return OR_CONTINUE; } //0AE5=1,create_directory %1d% //IF and SET OpcodeResult opcode_0AE5(CScriptThread *thread) { bool condResult = CreateDirectory(readString(thread), NULL) != 0; SetScriptCondResult(thread, condResult); return OR_CONTINUE; } //0AE6=3,%2d% = find_first_file %1d% get_filename_to %3d% //IF and SET OpcodeResult opcode_0AE6(CScriptThread *thread) { WIN32_FIND_DATA ffd; memset(&ffd, 0, sizeof(ffd)); HANDLE handle = FindFirstFile(readString(thread), &ffd); *thread << handle; GetThisInstance().opcodeSystem.fileSearches.insert(handle); if (handle != INVALID_HANDLE_VALUE) { auto type = *thread->ip; char* str; switch (type) { case globalVarVString: case localVarVString: str = (char*)GetScriptParamPointer(thread); memcpy(str, ffd.cFileName, 16); str[15] = '\0'; break; case globalVarSString: case localVarSString: str = (char*)GetScriptParamPointer(thread); memcpy(str, ffd.cFileName, 8); str[7] = '\0'; break; default: *thread >> str; strcpy(str, ffd.cFileName); } SetScriptCondResult(thread, true); } else { readString(thread); SetScriptCondResult(thread, false); } return OR_CONTINUE; } //0AE7=2,%2d% = find_next_file %1d% //IF and SET OpcodeResult opcode_0AE7(CScriptThread *thread) { WIN32_FIND_DATA ffd; memset(&ffd, 0, sizeof(ffd)); HANDLE handle; *thread >> handle; if (FindNextFile(handle, &ffd)) { auto type = *thread->ip; char* str; switch (type) { case globalVarVString: case localVarVString: str = (char*)GetScriptParamPointer(thread); memcpy(str, ffd.cFileName, 16); str[15] = '\0'; break; case globalVarSString: case localVarSString: str = (char*)GetScriptParamPointer(thread); memcpy(str, ffd.cFileName, 8); str[7] = '\0'; break; default: *thread >> str; strcpy(str, ffd.cFileName); } SetScriptCondResult(thread, true); } else { readString(thread); SetScriptCondResult(thread, false); } return OR_CONTINUE; } //0AE8=1,find_close %1d% OpcodeResult opcode_0AE8(CScriptThread *thread) { HANDLE handle; *thread >> handle; FindClose(handle); GetThisInstance().opcodeSystem.fileSearches.erase(handle); return OR_CONTINUE; } //0AE9=0,pop_float OpcodeResult opcode_0AE9(CScriptThread *thread) { asm volatile ("fstp (%0)" : : "r"(&opcodeParams[0].fParam)); SetScriptParams(thread, 1); return OR_CONTINUE; } //0AEA=2,%2d% = actor_struct %1d% handle OpcodeResult opcode_0AEA(CScriptThread *thread) { CPed *struc; *thread >> struc; *thread << GetPedPool().handleOf(struc); return OR_CONTINUE; } //0AEB=2,%2d% = car_struct %1d% handle OpcodeResult opcode_0AEB(CScriptThread *thread) { CVehicle *struc; *thread >> struc; *thread << GetVehiclePool().handleOf(struc); return OR_CONTINUE; } //0AEC=2,%2d% = object_struct %1d% handle OpcodeResult opcode_0AEC(CScriptThread *thread) { CObject *struc; *thread >> struc; *thread << GetObjectPool().handleOf(struc); return OR_CONTINUE; } //0AED=3,%3d% = float %1d% to_string_format %2d% OpcodeResult opcode_0AED(CScriptThread *thread) { // this opcode is useless now float val; char *format, *result; *thread >> val; format = readString(thread); if (*thread->ip >= 1 && *thread->ip <= 8) *thread >> result; else result = (char *)GetScriptParamPointer(thread); sprintf(result, format, val); return OR_CONTINUE; } //0AEE=3,%3d% = %1d% exp %2d% //all floats OpcodeResult opcode_0AEE(CScriptThread *thread) { float base, arg; RetrieveScriptParams(thread, base, arg); *thread << (float)pow(base, arg); return OR_CONTINUE; } //0AEF=3,%3d% = log %1d% base %2d% //all floats OpcodeResult opcode_0AEF(CScriptThread *thread) { float base, arg; RetrieveScriptParams(thread, arg, base); *thread << (float)(log(arg) / log(base)); return OR_CONTINUE; } } /********************************************************************/ // API extern "C" { using namespace CLEO; // Define external symbols with MSVC decorating schemes BOOL STDCALL CLEO_RegisterOpcode(WORD opcode, CustomOpcodeHandler callback) asm("__CLEO_RegisterOpcode@8"); DWORD STDCALL CLEO_GetIntOpcodeParam(CScriptThread* thread) asm("__CLEO_GetIntOpcodeParam@4"); float STDCALL WINAPI CLEO_GetFloatOpcodeParam(CScriptThread* thread) asm("__CLEO_GetFloatOpcodeParam@4"); void STDCALL CLEO_SetIntOpcodeParam(CScriptThread* thread, DWORD value) asm("__CLEO_SetIntOpcodeParam@8"); void STDCALL CLEO_SetFloatOpcodeParam(CScriptThread* thread, float value) asm("__CLEO_SetFloatOpcodeParam@8"); LPSTR STDCALL CLEO_ReadStringOpcodeParam(CScriptThread* thread, char *buf, int size) asm("__CLEO_ReadStringOpcodeParam@12"); void STDCALL CLEO_WriteStringOpcodeParam(CScriptThread* thread, LPCSTR str) asm("__CLEO_WriteStringOpcodeParam@8"); void STDCALL CLEO_SetThreadCondResult(CScriptThread* thread, BOOL result) asm("__CLEO_SetThreadCondResult@8"); void STDCALL CLEO_SkipOpcodeParams(CScriptThread* thread, int count) asm("__CLEO_SkipOpcodeParams@8"); void STDCALL CLEO_ThreadJumpAtLabelPtr(CScriptThread* thread, int labelPtr) asm("__CLEO_ThreadJumpAtLabelPtr@8"); int STDCALL CLEO_GetOperandType(CScriptThread* thread) asm("__CLEO_GetOperandType@4"); void STDCALL CLEO_RetrieveOpcodeParams(CScriptThread *thread, int count) asm("__CLEO_RetrieveOpcodeParams@8"); void STDCALL CLEO_RecordOpcodeParams(CScriptThread *thread, int count) asm("__CLEO_RecordOpcodeParams@8"); BOOL STDCALL CLEO_RegisterOpcode(WORD opcode, CustomOpcodeHandler callback) { if ((opcode > 0x7FFF) || (opcode < 0x0AF0)) return FALSE; CustomOpcodeHandler& dst = extraOpcodeHandlers[opcode % 100][opcode / 100 - 28]; if (*dst) { Error("Warning! CLEO couldn't register opcode handler."); return FALSE; } dst = callback; return TRUE; } DWORD STDCALL CLEO_GetIntOpcodeParam(CScriptThread* thread) { unsigned result; *thread >> result; return result; } float STDCALL CLEO_GetFloatOpcodeParam(CScriptThread* thread) { float result; *thread >> result; return result; } void STDCALL CLEO_SetIntOpcodeParam(CScriptThread* thread, DWORD value) { *thread << (unsigned)value; } void STDCALL CLEO_SetFloatOpcodeParam(CScriptThread* thread, float value) { *thread << value; } LPSTR STDCALL CLEO_ReadStringOpcodeParam(CScriptThread* thread, char *buf, int size) { static char internal_buf[100]; if (!buf) { buf = internal_buf; size = 100; } if (!size) size = 100; std::fill(buf, buf + size, '\0'); GetScriptStringParam(thread, buf, size); return buf; } void STDCALL CLEO_WriteStringOpcodeParam(CScriptThread* thread, LPCSTR str) { auto dst = (char *)GetScriptParamPointer(thread); memcpy(dst, str, 16); dst[15] = '\0'; } void STDCALL CLEO_SetThreadCondResult(CScriptThread* thread, BOOL result) { SetScriptCondResult(thread, result != FALSE); } void STDCALL CLEO_SkipOpcodeParams(CScriptThread* thread, int count) { int strlen; for (int i = 0; i < count; i++) { switch (*(thread->ip)++) { case globalVar: case localVar: case globalVarVString: case localVarVString: case globalVarSString: case localVarSString: thread->ip += 2; break; case globalArr: case localArr: thread->ip += 6; break; case imm8: thread->ip += 1; break; case imm16: thread->ip += 2; break; case imm32: case imm32f: thread->ip += 4; break; case vstring: strlen = *(thread->ip)++; thread->ip += strlen; case sstring: thread->ip += 8; } } } void STDCALL CLEO_ThreadJumpAtLabelPtr(CScriptThread* thread, int labelPtr) { ThreadJump(thread, labelPtr); } int STDCALL CLEO_GetOperandType(CScriptThread* thread) { return *thread->ip; } void STDCALL CLEO_RetrieveOpcodeParams(CScriptThread *thread, int count) { GetScriptParams(thread, count); } void STDCALL CLEO_RecordOpcodeParams(CScriptThread *thread, int count) { SetScriptParams(thread, count); } }