Refactor OpcodeContext class to make more sense.

This commit is contained in:
phobos2077
2017-02-21 00:26:42 +07:00
parent c26c2ad8f6
commit 0026590058
5 changed files with 53 additions and 69 deletions
@@ -149,14 +149,6 @@ __asm resultnotstr##num: \
func - C-function to call (should not have arguments)
argnum - number of opcode arguments
isExpression - 1 if opcode has return value, 0 otherwise
TODO: get rid of this, there is even better way:
- single ASM handler for all new opcodes
- it will check opcode ID against OpcodeMetaArray: validate arguments if necessary, call sf_* handler
Benefits:
- each opcode will be defined in single place along with validation info and other data
- no need for ASM handlers for each opcode (op_* functions)
*/
#define _WRAP_OPCODE(func, argnum, isExpression) __asm { \
__asm pushad \
@@ -136,7 +136,7 @@ static bool ValidateMetaruleArguments(OpcodeContext& ctx, const SfallMetarule* m
return false;
} else {
return ctx.validateArguments(metaruleInfo->func, argCount);
return ctx.validateArguments(metaruleInfo->func);
}
}
+31 -39
View File
@@ -24,15 +24,24 @@
#include "OpcodeContext.h"
OpcodeContext OpcodeContext::_defaultInstance;
OpcodeMetaTableType OpcodeContext::_opcodeMetaTable;
OpcodeContext::OpcodeContext() {
OpcodeContext::OpcodeContext(TProgram* program, int argNum, bool hasReturn) {
assert(argNum < OP_MAX_ARGUMENTS);
_program = program;
_numArgs = argNum;
_hasReturn = hasReturn;
_argShift = 0;
_args.reserve (OP_MAX_ARGUMENTS);
}
int OpcodeContext::numArgs() const {
return _args.size() - _argShift;
return _numArgs - _argShift;
}
bool OpcodeContext::hasReturn() const {
return _hasReturn;
}
int OpcodeContext::argShift() const {
@@ -65,17 +74,6 @@ void OpcodeContext::setReturn(const ScriptValue& val) {
_ret = val;
}
void OpcodeContext::resetState(TProgram* program, int argNum) {
_program = program;
// reset return value
_ret = ScriptValue();
// reset argument list
_args.resize(argNum);
// reset arg shift
_argShift = 0;
}
void OpcodeContext::printOpcodeError(const char* fmt, ...) const {
assert(_program != nullptr);
@@ -89,19 +87,19 @@ void OpcodeContext::printOpcodeError(const char* fmt, ...) const {
Wrapper::debug_printf("\nOPCODE ERROR: %s\n Current script: %s, procedure %s.", msg, _program->fileName, procName);
}
bool OpcodeContext::validateArguments(ScriptingFunctionHandler func, int argNum) const {
bool OpcodeContext::validateArguments(ScriptingFunctionHandler func) const {
OpcodeMetaTableType::const_iterator it = _opcodeMetaTable.find(func);
if (it != _opcodeMetaTable.end()) {
const SfallOpcodeMetadata* meta = it->second;
// automatically validate argument types
return validateArguments(meta->argTypeMasks, argNum, meta->name);
return validateArguments(meta->argTypeMasks, meta->name);
}
return true;
}
bool OpcodeContext::validateArguments(const int argTypeMasks[], int argCount, const char* opcodeName) const {
for (int i = 0; i < argCount; i++) {
bool OpcodeContext::validateArguments(const int argTypeMasks[], const char* opcodeName) const {
for (int i = 0; i < _numArgs; i++) {
int typeMask = argTypeMasks[i];
const ScriptValue &argI = arg(i);
if (typeMask != 0 && ((1 << argI.type()) & typeMask) == 0) {
@@ -124,53 +122,47 @@ bool OpcodeContext::validateArguments(const int argTypeMasks[], int argCount, co
return true;
}
void OpcodeContext::handleOpcode(TProgram* program, ScriptingFunctionHandler func, int argNum, bool hasReturn) {
assert(argNum < OP_MAX_ARGUMENTS);
// reset state after previous
resetState(program, argNum);
void OpcodeContext::handleOpcode(ScriptingFunctionHandler func) {
// process arguments on stack (reverse order)
for (int i = argNum - 1; i >= 0; i--) {
for (int i = _numArgs - 1; i >= 0; i--) {
// get argument from stack
DWORD rawValueType = Wrapper::interpretPopShort(program);
DWORD rawValue = Wrapper::interpretPopLong(program);
DWORD rawValueType = Wrapper::interpretPopShort(_program);
DWORD rawValue = Wrapper::interpretPopLong(_program);
SfallDataType type = static_cast<SfallDataType>(getSfallTypeByScriptType(rawValueType));
// retrieve string argument
if (type == DATATYPE_STR) {
_args.at(i) = Wrapper::interpretGetString(program, rawValueType, rawValue);
_args.at(i) = Wrapper::interpretGetString(_program, rawValueType, rawValue);
} else {
_args.at(i) = ScriptValue(type, rawValue);
}
}
// call opcode handler if arguments are valid (or no automatic validation was done)
if (validateArguments(func, argNum)) {
if (validateArguments(func)) {
func(*this);
}
// process return value
if (hasReturn) {
if (_hasReturn) {
if (_ret.type() == DATATYPE_NONE) {
// if no value was set in handler, force return 0 to avoid stack error
_ret = ScriptValue(0);
}
DWORD rawResult = _ret.rawValue();
if (_ret.type() == DATATYPE_STR) {
rawResult = Wrapper::interpretAddString(program, _ret.asString());
rawResult = Wrapper::interpretAddString(_program, _ret.asString());
}
Wrapper::interpretPushLong(program, rawResult);
Wrapper::interpretPushShort(program, getScriptTypeBySfallType(_ret.type()));
Wrapper::interpretPushLong(_program, rawResult);
Wrapper::interpretPushShort(_program, getScriptTypeBySfallType(_ret.type()));
}
}
OpcodeContext& OpcodeContext::defaultInstance() {
return _defaultInstance;
}
void __stdcall OpcodeContext::handleOpcodeStatic(TProgram* program, ScriptingFunctionHandler func, int argNum, bool hasReturn) {
defaultInstance().handleOpcode(program, func, argNum, hasReturn);
// for each opcode create new context on stack (no allocations at this point)
OpcodeContext currentContext(program, argNum, hasReturn);
// handle the opcode using provided handler
currentContext.handleOpcode(func);
}
void OpcodeContext::addOpcodeMetaData(const SfallOpcodeMetadata* data) {
+17 -17
View File
@@ -18,8 +18,8 @@
#pragma once
#include <array>
#include <unordered_map>
#include <vector>
#include "..\..\FalloutEngine\Structs.h"
#include "ScriptValue.h"
@@ -51,15 +51,20 @@ struct SfallOpcodeMetadata {
typedef std::tr1::unordered_map<ScriptingFunctionHandler, const SfallOpcodeMetadata*> OpcodeMetaTableType;
// A context for handling opcodes. Opcode handlers can retrieve arguments and set opcode return value via context.
class OpcodeContext {
public:
OpcodeContext();
void addOpcodeMetaData(const SfallOpcodeMetadata* data);
// program - pointer to script program (from the engine)
// argNum - number of arguments for this opcode
// hasReturn - true if opcode has return value (is expression)
OpcodeContext(TProgram* program, int argNum, bool hasReturn);
// number of arguments, possibly reduced by argShift
int numArgs() const;
// true if the current opcode is supposed to return some value
bool hasReturn() const;
// returns current argument shift, default is 0
int argShift() const;
@@ -81,28 +86,22 @@ public:
// set return value for current opcode
void setReturn(const ScriptValue& val);
// resets the state of handler for new opcode invocation
void resetState(TProgram* program, int argNum);
// writes error message to debug.log along with the name of script & procedure
void printOpcodeError(const char* fmt, ...) const;
// Validate opcode arguments against type masks
// if validation pass, returns true, otherwise writes error to debug.log and returns false
bool validateArguments(const int argTypeMasks[], int argCount, const char* opcodeName) const;
bool validateArguments(const int argTypeMasks[], const char* opcodeName) const;
// validate opcode arguments given opcode handler function and actual number of arguments
bool validateArguments(ScriptingFunctionHandler func, int argNum) const;
bool validateArguments(ScriptingFunctionHandler func) const;
// Handle opcodes
// program - pointer to script program (from the engine)
// func - opcode handler
// argNum - number of arguments
// hasReturn - true if opcode has return value (is expression)
void handleOpcode(TProgram* program, ScriptingFunctionHandler func, int argNum, bool hasReturn);
void handleOpcode(ScriptingFunctionHandler func);
static OpcodeContext& defaultInstance();
static void addOpcodeMetaData(const SfallOpcodeMetadata* data);
// handles opcode using default instance
static void __stdcall handleOpcodeStatic(TProgram* program, ScriptingFunctionHandler func, int argNum, bool hasReturn);
@@ -116,10 +115,11 @@ public:
private:
TProgram* _program;
int _numArgs;
bool _hasReturn;
int _argShift;
std::vector<ScriptValue> _args;
std::array<ScriptValue, OP_MAX_ARGUMENTS> _args;
ScriptValue _ret;
OpcodeMetaTableType _opcodeMetaTable;
static OpcodeContext _defaultInstance;
static OpcodeMetaTableType _opcodeMetaTable;
};
+4 -4
View File
@@ -18,7 +18,6 @@
#include "..\KillCounter.h"
#include "Handlers\AsmMacros.h"
#include "Handlers\Anims.h"
#include "Handlers\Arrays.h"
@@ -34,10 +33,10 @@
#include "Handlers\Utils.h"
#include "Handlers\Worldmap.h"
#include "Handlers\Metarule.h"
#include "OpcodeContext.h"
#include "Opcodes.h"
static void* opcodes[0x300];
/*
@@ -59,9 +58,8 @@ static const SfallOpcodeMetadata opcodeMetaArray[] = {
void InitOpcodeMetaTable() {
int length = sizeof(opcodeMetaArray) / sizeof(SfallOpcodeMetadata);
OpcodeContext& opHandler = OpcodeContext::defaultInstance();
for (int i = 0; i < length; ++i) {
opHandler.addOpcodeMetaData(&opcodeMetaArray[i]);
OpcodeContext::addOpcodeMetaData(&opcodeMetaArray[i]);
}
}
@@ -367,6 +365,8 @@ void InitNewOpcodes() {
opcodes[0x27a] = op_sfall_metarule4;
opcodes[0x27b] = op_sfall_metarule5;
opcodes[0x27c] = op_sfall_metarule6; // if you need more arguments - use arrays
// see opcodeMetaArray above for additional scripting functions via "metarule"
InitOpcodeMetaTable();
}