goodidea82 Posted January 16, 2016 Share Posted January 16, 2016 (edited) Using cleo-sdk I want to define a new opcode X which calls an existing opcode Y. How do I make a call to opcode Y within the implementation of X? How do I pass parameters to Y? The reason I want to do this is to apply transformations on the input and output parameter before/after calling Y. Opcode X will be a wrapper of Y. For convenience here is cleo.h from cleo 4.3 /* CLEO 4.3 header file; Copyright © 2014 Alien, Deji;*/#pragma once#include <wtypes.h>#define CLEO_VERSION 0x04031400#define CLEO_VERSIONTEXT "4.3"//result of CLEO_GetGameVersion()#define GV_US10 0 //1.0 us#define GV_US11 1 //1.01 us - not supported#define GV_EU10 2 //1.0 eu#define GV_EU11 3 //1.01 eu#define GV_UNK -1 //any othertypedef union{ DWORD dwParam; int nParam; float fParam; void * pParam; char * szParam;} SCRIPT_VAR;//operand types#define globalVar 2 //$#define localVar 3 //@#define globalArr 7 //$(,)#define localArr 8 //@(,)#define imm8 4 //char#define imm16 5 //short#define imm32 6 //long, unsigned long#define imm32f 1 //float#define vstring 0x0E //""#define sstring 9 //''#define globalVarVString 0x10 //v$#define localVarVString 0x11 //@v#define globalVarSString 0x0A //s$#define localVarSString 0x0B //@stypedef int SCRIPT_HANDLE;typedef SCRIPT_HANDLE HANDLE_ACTOR, ACTOR, HACTOR, PED, HPED, HANDLE_PED;typedef SCRIPT_HANDLE HANDLE_CAR, CAR, HCAR, VEHICLE, HVEHICLE, HANDLE_VEHICLE;typedef SCRIPT_HANDLE HANDLE_OBJECT, OBJECT, HOBJECT;typedef struct CScriptThread CScriptThread;#pragma pack(push,1)struct CScriptThread{ CScriptThread *next; //next script in queue CScriptThread *prev; //previous script in queue char threadName[8]; //name of thread, given by 03A4 opcode BYTE *baseIp; //pointer to begin of script in memory BYTE *ip; //current index pointer BYTE *stack[8]; //return stack for 0050, 0051 WORD sp; //current item in stack WORD _f3A; //padding SCRIPT_VAR tls[34]; //thread's local variables BYTE isActive; //is current thread active char condResult; //condition result (true or false) char missionCleanupFlag; //clean mission char external; //is thread external (from script.img) BYTE _fC8; //unknown BYTE _fC9; //unknown BYTE _fCA; //unknown BYTE _fCB; //unknown DWORD wakeTime; //time, when script starts again after 0001 opcode WORD logicalOp; //00D6 parameter BYTE notFlag; //opcode & 0x8000 != 0 BYTE wbCheckEnabled; //wasted_or_busted check flag BYTE wastedOrBusted; //is player wasted or busted BYTE _fD5; //unknown WORD _fD6; //unknown DWORD sceneSkip; //scene skip label ptr BYTE missionFlag; //is mission thread BYTE _fDD[3]; //padding};#pragma pack(pop)#define OR_CONTINUE 0#define OR_INTERRUPT 1typedef int OpcodeResult;typedef OpcodeResult (CALLBACK* _pOpcodeHandler)(CScriptThread*);#ifdef __cplusplusextern "C" {#endif //__cplusplusDWORD WINAPI CLEO_GetVersion();int WINAPI CLEO_GetGameVersion();BOOL WINAPI CLEO_RegisterOpcode(WORD opcode, _pOpcodeHandler callback);DWORD WINAPI CLEO_GetIntOpcodeParam(CScriptThread* thread);float WINAPI CLEO_GetFloatOpcodeParam(CScriptThread* thread);void WINAPI CLEO_SetIntOpcodeParam(CScriptThread* thread, DWORD value);void WINAPI CLEO_SetFloatOpcodeParam(CScriptThread* thread, float value);LPSTR WINAPI CLEO_ReadStringOpcodeParam(CScriptThread* thread, LPSTR buf, int size);void WINAPI CLEO_WriteStringOpcodeParam(CScriptThread* thread, LPCSTR str);void WINAPI CLEO_SetThreadCondResult(CScriptThread* thread, BOOL result);void WINAPI CLEO_SkipOpcodeParams(CScriptThread* thread, int count);void WINAPI CLEO_ThreadJumpAtLabelPtr(CScriptThread* thread, int labelPtr);int WINAPI CLEO_GetOperandType(CScriptThread* thread);SCRIPT_VAR *opcodeParams;SCRIPT_VAR *missionLocals;//intermediate data is stored in opcodeParams arrayvoid WINAPI CLEO_RetrieveOpcodeParams(CScriptThread *thread, int count);void WINAPI CLEO_RecordOpcodeParams(CScriptThread *thread, int count);SCRIPT_VAR * WINAPI CLEO_GetPointerToScriptVariable(CScriptThread *thread);#ifdef __cplusplus}#endif //__cplusplus and here is an example from the sdk of a custom opcode OpcodeResult WINAPI Script_IntOp_AND(CScriptThread* thread)/**************************************************************** Opcode Format0B10=3,%3d% = %1d% AND %2d%****************************************************************/{ int a = CLEO_GetIntOpcodeParam(thread); int b = CLEO_GetIntOpcodeParam(thread); CLEO_SetIntOpcodeParam(thread, a & b); return OR_CONTINUE;} Edited January 16, 2016 by goodidea82 Link to comment Share on other sites More sharing options...
Seemann Posted January 16, 2016 Share Posted January 16, 2016 I guess you can't do it easy. Instead try running a function or method that the desired opcode calls itself. Say, you want to call 0B10, then just call CLEO_SetIntOpcodeParam(thread, a & b); Otherwise you need to make a proper hex code to parse with CRunningScript::ProcessOneCommand(). goodidea82 1 Sanny Builder 3 • SA Memory Handling • OpenIV • gtamodding.com CLEO.li - The CLEO Library - Official site Link to comment Share on other sites More sharing options...
fastman92 Posted January 16, 2016 Share Posted January 16, 2016 use my CRunningScriptWrapper coming in SA Plugin SDK goodidea82 and dkluin 2 Link to comment Share on other sites More sharing options...
goodidea82 Posted January 16, 2016 Author Share Posted January 16, 2016 (edited) @Seemann. Despite fastman's solution, if you know how to pass parameters and call an opcode using ProcessOneCommand() that would be interesting to see. I suppose methods from Plugin SDK are needed? #pragma once#include <plugin/plugin.h>#include "ePedType.h"#include "eCommandName.h"#include "eWeaponType.h"#define FUNC_CRunningScript__Init 0x4648E0#define FUNC_CRunningScript__GetArrayOffsetAndValueOfIndexVariable 0x463CF0#define FUNC_CRunningScript__GetOffsetOfGlobalVariable 0x464700#define FUNC_CRunningScript__GetPointerToScriptVariable 0x464790#define FUNC_CRunningScript__GetPointerLocalVariableByArrayIndex 0x463CC0#define FUNC_CRunningScript__ProcessOneCommand 0x469EB0#define FUNC_CRunningScript__CollectParameters 0x464080#define FUNC_CRunningScript__CollectStringParameter 0x463D50#define FUNC_CRunningScript__StoreParameters 0x464370#define FUNC_CRunningScript__CollectParametersToNewScript 0x464500#define FUNC_CRunningScript__Process 0x469F00#define FUNC_CRunningScript__DoDeatharrestCheck 0x485A50#define FUNC_CRunningScript__CollectNextParameterWithoutIncreasingPC 0x464250#define FUNC_CRunningScript__SetIntructionPointer 0x464DA0#define FUNC_CRunningScript__UpdateCompareFlag 0x4859D0#define FUNC_CRunningScript__AddScriptToList 0x464C00#define FUNC_CRunningScript__RemoveScriptFromList 0x464BD0#define FUNC_CRunningScript__GetPadState 0x485B10#define FUNC_CRunningScript__TerminateThisScript 0x465AA0#define FUNC_CRunningScript__CheckDamagedWeaponType 0x43D9E0#define FUNC_CRunningScript__CarInAreaCheckCommand 0x488EC0#define FUNC_CRunningScript__CharInAreaCheckCommand 0x488B50#define FUNC_CRunningScript__LocateCarCommand 0x487A20#define FUNC_CRunningScript__LocateCharCarCommand 0x487420#define FUNC_CRunningScript__LocateCharCharCommand 0x4870F0#define FUNC_CRunningScript__LocateCharCommand 0x486D80#define FUNC_CRunningScript__LocateCharObjectCommand 0x487720#define FUNC_CRunningScript__LocateObjectCommand 0x487D10#define FUNC_CRunningScript__ObjectInAreaCheckCommand 0x489150#define FUNC_CRunningScript__ThisIsAValidRandomPed 0x489490// Command handlers#define FUNC_CRunningScript__ProcessCommands_0To99 0x465E60#define FUNC_CRunningScript__ProcessCommands_100To199 0x466DE0#define FUNC_CRunningScript__ProcessCommands_200To299 0x469390#define FUNC_CRunningScript__ProcessCommands_300To399 0x47C100#define FUNC_CRunningScript__ProcessCommands_400To499 0x47D210#define FUNC_CRunningScript__ProcessCommands_500To599 0x47E090#define FUNC_CRunningScript__ProcessCommands_600To699 0x47F370#define FUNC_CRunningScript__ProcessCommands_700To799 0x47FA30#define FUNC_CRunningScript__ProcessCommands_800To899 0x481300#define FUNC_CRunningScript__ProcessCommands_900To999 0x483BD0#define FUNC_CRunningScript__ProcessCommands_1000To1099 0x489500#define FUNC_CRunningScript__ProcessCommands_1100To1199 0x48A320#define FUNC_CRunningScript__ProcessCommands_1200To1299 0x48B590#define FUNC_CRunningScript__ProcessCommands_1300To1399 0x48CDD0#define FUNC_CRunningScript__ProcessCommands_1400To1499 0x48EAA0#define FUNC_CRunningScript__ProcessCommands_1500To1599 0x490DB0#define FUNC_CRunningScript__ProcessCommands_1600To1699 0x493FE0#define FUNC_CRunningScript__ProcessCommands_1700To1799 0x496E00#define FUNC_CRunningScript__ProcessCommands_1800To1899 0x46D050#define FUNC_CRunningScript__ProcessCommands_1900To1999 0x46B460#define FUNC_CRunningScript__ProcessCommands_2000To2099 0x472310#define FUNC_CRunningScript__ProcessCommands_2100To2199 0x470A90#define FUNC_CRunningScript__ProcessCommands_2200To2299 0x474900#define FUNC_CRunningScript__ProcessCommands_2300To2399 0x4762D0#define FUNC_CRunningScript__ProcessCommands_2400To2499 0x478000#define FUNC_CRunningScript__ProcessCommands_2500To2599 0x47A760#define FUNC_CRunningScript__ProcessCommands_2600To2699 0x479DA0enum eArgumentDataTypesFormat_GTA_III_VC_SA : __int8{ SCM_ARGUMENT_TYPE_END_OF_ARGUMENTS, SCM_ARGUMENT_TYPE_STATIC_INT_32BITS, SCM_ARGUMENT_TYPE_GLOBAL_NUMBER_VARIABLE, SCM_ARGUMENT_TYPE_LOCAL_NUMBER_VARIABLE, SCM_ARGUMENT_TYPE_STATIC_INT_8BITS, SCM_ARGUMENT_TYPE_STATIC_INT_16BITS, SCM_ARGUMENT_TYPE_STATIC_FLOAT, // Types below are only available in GTA SA SCM_ARGUMENT_TYPES_INTRODUCED_IN_GTASA, // Number arrays SCM_ARGUMENT_TYPE_GLOBAL_NUMBER_ARRAY = SCM_ARGUMENT_TYPES_INTRODUCED_IN_GTASA, SCM_ARGUMENT_TYPE_LOCAL_NUMBER_ARRAY, SCM_ARGUMENT_TYPE_STATIC_SHORT_STRING, SCM_ARGUMENT_TYPE_GLOBAL_SHORT_STRING_VARIABLE, SCM_ARGUMENT_TYPE_LOCAL_SHORT_STRING_VARIABLE, SCM_ARGUMENT_TYPE_GLOBAL_SHORT_STRING_ARRAY, SCM_ARGUMENT_TYPE_LOCAL_SHORT_STRING_ARRAY, SCM_ARGUMENT_TYPE_STATIC_PASCAL_STRING, SCM_ARGUMENT_TYPE_STATIC_LONG_STRING, SCM_ARGUMENT_TYPE_GLOBAL_LONG_STRING_VARIABLE, SCM_ARGUMENT_TYPE_LOCAL_LONG_STRING_VARIABLE, SCM_ARGUMENT_TYPE_GLOBAL_LONG_STRING_ARRAY, SCM_ARGUMENT_TYPE_LOCAL_LONG_STRING_ARRAY,};union tScriptVarValue{ unsigned __int32 dwParam; __int32 nParam; float fParam; void *pParam; char *szParam;};VALIDATE_SIZE(tScriptVarValue, 0x4);#pragma pack(push, 1)class PLUGIN_API CRunningScript{public: // FUNCTIONS CRunningScript *next; CRunningScript *prev; char threadName[8]; uint8_t *baseIP; uint8_t *curIP; uint8_t *gosubStack[8]; uint16_t gosubStackLevel; uint16_t _f3A; tScriptVarValue tls[32]; uint32_t timers[2]; bool isActive; bool condResult; bool MissionCleanUpFlag; bool IsExternalThread; uint8_t _fC8; char scrType; uint8_t _fCA; uint8_t _fCB; uint32_t wakeTime; uint16_t logicalOp; bool notFlag; bool bDeathArrestCheckEnabled; bool wastedOrBusted; uint8_t _fD5; uint16_t _fD6; uint32_t sceneSkipOffset; bool missionFlag; // VARIABLES // bellow is align in 4 bytes uint16_t scmFunction; uint8_t IsCustom; // Initializes member variables. void Init(); // Processes running script void Process(); // Processes one command char ProcessOneCommand(); // Performs death arrest check void DoDeatharrestCheck(); /////// USED HEAVILY IN COMMANDS ////// // Reads array offset and value from array index variable. void GetArrayOffsetAndValueOfIndexVariable(__int16 *pOffset, __int32 *pIdx); // Returns offset of global variable __int16 GetOffsetOfGlobalVariable(); // Returns pointer to script variable of any type. tScriptVarValue* GetPointerToScriptVariable(unsigned __int8 variableType); // Collects parameters void CollectParameters(__int16 count); // Collects parameter and returns it. tScriptVarValue CollectNextParameterWithoutIncreasingPC(); // Collects string parameter void CollectStringParameter(char *pBuffer, unsigned __int8 nBufferLength); // Stores parameters void StoreParameters(__int16 count); // Collects parameters and puts them to local variables of new script void CollectParametersToNewScript(CRunningScript* pNewScript); // Sets instruction pointer, used in GOTO-like commands void SetIntructionPointer(__int32 newIP); // Updates comparement flag, used in conditional commands void UpdateCompareFlag(bool state); // Terminates a script void TerminateThisScript(); /////////////////// // Returns condition result bool GetConditionResult(); // Returns pointer to local variable pointed by offset and array index as well as multiplier. void GetPointerLocalVariableByArrayIndex(__int16 off, __int16 idx, unsigned __int8 mul); // Adds script to list void AddScriptToList(CRunningScript ** list); // Removes script from list void RemoveScriptFromList(CRunningScript ** list); // Returns state of pad button. short GetPadState(unsigned short playerIndex, unsigned short buttonID); // Command handlers char ProcessCommands_0To99(eCommandName commandID); char ProcessCommands_100To199(eCommandName commandID); char ProcessCommands_200To299(eCommandName commandID); char ProcessCommands_300To399(eCommandName commandID); char ProcessCommands_400To499(eCommandName commandID); char ProcessCommands_500To599(eCommandName commandID); char ProcessCommands_600To699(eCommandName commandID); char ProcessCommands_700To799(eCommandName commandID); char ProcessCommands_800To899(eCommandName commandID); char ProcessCommands_900To999(eCommandName commandID); char ProcessCommands_1000To1099(eCommandName commandID); char ProcessCommands_1100To1199(eCommandName commandID); char ProcessCommands_1200To1299(eCommandName commandID); char ProcessCommands_1300To1399(eCommandName commandID); char ProcessCommands_1400To1499(eCommandName commandID); char ProcessCommands_1500To1599(eCommandName commandID); char ProcessCommands_1600To1699(eCommandName commandID); char ProcessCommands_1700To1799(eCommandName commandID); char ProcessCommands_1800To1899(eCommandName commandID); char ProcessCommands_1900To1999(eCommandName commandID); char ProcessCommands_2000To2099(eCommandName commandID); char ProcessCommands_2100To2199(eCommandName commandID); char ProcessCommands_2200To2299(eCommandName commandID); char ProcessCommands_2300To2399(eCommandName commandID); char ProcessCommands_2400To2499(eCommandName commandID); char ProcessCommands_2500To2599(eCommandName commandID); char ProcessCommands_2600To2699(eCommandName commandID); // Checks if damage ID is valid to expected damage weapon ID. static bool CheckDamagedWeaponType(eWeaponType damageWeaponID, eWeaponType expectedDamageWeaponID); // Processes commands that check if car is in specified area. void CarInAreaCheckCommand(eCommandName commandID); // Processes commands that check if char is in specified area. void CharInAreaCheckCommand(eCommandName commandID); // Processes commands that locate a vehicle void LocateCarCommand(eCommandName commandID); // Processes commands where char locates car void LocateCharCarCommand(eCommandName commandID); // Processes commands where char locates another char void LocateCharCharCommand(eCommandName commandID); // Processes commands where char locates map point void LocateCharCommand(eCommandName commandID); // Processes commands where char locates object void LocateCharObjectCommand(eCommandName commandID); // Processes commands where object locates map point void LocateObjectCommand(eCommandName commandID); // Processes commands that check if object is in area void ObjectInAreaCheckCommand(eCommandName commandID); // Checks if ped type conforms to valid ped types. bool ThisIsAValidRandomPed(ePedType pedType, bool civilian, bool gang, bool criminal);};#pragma pack(pop)VALIDATE_SIZE(CRunningScript, 0xE0); @fastman92. Can you tell us more about it and perhaps show an example how passing parameters and calling an opcode will work with your api. Edited January 16, 2016 by goodidea82 Link to comment Share on other sites More sharing options...
fastman92 Posted January 17, 2016 Share Posted January 17, 2016 In my LUA library I have a Script class which uses SCM wrapper itself. #pragma once#include "Commands_PED.h"using namespace ScriptManager;namespace Commands_PED{ const luaL_Reg Commands_PED::CommandList[] = { luaL_reg_function(GetWorldPosition), luaL_reg_end }; // Returns position // Example: local x, y, z = PED.GetWorldPosition(playerHandle) int Commands_PED::GetWorldPosition(lua_State* L) { int pedHandle = pCurrentScriptInfo -> GetInt(1); pCurrentScriptInfo -> TheSCMwrapper.PushInt(pedHandle, false); pCurrentScriptInfo -> TheSCMwrapper.PushReturnArguments(RUNNING_SCRIPT_VALUE_TYPE_FLOAT, 3); pCurrentScriptInfo -> TheSCMwrapper.CallCommand(COMMAND_GET_CHAR_COORDINATES); pCurrentScriptInfo -> PushFloat(*pCurrentScriptInfo -> TheSCMwrapper.ReturnedValues[0].data.pFloat); pCurrentScriptInfo -> PushFloat(*pCurrentScriptInfo -> TheSCMwrapper.ReturnedValues[1].data.pFloat); pCurrentScriptInfo -> PushFloat(*pCurrentScriptInfo -> TheSCMwrapper.ReturnedValues[2].data.pFloat); return 3; }}You need CRunningScriptWrapper, not CRunningScript.https://github.com/DK22Pac/plugin-sdk/blob/master/src/sdk/plugin/script/wrapper.h Using this class requires pushing the arguments and calling a command by CallCommand. The returned values will be accessible from ReturnedValues after calling a command. They can also be saved into structure by SaveReturnedValues method. Link to comment Share on other sites More sharing options...
goodidea82 Posted January 17, 2016 Author Share Posted January 17, 2016 Thanks, this is just what I have been looking for. The example is very good. I will leave the thread open in case I have a question. Link to comment Share on other sites More sharing options...
fastman92 Posted January 19, 2016 Share Posted January 19, 2016 http://pastebin.com/WSrRc4dv goodidea82 1 Link to comment Share on other sites More sharing options...
goodidea82 Posted January 31, 2016 Author Share Posted January 31, 2016 (edited) Thanks. Meanwhile I have returned from traveling. Where can I find the implementation of CRunningScriptWrapper or are you still working on it? Edit: Ok found it. It is part of wrapper.cpp. Edited January 31, 2016 by goodidea82 Link to comment Share on other sites More sharing options...
goodidea82 Posted February 12, 2016 Author Share Posted February 12, 2016 (edited) The CLEO sdk (version 4.3) provides the following functions to read and write strings. LPSTR WINAPI CLEO_ReadStringOpcodeParam(CScriptThread* thread, LPSTR buf, int size);void WINAPI CLEO_WriteStringOpcodeParam(CScriptThread* thread, LPCSTR str); Do these functions support short strings, long strings, or variables strings, or do they handle all of them? For example, wrapper.h provides different function for different string types: // Pushes a short string value void PushShortString(const char* value, bool bIsReturnedValue = true); // Pushes a long string value void PushLongString(const char* value, bool bIsReturnedValue = true); // Pushes a varlen string void PushVarlenString(const char* value); void PushReturnArgument(eRunningScriptWrapperDataValueType valueType);//withenum eRunningScriptWrapperDataValueType{ RUNNING_SCRIPT_VALUE_TYPE_INT = 1, // 4 bytes, integer RUNNING_SCRIPT_VALUE_TYPE_FLOAT = 2, // 4 bytes, float RUNNING_SCRIPT_VALUE_TYPE_TEXT_LABEL = 3, // 8 bytes, short string RUNNING_SCRIPT_VALUE_TYPE_STRING = 4 // 16 bytes, long string}; --------- Edit: Here is an excerpt from the implementation of CLEO 4.2. 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'; } It seams that 16bytes are written by the second function to it seems that this is for long strings. Is this assumption correct? If so, then how to handle short strings and varlenght strings? Edited February 14, 2016 by goodidea82 Link to comment Share on other sites More sharing options...
Link2012 Posted February 14, 2016 Share Posted February 14, 2016 Since variables can only handle 16 characters you have that.on the writer but not on the reader. So yeah, the reader handles varlens fine. Both don't handle integer values being pointers to a string, you have to handle that yourself. goodidea82 1 Link to comment Share on other sites More sharing options...
goodidea82 Posted February 16, 2016 Author Share Posted February 16, 2016 (edited) In SCMWrapper what is the meaning of the parameter bIsReturnedValue which appears in several function? For example, void PushInt(__int32 value, bool bIsReturnedValue = true) When should I use the default value true and when should I set it to false? class PLUGIN_API CRunningScriptWrapper : public CRunningScript{// private:public: // Used to put code of command into uint8_t CommandSpace[2000]; uint8_t* CommandSpaceArguments; // VARIABLES int pushArgIndex; int pushReturnValueIndex;public: // Number of returned values int numberOfReturnedValues; // Returned values tRunningScriptWrapperDataValue ReturnedValues[maxNumberOfReturnedValues];private: char reserved[32]; // for the future purpose of extending a struct. public: // FUNCTIONS // Constructor CRunningScriptWrapper(); // Saves returned values to object of type tRunningScriptWrapper_SavedReturnedValuesArray. // This function may only be called after execution of CallCommand - pushing values past the execution of CallCommand will overwrite values void SaveReturnedValues(tRunningScriptWrapper_SavedReturnedValuesArray& arrayOfSavedReturnedValues); // Calls a command. Return values: (-1 : invalid command ID, 0: continue, 1: WAIT) char CallCommand(eCommandName commandID); // Calls a command. Return values: (-1 : invalid command ID, 0: continue, 1: WAIT) char CallCommand(__int16 commandID); // Returns offset of global variable by number of variable static int GetGlobalVariableOffsetByNumber(int variableNum); // Returns pointer to global variable tScriptVarValue* GetPointerToGlobalVariableByOffset(int variableOffset); ///////// Functions to pass arguments ///////// // Pushes boolean argument void PushBoolean(bool value, bool bIsReturnedValue = true); // Pushes float value void PushFloat(float value, bool bIsReturnedValue = true); // Pushes integer value void PushInt(__int32 value, bool bIsReturnedValue = true); // Pushes a pointer void PushPointer(void* value, bool bIsReturnedValue = true); // Pushes a short string value void PushShortString(const char* value, bool bIsReturnedValue = true); // Pushes a long string value void PushLongString(const char* value, bool bIsReturnedValue = true); // Pushes a varlen string void PushVarlenString(const char* value); // Push a global variable value void PushGlobalVariable(int globalVarOffset, eRunningScriptWrapperDataValueType valueType, bool bIsReturnedValue = true); // Pushes a return argument void PushReturnArgument(eRunningScriptWrapperDataValueType valueType); // Pushes return arguments void PushReturnArguments(eRunningScriptWrapperDataValueType valueType, int count);private: // Initializes wrapper variables void InitWrapperVars(); // Pushes string value void PushStringAsVar(const char* value, eRunningScriptWrapperDataValueType valueType, bool bIsReturnedValue); // Writes an argument a local/global variable void WriteArgumentVariable(eArgumentDataTypesFormat_GTA_III_VC_SA argumentType, int numberOfLocalVarsTaken);}; Here is the implementation: // Pushes integer valuevoid CRunningScriptWrapper::PushInt(__int32 value, bool bIsReturnedValue){ this -> tls[pushArgIndex].nParam = value; if(bIsReturnedValue) { tRunningScriptWrapperDataValue& returnedValue = this -> ReturnedValues[this -> pushReturnValueIndex++]; returnedValue.type = RUNNING_SCRIPT_VALUE_TYPE_INT; returnedValue.data.pInt = &this -> tls[pushArgIndex].nParam; } this -> WriteArgumentVariable(SCM_ARGUMENT_TYPE_LOCAL_NUMBER_VARIABLE, 1);} For passing return variables the following function is used: SCMwrapper.PushReturnArgument(RUNNING_SCRIPT_VALUE_TYPE_INT); Edited February 16, 2016 by goodidea82 Link to comment Share on other sites More sharing options...
fastman92 Posted February 16, 2016 Share Posted February 16, 2016 (edited) Some SCM commands (mostly related to pickups) not only will read a value a value from variable, but also update a value. The variable must be passed then. bIsReturnedValue needs to be true. Basically, it's never wrong if bIsReturnedValue will be set true. New possible value can be found within ReturnedValues. According to order of values that were said to be "returned". Edited February 16, 2016 by fastman92 Link to comment Share on other sites More sharing options...
goodidea82 Posted February 16, 2016 Author Share Posted February 16, 2016 Is there a way to find out which opcodes have arguments with such double roles? FYI. So far I'm using the gtag database to find out which parameters are input and output parameters, I'm not aware how to recognize if a parameter is both input and output. Link to comment Share on other sites More sharing options...
fastman92 Posted February 16, 2016 Share Posted February 16, 2016 (edited) Is there a way to find out which opcodes have arguments with such double roles? FYI. So far I'm using the gtag database to find out which parameters are input and output parameters, I'm not aware how to recognize if a parameter is both input and output. The SCM commands that use this function: .text:00464250 ; ScriptVar __thiscall CRunningScript::CollectNextParameterWithoutIncreasingPC(CRunningScript *this).text:00464250 _ZN14CRunningScript39CollectNextParameterWithoutIncreasingPCEv proc nearExample: case 0x186: CRunningScript::CollectParameters(this, 1u); v65 = CRunningScript::CollectNextParameterWithoutIncreasingPC(v2).dwParam; CRadar::GetActualBlipArrayIndex(v65); v64 = CRadar::setEntityBlip(1, CollectiveArray[0].dwParam, 0, 3); goto LABEL_114;LABEL_114: v66 = v64; CRadar::setBlipScaleMode(v64, 3); goto LABEL_120;LABEL_120: CollectiveArray[0].dwParam = v66; CRunningScript__StoreParameters(v2, 1); return 0; http://gtag.gtagaming.com/opcode-database/opcode/0186/ The second parameter is read and written. Edited February 16, 2016 by fastman92 goodidea82 1 Link to comment Share on other sites More sharing options...
goodidea82 Posted February 16, 2016 Author Share Posted February 16, 2016 (edited) I'm not an IDA user. Could you or someone create a complete list of opcodes (up to opcode 05A8) which have these special parameters? (I only need to create wrappers for opcodes that exist either in VC or LC, so do not need opcodes above 05A8) Edit: perhaps, providing the surrounding code where it appears is sufficient (as done above), because it's possible to see which opcode it is. Edit2: And I don't need opcodes that do arithmetic operations, e.g. +=, *=, -= because they behave the same in all games, so I don't need wrappers for opcodes below 004B. Edited February 16, 2016 by goodidea82 Link to comment Share on other sites More sharing options...
fastman92 Posted February 16, 2016 Share Posted February 16, 2016 I'm not an IDA user. Could you or someone create a complete list of opcodes (up to opcode 05A8) which have these special parameters? (I only need to create wrappers for opcodes that exist either in VC or LC, so do not need opcodes above 05A8) Edit: perhaps, providing the surrounding code where it appears is sufficient (as done above), because it's possible to see which opcode it is. Edit2: And I don't need opcodes that do arithmetic operations, e.g. +=, *=, -= because they behave the same in all games, so I don't need wrappers for opcodes below 004B. You could consider all the values to be returned.Nothing bad will happen. Link to comment Share on other sites More sharing options...
goodidea82 Posted February 16, 2016 Author Share Posted February 16, 2016 (edited) Here is an example how my wrapper looks for 0186 (it's automatically generated): /* add blip for car PARAMS: carHandle,varInt */OpcodeResult WINAPI opcode1186(CScriptThread* t){ DWORD carHandle1 = CLEO_GetIntOpcodeParam(t); SCMwrapper.PushInt(carHandle1, false); SCMwrapper.PushReturnArgument(RUNNING_SCRIPT_VALUE_TYPE_INT); SCMwrapper.CallCommand(0x0186); SCMwrapper.SaveReturnedValues(retVal); CLEO_SetIntOpcodeParam(t, retVal.returnedValues[0].Int); return OR_CONTINUE;} The problem in this code is that the second parameter is treated only as a return parameter. There is an input file for my generator which tells it that the scond parameter is a return parameter and therefore it generates not call to a CLEO function which reads the second parameters as input. If I understand you correctly, you suggest that I should read for all opcodes, all parameters as input parameters (using functions such as CLEO_GetIntOpcodeParam(t);) and never use the function SCMwrapper.PushReturnArgument(RUNNING_SCRIPT_VALUE_TYPE_INT);, but only SCMwrapper.PushInt(carHandle1, true); on all parameters. Is that correct? The code would look like this: /* add blip for car PARAMS: carHandle,varInt */OpcodeResult WINAPI opcode1186(CScriptThread* t){ DWORD carHandle1 = CLEO_GetIntOpcodeParam(t); DWORD blibInt = CLEO_GetIntOpcodeParam(t); SCMwrapper.PushInt(carHandle1, true); SCMwrapper.PushInt(blibInt, true); //SCMwrapper.PushReturnArgument(RUNNING_SCRIPT_VALUE_TYPE_INT); SCMwrapper.CallCommand(0x0186); SCMwrapper.SaveReturnedValues(retVal); CLEO_SetIntOpcodeParam(t, retVal.returnedValues[1].Int); return OR_CONTINUE;} However, a problem here is that when CLEO_SetIntOpcodeParam(t, retVal.returnedValues[1].Int); is called, this is like a third parameter, but the opcode has only 2 parameters. So somehow the second parameters would have to be read and written without incrementing the parameter index (or program counter). Edit: I guess the problem can be solved when using the the fuction SCRIPT_VAR * WINAPI CLEO_GetPointerToScriptVariable(CScriptThread *thread); for all return variables. Using this functions variable parameter can be read and written via the pointer while progressing the program counter only once (not twice). /* add blip for car PARAMS: carHandle,varInt */OpcodeResult WINAPI opcode1186(CScriptThread* t){ DWORD carHandle1 = CLEO_GetIntOpcodeParam(t); SCRIPT_VAR * blibIntVar = CLEO_GetPointerToScriptVariable(t); DWORD blibInt = blibIntVar->dwParam; SCMwrapper.PushInt(carHandle1, true); SCMwrapper.PushInt(blibInt, true); //SCMwrapper.PushReturnArgument(RUNNING_SCRIPT_VALUE_TYPE_INT); SCMwrapper.CallCommand(0x0186); SCMwrapper.SaveReturnedValues(retVal); blibIntVar->dwParam = retVal.returnedValues[1].Int; //CLEO_SetIntOpcodeParam(t, retVal.returnedValues[1].Int); return OR_CONTINUE;} However, this makes the whole thing more complicated. If the number of opcodes in the range 004B - 05A8 which have these special parameters is very small, then it would be better to treat only those in a special way instead of doing this for all the opcodes. So if a simple text search for "CollectNextParameterWithoutIncreasingPC" in IDA would do the job, that would be helpful. Edited February 16, 2016 by goodidea82 Link to comment Share on other sites More sharing options...
fastman92 Posted February 17, 2016 Share Posted February 17, 2016 However, a problem here is that when CLEO_SetIntOpcodeParam(t, retVal.returnedValues[1].Int); is called, this is like a third parameter, but the opcode has only 2 parameters. So somehow the second parameters would have to be read and written without incrementing the parameter index (or program counter).That's not SCM wrapper problem, but maybe a functionality of CLEO. Link to comment Share on other sites More sharing options...
goodidea82 Posted February 17, 2016 Author Share Posted February 17, 2016 That's right, this is not an SCM wrapper problem but a problem how I must read and write parameters of (a) the wrapper opcode using CLEO (e.g. 1186), and (b) of the original opcode using SCMwrapper (e.g. 0186). The wrapper must have the same signature as the original one. At the end of my last post I describe how to probably solve it, but I would like to apply this solution only to the opcodes which use the CollectNextParameterWithoutIncreasingPC function. Link to comment Share on other sites More sharing options...
goodidea82 Posted June 30, 2017 Author Share Posted June 30, 2017 This is just a note for myself so that I will find it later: https://github.com/DK22Pac/plugin-sdk/issues/34 fastman92 1 Link to comment Share on other sites More sharing options...
fastman92 Posted June 30, 2017 Share Posted June 30, 2017 That's right, this is not an SCM wrapper problem but a problem how I must read and write parameters of (a) the wrapper opcode using CLEO (e.g. 1186), and (b) of the original opcode using SCMwrapper (e.g. 0186). The wrapper must have the same signature as the original one. At the end of my last post I describe how to probably solve it, but I would like to apply this solution only to the opcodes which use the CollectNextParameterWithoutIncreasingPC function. You need to use a function to push a local variable for these opcodes. Link to comment Share on other sites More sharing options...
goodidea82 Posted July 1, 2017 Author Share Posted July 1, 2017 Back in 2016 I have solved the problem as described at the very bottom (last code snipet) of this post. I save the pointers to all the parameters to make them available for writing after callling the original opcode. I guess this is what you mean with "push a local variable for these opcodes". Thanks Link to comment Share on other sites More sharing options...
goodidea82 Posted July 20, 2017 Author Share Posted July 20, 2017 Dumping information here for later use because I close the issue on Github. Some earlier version had a wrapper (SCMWrapper) for opcodes (developed by fastman92), but now the API is missing:https://github.com/DK22Pac/plugin-sdk/blob/master/src/sdk/plugin/script/wrapper.hThis API is important for because I already have a lot of custom code that depend on it. (Some background: http://gtaforums.com/topic/838490-cleo-plugin-how-to-call-an-opcode-within-a-cleo-plugin/?p=1068513051) Owner DK22Pac commented 22 days ago • edited It's possible to "call" an opcode in this way: #include "extensions\ScriptCommands.h"using namespace plugin::test;// ...ScriptCommand<PRINT_NOW>("FEC_INC", 150, 1); #include "plugin_III.h"#include "extensions\ScriptCommands.h"using namespace plugin;using namespace plugin::test;class Gta3ScriptCommandsTest {public: Gta3ScriptCommandsTest() { Events::processScriptsEvent += [] { int PlayerChar, PlayerCar; float CoordX, CoordY, CoordZ; if (ScriptCommand<IS_PLAYER_PLAYING>(0)) { ScriptCommand<GET_PLAYER_CHAR>(0, &PlayerChar); if (ScriptCommand<IS_CHAR_IN_ANY_CAR>(PlayerChar)) { ScriptCommand<STORE_CAR_CHAR_IS_IN_NO_SAVE>(PlayerChar, &PlayerCar); ScriptCommand<GET_CAR_COORDINATES>(PlayerCar, &CoordX, &CoordY, &CoordZ); ScriptCommand<DRAW_SHADOW>(3, CoordX, CoordY, CoordZ, 0.0f, 3.0f, 150, 255, 0, 0); ScriptCommand<PRINT_NOW>("FEC_INC", 150, 1); } } }; }} test; goodidea82 commented 21 days ago • edited Thanks for the quick reply. I have a special use case where wrapper functions are automatically generated for opcodes. Can I use hex-numbers instead of opcode names? E.g. ScriptCommand< 0x00AA >(PlayerCar, &CoordX, &CoordY, &CoordZ); Do I need the "Events::processScriptsEvent += [] {...}" to call an opcode and what does it mean? I want the called opcode have the same effect on the current thread as a normal opcode would have. How do I pass the thread-pointer to the ScriptCommand? (or obtain a thread pointer if it has its own thread) In my code I have a function "copyThreadAttrToWrapper" that translates the state from the current thread to the fastman's SCMwrapper's thread and "copyWrapperToThread" to translate it back. Here is an example of what I mean: void op00A0(DWORD actHandle1, float& varX2, float& varY3, float& varZ4){ SCMwrapper.PushInt(actHandle1, true); SCMwrapper.PushFloat(varX2, true); SCMwrapper.PushFloat(varY3, true); SCMwrapper.PushFloat(varZ4, true); copyThreadAttrToWrapper(curThread, SCMwrapper, (curThread->notFlag? 0x80A0: 0x00A0)); cmdResult = SCMwrapper.CallCommand(curThread->notFlag? 0x80A0: 0x00A0); copyWrapperToThread(SCMwrapper, curThread); SCMwrapper.SaveReturnedValues(retVal); varX2 = varX(retVal.returnedValues[1].Float); varY3 = varY(retVal.returnedValues[2].Float); varZ4 = varZ(retVal.returnedValues[3].Float); } The easiest for me would be if fastman's SCMwrapper API would be added back. Owner DK22Pac commented 18 days ago • edited Yes. Can I use hex-numbers instead of opcode names? Do I need the "Events::processScriptsEvent += [] {...}" to call an opcode and what does it mean? It's recommended to 'call' opcodes when scripts are 'processed' (i.e. executed). That's why processScriptsEvent event is used in this example. How do I pass the thread-pointer to the ScriptCommand? Not possible yet. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now