From af4bad36d445629d2b13542cec4df3612f4cd48d Mon Sep 17 00:00:00 2001 From: phobos2077 Date: Fri, 4 Nov 2016 17:22:05 +0700 Subject: [PATCH] Added validation for null arguments #55; Added scripting function "spatial_radius"; Added scripting function "critter_inven_obj2" #38. --- sfall/FalloutEngine.cpp | 23 +++++++++++++++++++ sfall/FalloutEngine.h | 15 ++++++++++++- sfall/FalloutStructs.h | 4 ++-- sfall/ScriptExtender.cpp | 13 +++++++++++ sfall/ScriptOps/MetaruleOp.hpp | 32 +++++++++++++++++++++++--- sfall/ScriptOps/ObjectsOps.hpp | 41 +++++++++++++++++++++++++--------- 6 files changed, 111 insertions(+), 17 deletions(-) diff --git a/sfall/FalloutEngine.cpp b/sfall/FalloutEngine.cpp index 98e78e1c..e2b82502 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -725,6 +725,14 @@ void SkillSetTags(int* tags, DWORD num) { } } +int __stdcall ScrPtr(int scriptId, TScript** scriptPtr) { + __asm { + mov eax, scriptId; + mov edx, scriptPtr; + call scr_ptr_; + } +} + // redraws the main game interface windows (useful after changing some data like active hand, etc.) void InterfaceRedraw() { __asm { @@ -800,4 +808,19 @@ const char* __stdcall FindCurrentProc(TProgram* program) { mov eax, program call findCurrentProc_ } +} + +TGameObj* __stdcall InvenWorn(TGameObj* critter) { + __asm mov eax, critter + __asm call inven_worn_ +} + +TGameObj* __stdcall InvenLeftHand(TGameObj* critter) { + __asm mov eax, critter + __asm call inven_left_hand_ +} + +TGameObj* __stdcall InvenRightHand(TGameObj* critter) { + __asm mov eax, critter + __asm call inven_right_hand_ } \ No newline at end of file diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index f442be09..0d073ab9 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -854,6 +854,10 @@ void CritterPcSetName(const char* newName); // Returns the name of the critter const char* __stdcall CritterName(TGameObj* critter); +// Saves pointer to script object into scriptPtr using scriptID. +// Returns 0 on success, -1 on failure. +int __stdcall ScrPtr(int scriptId, TScript** scriptPtr); + void SkillGetTags(int* result, DWORD num); void SkillSetTags(int* tags, DWORD num); @@ -884,4 +888,13 @@ void __declspec() InterpretError(const char* fmt, ...); void __declspec() DebugPrintf(const char* fmt, ...); // returns the name of current procedure by program pointer -const char* __stdcall FindCurrentProc(TProgram* program); \ No newline at end of file +const char* __stdcall FindCurrentProc(TProgram* program); + +// critter worn item (armor) +TGameObj* __stdcall InvenWorn(TGameObj* critter); + +// item in critter's left hand slot +TGameObj* __stdcall InvenLeftHand(TGameObj* critter); + +// item in critter's right hand slot +TGameObj* __stdcall InvenRightHand(TGameObj* critter); \ No newline at end of file diff --git a/sfall/FalloutStructs.h b/sfall/FalloutStructs.h index c76b9b9f..b4cb1b93 100644 --- a/sfall/FalloutStructs.h +++ b/sfall/FalloutStructs.h @@ -34,7 +34,7 @@ struct TInvenRec }; #pragma pack(pop) -/* 8 */ +/* 15 */ #pragma pack(push, 1) struct TGameObj { @@ -52,7 +52,7 @@ struct TGameObj TInvenRec *invenTablePtr; char gap_38[4]; int itemCharges; - int movePoints; + int critterAP_weaponAmmoPid; char gap_44[16]; int lastTarget; char gap_58[12]; diff --git a/sfall/ScriptExtender.cpp b/sfall/ScriptExtender.cpp index 29df15fb..b6b9ca70 100644 --- a/sfall/ScriptExtender.cpp +++ b/sfall/ScriptExtender.cpp @@ -47,6 +47,7 @@ typedef struct { int i; float f; const char* str; + TGameObj* gObj; } val; DWORD type; // TODO: replace with enum class } ScriptValue; @@ -76,6 +77,12 @@ static void _stdcall SetOpReturn(const char* value) { opRet.type = DATATYPE_STR; } +static void _stdcall SetOpReturn(TGameObj* value) { + opRet.val.gObj = value; + opRet.type = DATATYPE_INT; +} + + // TODO: replace these functions with proper ScriptValue class (encapsulation...) static bool _stdcall IsOpArgInt(int num) { return (opArgs[num].type == DATATYPE_INT); @@ -121,6 +128,12 @@ static const char* _stdcall GetOpArgStr(int num) { : ""; } +static TGameObj* _stdcall GetOpArgObj(int num) { + return (opArgs[num].type == DATATYPE_INT) + ? opArgs[num].val.gObj + : nullptr; +} + // writes error message to debug.log along with the name of script & procedure static void _stdcall PrintOpcodeError(const char* fmt, ...) { assert(opProgram != nullptr); diff --git a/sfall/ScriptOps/MetaruleOp.hpp b/sfall/ScriptOps/MetaruleOp.hpp index c2c0adfa..32214df4 100644 --- a/sfall/ScriptOps/MetaruleOp.hpp +++ b/sfall/ScriptOps/MetaruleOp.hpp @@ -48,6 +48,8 @@ struct SfallMetarule { #define DATATYPE_MASK_INT (1 << DATATYPE_INT) #define DATATYPE_MASK_FLOAT (1 << DATATYPE_FLOAT) #define DATATYPE_MASK_STR (1 << DATATYPE_STR) +#define DATATYPE_MASK_NOT_NULL (0x00010000) +#define DATATYPE_MASK_VALID_OBJ (DATATYPE_MASK_INT | DATATYPE_MASK_NOT_NULL) typedef std::tr1::unordered_map MetaruleTableType; @@ -93,10 +95,24 @@ static void sf_get_metarule_table() { SetOpReturn(arr, DATATYPE_INT); } +/* + Metarule array. + + Add your custom scripting functions here. + + Format is as follows: + { name, handler, minArgs, maxArgs, {MASK1, MASK2, ...} } + - name - name of function that will be used to call it from scripts, + - handler - pointer to handler function (see examples above), + - minArgs/maxArgs - minimum and maximum number of arguments allowed for this function, + - MASK1, MASK2, ... - validation parameters for each argument as bit masks (see DATATYPE_MASK_* defines) +*/ static const SfallMetarule metaruleArray[] = { {"test", sf_test, 0, 6, {}}, {"get_metarule_table", sf_get_metarule_table, 0, 0, {}}, - {"validate_test", sf_test, 2, 5, {DATATYPE_MASK_INT, DATATYPE_MASK_INT | DATATYPE_MASK_FLOAT, DATATYPE_MASK_STR, DATATYPE_NONE}} + {"validate_test", sf_test, 2, 5, {DATATYPE_MASK_INT, DATATYPE_MASK_INT | DATATYPE_MASK_FLOAT, DATATYPE_MASK_STR, DATATYPE_NONE}}, + {"spatial_radius", sf_spatial_radius, 1, 1, {DATATYPE_MASK_VALID_OBJ}}, + {"critter_inven_obj2", sf_critter_inven_obj2, 2, 2, {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT}}, }; static void InitMetaruleTable() { @@ -121,12 +137,22 @@ static bool ValidateOpcodeArguments(const SfallMetarule* metaruleInfo) { return false; } else { for (int i = 0; i < argCount; i++) { - if (metaruleInfo->argTypeMasks[i] != 0 && ((1 << opArgs[i + 1].type) & metaruleInfo->argTypeMasks[i]) == 0) { + int typeMask = metaruleInfo->argTypeMasks[i]; + ScriptValue arg = opArgs[i + 1]; + if (typeMask != 0 && ((1 << arg.type) & typeMask) == 0) { PrintOpcodeError( "sfall_funcX(\"%s\", ...) - argument #%d has invalid type: %s.", metaruleInfo->name, i + 1, - GetSfallTypeName(opArgs[i + 1].type)); + GetSfallTypeName(arg.type)); + + return false; + } else if ((typeMask & DATATYPE_MASK_NOT_NULL) && arg.val.dw == 0) { + PrintOpcodeError( + "sfall_funcX(\"%s\", ...) - argument #%d is null.", + metaruleInfo->name, + i + 1); + return false; } } diff --git a/sfall/ScriptOps/ObjectsOps.hpp b/sfall/ScriptOps/ObjectsOps.hpp index cfcb8c01..97e78693 100644 --- a/sfall/ScriptOps/ObjectsOps.hpp +++ b/sfall/ScriptOps/ObjectsOps.hpp @@ -24,6 +24,7 @@ //script control functions +// TODO: rewrite static void __declspec(naked) RemoveScript() { __asm { push ebx; @@ -52,6 +53,7 @@ end: } } +// TODO: rewrite static void __declspec(naked) SetScript() { __asm { pushad; @@ -111,6 +113,7 @@ end: } } +// TODO: rewrite, remove all ASM static void _stdcall op_create_spatial2() { DWORD scriptIndex = GetOpArgInt(0), tile = GetOpArgInt(1), @@ -154,18 +157,13 @@ static void _stdcall op_create_spatial2() { static void __declspec(naked) op_create_spatial() { _WRAP_OPCODE(op_create_spatial2, 4, 1) } -static void __declspec(naked) op_spatial_radius() { - _OP_BEGIN(ebp) - _GET_ARG_R32(ebp, ecx, eax) - _CHECK_ARG_INT(cx, fail) - __asm { - test eax, eax; - js fail; - mov eax, [eax+0xC]; -fail: + +static void sf_spatial_radius() { + TGameObj* spatialObj = GetOpArgObj(1); + TScript* script; + if (ScrPtr(spatialObj->scriptID, &script) != -1) { + SetOpReturn(script->spatial_radius); } - _RET_VAL_INT(ebp) - _OP_END } static void __declspec(naked) GetScript() { @@ -533,3 +531,24 @@ static void _stdcall op_obj_is_carrying_obj2() { static void __declspec(naked) op_obj_is_carrying_obj() { _WRAP_OPCODE(op_obj_is_carrying_obj2, 2, 1) } + +static void sf_critter_inven_obj2() { + TGameObj* critter = GetOpArgObj(1); + int slot = GetOpArgInt(2); + switch (slot) { + case 0: + SetOpReturn(InvenWorn(critter)); + break; + case 1: + SetOpReturn(InvenRightHand(critter)); + break; + case 2: + SetOpReturn(InvenLeftHand(critter)); + break; + case -2: + SetOpReturn(critter->invenCount); + break; + default: + PrintOpcodeError("critter_inven_obj2() - invalid type."); + } +}