mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Refactor OpcodeContext class to make more sense.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user