diff --git a/src/p_acs.cpp b/src/p_acs.cpp index e499f9d01..19f9de486 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -644,7 +644,7 @@ inline int PitchToACS(DAngle ang) struct CallReturn { - CallReturn(int pc, ScriptFunction *func, FBehavior *module, int32_t *locals, ACSLocalArrays *arrays, bool discard, unsigned int runaway) + CallReturn(int pc, ScriptFunction *func, FBehavior *module, const ACSLocalVariables &locals, ACSLocalArrays *arrays, bool discard, unsigned int runaway) : ReturnFunction(func), ReturnModule(module), ReturnLocals(locals), @@ -656,7 +656,7 @@ struct CallReturn ScriptFunction *ReturnFunction; FBehavior *ReturnModule; - int32_t *ReturnLocals; + ACSLocalVariables ReturnLocals; ACSLocalArrays *ReturnArrays; int ReturnAddress; int bDiscardResult; @@ -761,6 +761,35 @@ protected: private: DLevelScript(); + int getbyte(int *&pc) + { + CheckInstructionPointer(pc); + + int res = *(uint8_t *)pc; + pc = (int *)((uint8_t *)pc+1); + return res; + } + + int getshort(int *&pc) + { + CheckInstructionPointer(pc); + + int res = LittleShort( *(int16_t *)pc); + pc = (int *)((uint8_t *)pc+2); + return res; + } + + void CheckInstructionPointer(int *pc) const + { + const uint32_t offset = activeBehavior->PC2Ofs(pc); + const uint32_t size = activeBehavior->GetDataSize(); + + if (offset >= size) + { + I_Error("Out of bounds instruction pointer in ACS VM"); + } + } + friend class DACSThinker; }; @@ -6896,7 +6925,7 @@ enum }; -#define NEXTWORD (LittleLong(*pc++)) +#define NEXTWORD (CheckInstructionPointer(pc), LittleLong(*pc++)) #define NEXTBYTE (fmt==ACS_LittleEnhanced?getbyte(pc):NEXTWORD) #define NEXTSHORT (fmt==ACS_LittleEnhanced?getshort(pc):NEXTWORD) #define STACK(a) (Stack[sp - (a)]) @@ -6904,20 +6933,6 @@ enum // Direct instructions that take strings need to have the tag applied. #define TAGSTR(a) (a|activeBehavior->GetLibraryID()) -inline int getbyte (int *&pc) -{ - int res = *(uint8_t *)pc; - pc = (int *)((uint8_t *)pc+1); - return res; -} - -inline int getshort (int *&pc) -{ - int res = LittleShort( *(int16_t *)pc); - pc = (int *)((uint8_t *)pc+2); - return res; -} - static bool CharArrayParms(int &capacity, int &offset, int &a, FACSStackMemory& Stack, int &sp, bool ranged) { if (ranged) @@ -6967,7 +6982,7 @@ static void SetMarineSprite(AActor *marine, PClassActor *source) int DLevelScript::RunScript () { DACSThinker *controller = DACSThinker::ActiveThinker; - int32_t *locals = &Localvars[0]; + ACSLocalVariables locals(Localvars); ACSLocalArrays noarrays; ACSLocalArrays *localarrays = &noarrays; ScriptFunction *activeFunction = NULL; @@ -7335,7 +7350,6 @@ int DLevelScript::RunScript () int i; ScriptFunction *func; FBehavior *module; - int32_t *mylocals; if(pcd == PCD_CALLSTACK) { @@ -7364,9 +7378,9 @@ int DLevelScript::RunScript () state = SCRIPT_PleaseRemove; break; } - mylocals = locals; + const ACSLocalVariables mylocals = locals; // The function's first argument is also its first local variable. - locals = &Stack[sp - func->ArgCount]; + locals.Reset(&Stack[sp - func->ArgCount], func->ArgCount + func->LocalCount); // Make space on the stack for any other variables the function uses. for (i = 0; i < func->LocalCount; ++i) { @@ -7405,7 +7419,7 @@ int DLevelScript::RunScript () sp -= sizeof(CallReturn)/sizeof(int); retsp = &Stack[sp]; activeBehavior->GetFunctionProfileData(activeFunction)->AddRun(runaway - ret->EntryInstrCount); - sp = int(locals - &Stack[0]); + sp = int(locals.GetPointer() - &Stack[0]); pc = ret->ReturnModule->Ofs2PC(ret->ReturnAddress); activeFunction = ret->ReturnFunction; activeBehavior = ret->ReturnModule; diff --git a/src/p_acs.h b/src/p_acs.h index 178a6ecce..4f53ce1a3 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -147,6 +147,44 @@ struct ProfileCollector int Index; }; +class ACSLocalVariables +{ +public: + ACSLocalVariables(TArray &variables) + : memory(&variables[0]) + , count(variables.Size()) + { + } + + void Reset(int32_t *const memory, const size_t count) + { + // TODO: pointer sanity check? + // TODO: constraints on count? + + this->memory = memory; + this->count = count; + } + + int32_t& operator[](const size_t index) + { + if (index >= count) + { + I_Error("Out of bounds access to local variables in ACS VM"); + } + + return memory[index]; + } + + const int32_t *GetPointer() const + { + return memory; + } + +private: + int32_t *memory; + size_t count; +}; + struct ACSLocalArrayInfo { unsigned int Size; @@ -173,7 +211,7 @@ struct ACSLocalArrays } // Bounds-checking Set and Get for local arrays - void Set(int *locals, int arraynum, int arrayentry, int value) + void Set(ACSLocalVariables &locals, int arraynum, int arrayentry, int value) { if ((unsigned int)arraynum < Count && (unsigned int)arrayentry < Info[arraynum].Size) @@ -181,7 +219,7 @@ struct ACSLocalArrays locals[Info[arraynum].Offset + arrayentry] = value; } } - int Get(int *locals, int arraynum, int arrayentry) + int Get(ACSLocalVariables &locals, int arraynum, int arrayentry) { if ((unsigned int)arraynum < Count && (unsigned int)arrayentry < Info[arraynum].Size) diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index a715f6a49..5d3a872a4 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -1044,7 +1044,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply player->SetLogNumber(reply->LogNumber); } - if (replyText != NULL && isconsole) + if (nullptr != replyText && '\0' != replyText[0] && isconsole) { TerminalResponse(replyText); } diff --git a/wadsrc/static/zscript/shared/randomspawner.txt b/wadsrc/static/zscript/shared/randomspawner.txt index 7488fdd4d..9914670b4 100644 --- a/wadsrc/static/zscript/shared/randomspawner.txt +++ b/wadsrc/static/zscript/shared/randomspawner.txt @@ -168,6 +168,7 @@ class RandomSpawner : Actor newmobj.SpawnFlags = SpawnFlags & ~MTF_SECRET; // MTF_SECRET needs special treatment to avoid incrementing the secret counter twice. It had already been processed for the spawner itself. newmobj.HandleSpawnFlags(); newmobj.SpawnFlags = SpawnFlags; + newmobj.bCountSecret = SpawnFlags & MTF_SECRET; // "Transfer" count secret flag to spawned actor newmobj.ChangeTid(tid); newmobj.Vel = Vel; newmobj.master = master; // For things such as DamageMaster/DamageChildren, transfer mastery.