Initial commit
This commit is contained in:
598
engines/mediastation/mediascript/codechunk.cpp
Normal file
598
engines/mediastation/mediascript/codechunk.cpp
Normal file
@@ -0,0 +1,598 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/ptr.h"
|
||||
|
||||
#include "mediastation/mediastation.h"
|
||||
#include "mediastation/mediascript/codechunk.h"
|
||||
#include "mediastation/mediascript/collection.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
CodeChunk::CodeChunk(Chunk &chunk) {
|
||||
uint lengthInBytes = chunk.readTypedUint32();
|
||||
debugC(5, kDebugLoading, "CodeChunk::CodeChunk(): Length 0x%x (@0x%llx)", lengthInBytes, static_cast<long long int>(chunk.pos()));
|
||||
_bytecode = static_cast<ParameterReadStream *>(chunk.readStream(lengthInBytes));
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::executeNextBlock() {
|
||||
uint blockSize = _bytecode->readTypedUint32();
|
||||
int64 startingPos = _bytecode->pos();
|
||||
debugC(7, kDebugScript, "%s: Entering new block (blockSize: %d, startingPos: %lld)",
|
||||
__func__, blockSize, static_cast<long long int>(startingPos));
|
||||
|
||||
ScriptValue returnValue;
|
||||
ExpressionType expressionType = static_cast<ExpressionType>(_bytecode->readTypedUint16());
|
||||
while (expressionType != kExpressionTypeEmpty && !_returnImmediately) {
|
||||
returnValue = evaluateExpression(expressionType);
|
||||
expressionType = static_cast<ExpressionType>(_bytecode->readTypedUint16());
|
||||
|
||||
if (expressionType == kExpressionTypeEmpty) {
|
||||
debugC(7, kDebugScript, "%s: Done executing block due to end of chunk", __func__);
|
||||
}
|
||||
if (_returnImmediately) {
|
||||
debugC(7, kDebugScript, "%s: Done executing block due to script requesting immediate return", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify we consumed the right number of script bytes.
|
||||
if (!_returnImmediately) {
|
||||
uint bytesRead = _bytecode->pos() - startingPos;
|
||||
if (bytesRead != blockSize) {
|
||||
error("%s: Expected to have read %d script bytes, actually read %d", __func__, blockSize, bytesRead);
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void CodeChunk::skipNextBlock() {
|
||||
uint lengthInBytes = _bytecode->readTypedUint32();
|
||||
_bytecode->skip(lengthInBytes);
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::execute(Common::Array<ScriptValue> *args) {
|
||||
_args = args;
|
||||
ScriptValue returnValue = executeNextBlock();
|
||||
|
||||
// Rewind the stream once we're finished, in case we need to execute
|
||||
// this code again!
|
||||
_bytecode->seek(0);
|
||||
_returnImmediately = false;
|
||||
_locals.clear();
|
||||
// We don't own the args, so we will prevent a potentially out-of-scope
|
||||
// variable from being re-accessed.
|
||||
_args = nullptr;
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateExpression() {
|
||||
ExpressionType expressionType = static_cast<ExpressionType>(_bytecode->readTypedUint16());
|
||||
ScriptValue returnValue = evaluateExpression(expressionType);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateExpression(ExpressionType expressionType) {
|
||||
debugCN(5, kDebugScript, "(%s) ", expressionTypeToStr(expressionType));
|
||||
|
||||
ScriptValue returnValue;
|
||||
switch (expressionType) {
|
||||
case kExpressionTypeEmpty:
|
||||
break;
|
||||
|
||||
case kExpressionTypeOperation:
|
||||
returnValue = evaluateOperation();
|
||||
break;
|
||||
|
||||
case kExpressionTypeValue:
|
||||
returnValue = evaluateValue();
|
||||
break;
|
||||
|
||||
case kExpressionTypeVariable:
|
||||
returnValue = evaluateVariable();
|
||||
break;
|
||||
|
||||
default:
|
||||
error("%s: Got unimplemented expression type %s (%d)", __func__,
|
||||
expressionTypeToStr(expressionType), static_cast<uint>(expressionType));
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateOperation() {
|
||||
Opcode opcode = static_cast<Opcode>(_bytecode->readTypedUint16());
|
||||
debugCN(5, kDebugScript, "%s ", opcodeToStr(opcode));
|
||||
|
||||
ScriptValue returnValue;
|
||||
switch (opcode) {
|
||||
case kOpcodeIf:
|
||||
evaluateIf();
|
||||
break;
|
||||
|
||||
case kOpcodeIfElse:
|
||||
evaluateIfElse();
|
||||
break;
|
||||
|
||||
case kOpcodeAssignVariable:
|
||||
evaluateAssign();
|
||||
break;
|
||||
|
||||
case kOpcodeOr:
|
||||
case kOpcodeXor:
|
||||
case kOpcodeAnd:
|
||||
case kOpcodeEquals:
|
||||
case kOpcodeNotEquals:
|
||||
case kOpcodeLessThan:
|
||||
case kOpcodeGreaterThan:
|
||||
case kOpcodeLessThanOrEqualTo:
|
||||
case kOpcodeGreaterThanOrEqualTo:
|
||||
case kOpcodeAdd:
|
||||
case kOpcodeSubtract:
|
||||
case kOpcodeMultiply:
|
||||
case kOpcodeDivide:
|
||||
case kOpcodeModulo:
|
||||
returnValue = evaluateBinaryOperation(opcode);
|
||||
break;
|
||||
|
||||
case kOpcodeNegate:
|
||||
returnValue = evaluateUnaryOperation();
|
||||
break;
|
||||
|
||||
case kOpcodeCallFunction:
|
||||
returnValue = evaluateFunctionCall();
|
||||
break;
|
||||
|
||||
case kOpcodeCallMethod:
|
||||
returnValue = evaluateMethodCall();
|
||||
break;
|
||||
|
||||
case kOpcodeDeclareLocals:
|
||||
evaluateDeclareLocals();
|
||||
break;
|
||||
|
||||
case kOpcodeReturn:
|
||||
returnValue = evaluateReturn();
|
||||
break;
|
||||
|
||||
case kOpcodeReturnNoValue:
|
||||
evaluateReturnNoValue();
|
||||
break;
|
||||
|
||||
case kOpcodeWhile:
|
||||
evaluateWhileLoop();
|
||||
break;
|
||||
|
||||
case kOpcodeCallFunctionInVariable:
|
||||
returnValue = evaluateFunctionCall(true);
|
||||
break;
|
||||
|
||||
case kOpcodeCallMethodInVariable:
|
||||
returnValue = evaluateMethodCall(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
error("%s: Got unimplemented opcode %s (%d)", __func__, opcodeToStr(opcode), static_cast<uint>(opcode));
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateValue() {
|
||||
OperandType operandType = static_cast<OperandType>(_bytecode->readTypedUint16());
|
||||
debugCN(5, kDebugScript, "%s ", operandTypeToStr(operandType));
|
||||
|
||||
ScriptValue returnValue;
|
||||
switch (operandType) {
|
||||
case kOperandTypeBool: {
|
||||
int b = _bytecode->readTypedByte();
|
||||
if (b != 0 && b != 1) {
|
||||
error("%s: Got invalid literal bool value %d", __func__, b);
|
||||
}
|
||||
debugC(5, kDebugScript, "%d ", b);
|
||||
returnValue.setToBool(b == 1 ? true : false);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kOperandTypeFloat: {
|
||||
double f = _bytecode->readTypedDouble();
|
||||
debugC(5, kDebugScript, "%f ", f);
|
||||
returnValue.setToFloat(f);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kOperandTypeInt: {
|
||||
int i = _bytecode->readTypedSint32();
|
||||
debugC(5, kDebugScript, "%d ", i);
|
||||
// Ints are stored internally as doubles.
|
||||
returnValue.setToFloat(static_cast<double>(i));
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kOperandTypeString: {
|
||||
// This is indeed a raw string, not a string wrapped in a datum!
|
||||
uint size = _bytecode->readTypedUint16();
|
||||
Common::String string = _bytecode->readString('\0', size);
|
||||
debugC(5, kDebugScript, "%s ", string.c_str());
|
||||
returnValue.setToString(string);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kOperandTypeParamToken: {
|
||||
uint literal = _bytecode->readTypedUint16();
|
||||
debugC(5, kDebugScript, "%d ", literal);
|
||||
returnValue.setToParamToken(literal);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kOperandTypeActorId: {
|
||||
uint actorId = _bytecode->readTypedUint16();
|
||||
debugC(5, kDebugScript, "%d ", actorId);
|
||||
returnValue.setToActorId(actorId);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kOperandTypeTime: {
|
||||
double d = _bytecode->readTypedTime();
|
||||
debugC(5, kDebugScript, "%f ", d);
|
||||
returnValue.setToTime(d);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kOperandTypeVariable: {
|
||||
returnValue = ScriptValue(_bytecode);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kOperandTypeFunctionId: {
|
||||
uint functionId = _bytecode->readTypedUint16();
|
||||
debugC(5, kDebugScript, "%d ", functionId);
|
||||
returnValue.setToFunctionId(functionId);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
case kOperandTypeMethodId: {
|
||||
BuiltInMethod methodId = static_cast<BuiltInMethod>(_bytecode->readTypedUint16());
|
||||
debugC(5, kDebugScript, "%s ", builtInMethodToStr(methodId));
|
||||
returnValue.setToMethodId(methodId);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
default:
|
||||
error("%s: Got unknown ScriptValue type %s (%d)", __func__, operandTypeToStr(operandType), static_cast<uint>(operandType));
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateVariable() {
|
||||
ScriptValue *variable = readAndReturnVariable();
|
||||
return *variable;
|
||||
}
|
||||
|
||||
ScriptValue *CodeChunk::readAndReturnVariable() {
|
||||
uint id = _bytecode->readTypedUint16();
|
||||
VariableScope scope = static_cast<VariableScope>(_bytecode->readTypedUint16());
|
||||
debugC(5, kDebugScript, "%d (%s)", id, variableScopeToStr(scope));
|
||||
|
||||
ScriptValue returnValue;
|
||||
switch (scope) {
|
||||
case kVariableScopeGlobal: {
|
||||
ScriptValue *variable = g_engine->getVariable(id);
|
||||
if (variable == nullptr) {
|
||||
error("%s: Global variable %d doesn't exist", __func__, id);
|
||||
}
|
||||
return variable;
|
||||
}
|
||||
|
||||
case kVariableScopeLocal: {
|
||||
uint index = id - 1;
|
||||
return &_locals.operator[](index);
|
||||
}
|
||||
|
||||
case kVariableScopeIndirectParameter: {
|
||||
ScriptValue indexValue = evaluateExpression();
|
||||
uint index = static_cast<uint>(indexValue.asFloat() + id);
|
||||
return &_args->operator[](index);
|
||||
}
|
||||
|
||||
case kVariableScopeParameter: {
|
||||
uint index = id - 1;
|
||||
if (_args == nullptr) {
|
||||
error("%s: Requested a parameter in a code chunk that has no parameters", __func__);
|
||||
}
|
||||
return &_args->operator[](index);
|
||||
}
|
||||
|
||||
default:
|
||||
error("%s: Got unknown variable scope %s (%d)", __func__, variableScopeToStr(scope), static_cast<uint>(scope));
|
||||
}
|
||||
}
|
||||
|
||||
void CodeChunk::evaluateIf() {
|
||||
debugCN(5, kDebugScript, "\n condition: ");
|
||||
ScriptValue condition = evaluateExpression();
|
||||
if (condition.getType() != kScriptValueTypeBool) {
|
||||
error("%s: Expected bool condition, got %s", __func__, scriptValueTypeToStr(condition.getType()));
|
||||
}
|
||||
|
||||
if (condition.asBool()) {
|
||||
executeNextBlock();
|
||||
} else {
|
||||
skipNextBlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CodeChunk::evaluateIfElse() {
|
||||
debugCN(5, kDebugScript, "\n condition: ");
|
||||
ScriptValue condition = evaluateExpression();
|
||||
if (condition.getType() != kScriptValueTypeBool) {
|
||||
error("%s: Expected bool condition, got %s", __func__, scriptValueTypeToStr(condition.getType()));
|
||||
}
|
||||
|
||||
if (condition.asBool()) {
|
||||
executeNextBlock();
|
||||
skipNextBlock();
|
||||
} else {
|
||||
skipNextBlock();
|
||||
executeNextBlock();
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateAssign() {
|
||||
debugCN(5, kDebugScript, "Variable ");
|
||||
ScriptValue *targetVariable = readAndReturnVariable();
|
||||
|
||||
debugC(5, kDebugScript, " Value: ");
|
||||
ScriptValue value = evaluateExpression();
|
||||
|
||||
if (value.getType() == kScriptValueTypeEmpty) {
|
||||
error("%s: Attempt to assign an empty value to a variable", __func__);
|
||||
}
|
||||
|
||||
if (targetVariable != nullptr) {
|
||||
*targetVariable = value;
|
||||
return value;
|
||||
} else {
|
||||
error("%s: Attempt to assign to null variable", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateBinaryOperation(Opcode op) {
|
||||
debugCN(5, kDebugScript, "\n lhs: ");
|
||||
ScriptValue value1 = evaluateExpression();
|
||||
debugCN(5, kDebugScript, " rhs: ");
|
||||
ScriptValue value2 = evaluateExpression();
|
||||
|
||||
ScriptValue returnValue;
|
||||
switch (op) {
|
||||
case kOpcodeOr:
|
||||
returnValue.setToBool(value1 || value2);
|
||||
break;
|
||||
|
||||
case kOpcodeXor:
|
||||
returnValue.setToBool(value1 ^ value2);
|
||||
break;
|
||||
|
||||
case kOpcodeAnd:
|
||||
returnValue.setToBool(value1 && value2);
|
||||
break;
|
||||
|
||||
case kOpcodeEquals:
|
||||
returnValue.setToBool(value1 == value2);
|
||||
break;
|
||||
|
||||
case kOpcodeNotEquals:
|
||||
returnValue.setToBool(value1 != value2);
|
||||
break;
|
||||
|
||||
case kOpcodeLessThan:
|
||||
returnValue.setToBool(value1 < value2);
|
||||
break;
|
||||
|
||||
case kOpcodeGreaterThan:
|
||||
returnValue.setToBool(value1 > value2);
|
||||
break;
|
||||
|
||||
case kOpcodeLessThanOrEqualTo:
|
||||
returnValue.setToBool(value1 <= value2);
|
||||
break;
|
||||
|
||||
case kOpcodeGreaterThanOrEqualTo:
|
||||
returnValue.setToBool(value1 >= value2);
|
||||
break;
|
||||
|
||||
case kOpcodeAdd:
|
||||
returnValue = value1 + value2;
|
||||
break;
|
||||
|
||||
case kOpcodeSubtract:
|
||||
returnValue = value1 - value2;
|
||||
break;
|
||||
|
||||
case kOpcodeMultiply:
|
||||
returnValue = value1 * value2;
|
||||
break;
|
||||
|
||||
case kOpcodeDivide:
|
||||
returnValue = value1 / value2;
|
||||
break;
|
||||
|
||||
case kOpcodeModulo:
|
||||
returnValue = value1 % value2;
|
||||
break;
|
||||
|
||||
default:
|
||||
error("%s: Got unknown binary operation opcode %s", __func__, opcodeToStr(op));
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateUnaryOperation() {
|
||||
// The only supported unary operation seems to be negation.
|
||||
ScriptValue value = evaluateExpression();
|
||||
debugCN(5, kDebugScript, " value: ");
|
||||
return -value;
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateFunctionCall(bool isIndirect) {
|
||||
uint functionId, paramCount = 0;
|
||||
if (isIndirect) {
|
||||
paramCount = _bytecode->readTypedUint16();
|
||||
ScriptValue value = evaluateExpression();
|
||||
functionId = value.asFunctionId();
|
||||
} else {
|
||||
functionId = _bytecode->readTypedUint16();
|
||||
paramCount = _bytecode->readTypedUint16();
|
||||
}
|
||||
|
||||
return evaluateFunctionCall(functionId, paramCount);
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateFunctionCall(uint functionId, uint paramCount) {
|
||||
debugC(5, kDebugScript, "%d (%d params)", functionId, paramCount);
|
||||
|
||||
Common::Array<ScriptValue> args;
|
||||
for (uint i = 0; i < paramCount; i++) {
|
||||
debugCN(5, kDebugScript, " Param %d: ", i);
|
||||
ScriptValue arg = evaluateExpression();
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
||||
ScriptValue returnValue = g_engine->getFunctionManager()->call(functionId, args);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateMethodCall(bool isIndirect) {
|
||||
BuiltInMethod method;
|
||||
uint paramCount = 0;
|
||||
if (isIndirect) {
|
||||
paramCount = _bytecode->readTypedUint16();
|
||||
ScriptValue value = evaluateExpression();
|
||||
method = value.asMethodId();
|
||||
} else {
|
||||
method = static_cast<BuiltInMethod>(_bytecode->readTypedUint16());
|
||||
paramCount = _bytecode->readTypedUint16();
|
||||
}
|
||||
|
||||
return evaluateMethodCall(method, paramCount);
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount) {
|
||||
// In Media Station, all methods are built-in - there aren't
|
||||
// custom objects or methods individual titles can
|
||||
// define. Functions, however, CAN be title-defined.
|
||||
// But here, we're only looking for built-in methods.
|
||||
debugC(5, kDebugScript, "%s (%d params)", builtInMethodToStr(method), paramCount);
|
||||
debugCN(5, kDebugScript, " Self: ");
|
||||
|
||||
ScriptValue target = evaluateExpression();
|
||||
Common::Array<ScriptValue> args;
|
||||
for (uint i = 0; i < paramCount; i++) {
|
||||
debugCN(5, kDebugScript, " Param %d: ", i);
|
||||
ScriptValue arg = evaluateExpression();
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
||||
ScriptValue returnValue;
|
||||
switch (target.getType()) {
|
||||
case kScriptValueTypeActorId: {
|
||||
if (target.asActorId() == 0) {
|
||||
// It seems to be valid to call a method on a null actor ID, in
|
||||
// which case nothing happens. Still issue warning for traceability.
|
||||
warning("%s: Attempt to call method on a null actor ID", __func__);
|
||||
return returnValue;
|
||||
} else {
|
||||
// This is a regular actor that we can process directly.
|
||||
uint actorId = target.asActorId();
|
||||
Actor *targetActor = g_engine->getActorById(actorId);
|
||||
if (targetActor == nullptr) {
|
||||
error("%s: Attempt to call method on actor ID %d, which isn't loaded", __func__, target.asActorId());
|
||||
}
|
||||
returnValue = targetActor->callMethod(method, args);
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
case kScriptValueTypeCollection: {
|
||||
Common::SharedPtr<Collection> collection = target.asCollection();
|
||||
returnValue = collection->callMethod(method, args);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
default:
|
||||
error("Attempt to call method on unimplemented value type %s (%d)",
|
||||
scriptValueTypeToStr(target.getType()), static_cast<uint>(target.getType()));
|
||||
}
|
||||
}
|
||||
|
||||
void CodeChunk::evaluateDeclareLocals() {
|
||||
uint localVariableCount = _bytecode->readTypedUint16();
|
||||
if (localVariableCount <= 0) {
|
||||
error("Got non-positive local variable count");
|
||||
}
|
||||
debugC(5, kDebugScript, "%d", localVariableCount);
|
||||
_locals = Common::Array<ScriptValue>(localVariableCount);
|
||||
}
|
||||
|
||||
ScriptValue CodeChunk::evaluateReturn() {
|
||||
ScriptValue returnValue = evaluateExpression();
|
||||
_returnImmediately = true;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void CodeChunk::evaluateReturnNoValue() {
|
||||
_returnImmediately = true;
|
||||
}
|
||||
|
||||
void CodeChunk::evaluateWhileLoop() {
|
||||
uint loopStartPosition = _bytecode->pos();
|
||||
uint iterationCount = 0;
|
||||
|
||||
while (true) {
|
||||
// Seek to the top of the loop bytecode.
|
||||
_bytecode->seek(loopStartPosition);
|
||||
ScriptValue condition = evaluateExpression();
|
||||
if (condition.getType() != kScriptValueTypeBool) {
|
||||
error("Expected loop condition to be bool, not %s", scriptValueTypeToStr(condition.getType()));
|
||||
}
|
||||
|
||||
if (++iterationCount >= MAX_LOOP_ITERATION_COUNT) {
|
||||
error("Exceeded max loop iteration count");
|
||||
}
|
||||
|
||||
if (condition.asBool()) {
|
||||
executeNextBlock();
|
||||
} else {
|
||||
skipNextBlock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CodeChunk::~CodeChunk() {
|
||||
_locals.clear();
|
||||
|
||||
// We don't own the args, so we don't need to delete it.
|
||||
_args = nullptr;
|
||||
|
||||
delete _bytecode;
|
||||
_bytecode = nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
75
engines/mediastation/mediascript/codechunk.h
Normal file
75
engines/mediastation/mediascript/codechunk.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_MEDIASCRIPT_CODECHUNK_H
|
||||
#define MEDIASTATION_MEDIASCRIPT_CODECHUNK_H
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
#include "mediastation/datafile.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class CodeChunk {
|
||||
public:
|
||||
CodeChunk(Chunk &chunk);
|
||||
~CodeChunk();
|
||||
|
||||
ScriptValue executeNextBlock();
|
||||
ScriptValue execute(Common::Array<ScriptValue> *args = nullptr);
|
||||
|
||||
private:
|
||||
void skipNextBlock();
|
||||
|
||||
ScriptValue evaluateExpression();
|
||||
ScriptValue evaluateExpression(ExpressionType expressionType);
|
||||
ScriptValue evaluateOperation();
|
||||
ScriptValue evaluateValue();
|
||||
ScriptValue evaluateVariable();
|
||||
|
||||
ScriptValue *readAndReturnVariable();
|
||||
|
||||
void evaluateIf();
|
||||
void evaluateIfElse();
|
||||
ScriptValue evaluateAssign();
|
||||
ScriptValue evaluateBinaryOperation(Opcode op);
|
||||
ScriptValue evaluateUnaryOperation();
|
||||
ScriptValue evaluateFunctionCall(bool isIndirect = false);
|
||||
ScriptValue evaluateFunctionCall(uint functionId, uint paramCount);
|
||||
ScriptValue evaluateMethodCall(bool isIndirect = false);
|
||||
ScriptValue evaluateMethodCall(BuiltInMethod method, uint paramCount);
|
||||
void evaluateDeclareLocals();
|
||||
ScriptValue evaluateReturn();
|
||||
void evaluateReturnNoValue();
|
||||
void evaluateWhileLoop();
|
||||
|
||||
static const uint MAX_LOOP_ITERATION_COUNT = 1000;
|
||||
bool _returnImmediately = false;
|
||||
Common::Array<ScriptValue> _locals;
|
||||
Common::Array<ScriptValue> *_args = nullptr;
|
||||
ParameterReadStream *_bytecode = nullptr;
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
173
engines/mediastation/mediascript/collection.cpp
Normal file
173
engines/mediastation/mediascript/collection.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/mediastation.h"
|
||||
#include "mediastation/mediascript/collection.h"
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/codechunk.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
switch (method) {
|
||||
case kAppendMethod:
|
||||
for (ScriptValue value : args) {
|
||||
push_back(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case kApplyMethod:
|
||||
apply(args);
|
||||
break;
|
||||
|
||||
case kCountMethod:
|
||||
assert(args.empty());
|
||||
returnValue.setToFloat(size());
|
||||
break;
|
||||
|
||||
case kDeleteFirstMethod:
|
||||
assert(args.empty());
|
||||
returnValue = remove_at(0);
|
||||
break;
|
||||
|
||||
case kDeleteLastMethod:
|
||||
assert(args.empty());
|
||||
returnValue = remove_at(size() - 1);
|
||||
break;
|
||||
|
||||
case kEmptyMethod:
|
||||
assert(args.empty());
|
||||
clear();
|
||||
break;
|
||||
|
||||
case kGetAtMethod: {
|
||||
assert(args.size() == 1);
|
||||
uint index = static_cast<uint>(args[0].asFloat());
|
||||
returnValue = operator[](index);
|
||||
break;
|
||||
}
|
||||
|
||||
case kIsEmptyMethod:
|
||||
assert(args.empty());
|
||||
returnValue.setToBool(empty());
|
||||
break;
|
||||
|
||||
case kJumbleMethod:
|
||||
assert(args.empty());
|
||||
jumble();
|
||||
break;
|
||||
|
||||
case kSeekMethod: {
|
||||
assert(args.size() == 1);
|
||||
int index = seek(args[0]);
|
||||
returnValue.setToFloat(index);
|
||||
break;
|
||||
}
|
||||
|
||||
case kSendMethod:
|
||||
send(args);
|
||||
break;
|
||||
|
||||
case kDeleteAtMethod: {
|
||||
assert(args.size() == 1);
|
||||
uint index = static_cast<uint>(args[0].asFloat());
|
||||
returnValue = remove_at(index);
|
||||
break;
|
||||
}
|
||||
|
||||
case kInsertAtMethod: {
|
||||
assert(args.size() == 2);
|
||||
uint index = static_cast<uint>(args[1].asFloat());
|
||||
insert_at(index, args[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
case kReplaceAtMethod: {
|
||||
assert(args.size() == 2);
|
||||
uint index = static_cast<uint>(args[1].asFloat());
|
||||
operator[](index) = args[0];
|
||||
break;
|
||||
}
|
||||
|
||||
case kPrependListMethod:
|
||||
insert_at(0, args);
|
||||
break;
|
||||
|
||||
case kSortMethod:
|
||||
assert(args.empty());
|
||||
Common::sort(begin(), end());
|
||||
break;
|
||||
|
||||
default:
|
||||
error("%s: Attempt to call unimplemented method %s (%d)", __func__, builtInMethodToStr(method), static_cast<uint>(method));
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void Collection::apply(const Common::Array<ScriptValue> &args) {
|
||||
// Calls a function with each element of the collection as the first arg.
|
||||
Common::Array<ScriptValue> argsToApply = args;
|
||||
uint functionId = args[0].asFunctionId();
|
||||
for (const ScriptValue &item : *this) {
|
||||
argsToApply[0] = item;
|
||||
g_engine->getFunctionManager()->call(functionId, argsToApply);
|
||||
}
|
||||
}
|
||||
|
||||
void Collection::send(const Common::Array<ScriptValue> &args) {
|
||||
Common::Array<ScriptValue> argsToSend(args.size() - 1);
|
||||
if (argsToSend.size() > 0) {
|
||||
for (uint i = 1; i < args.size(); i++) {
|
||||
argsToSend[i - 1] = args[i];
|
||||
}
|
||||
}
|
||||
|
||||
BuiltInMethod methodToSend = static_cast<BuiltInMethod>(args[0].asMethodId());
|
||||
Common::Array<ScriptValue> sendArgs;
|
||||
for (const ScriptValue &item : *this) {
|
||||
uint actorId = item.asActorId();
|
||||
Actor *targetActor = g_engine->getActorById(actorId);
|
||||
if (targetActor != nullptr) {
|
||||
targetActor->callMethod(methodToSend, argsToSend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Collection::seek(const ScriptValue &item) {
|
||||
// Search from back to front.
|
||||
for (int i = size() - 1; i >= 0; i--) {
|
||||
if (item == operator[](i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Collection::jumble() {
|
||||
for (uint i = size() - 1; i > 0; --i) {
|
||||
uint j = g_engine->_randomSource.getRandomNumber(size() - 1);
|
||||
SWAP(operator[](i), operator[](j));
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
49
engines/mediastation/mediascript/collection.h
Normal file
49
engines/mediastation/mediascript/collection.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_MEDIASCRIPT_COLLECTION_H
|
||||
#define MEDIASTATION_MEDIASCRIPT_COLLECTION_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/str.h"
|
||||
#include "common/array.h"
|
||||
|
||||
#include "mediastation/datafile.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class ScriptValue;
|
||||
|
||||
class Collection : public Common::Array<ScriptValue> {
|
||||
public:
|
||||
ScriptValue callMethod(BuiltInMethod method, Common::Array<ScriptValue> &args);
|
||||
|
||||
private:
|
||||
void apply(const Common::Array<ScriptValue> &values);
|
||||
void send(const Common::Array<ScriptValue> &values);
|
||||
int seek(const ScriptValue &itemToFind);
|
||||
void jumble();
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
56
engines/mediastation/mediascript/eventhandler.cpp
Normal file
56
engines/mediastation/mediascript/eventhandler.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/mediascript/eventhandler.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
EventHandler::EventHandler(Chunk &chunk) {
|
||||
_type = static_cast<EventType>(chunk.readTypedUint16());
|
||||
debugC(5, kDebugLoading, "EventHandler::EventHandler(): Type %s (%d) (@0x%llx)",
|
||||
eventTypeToStr(_type), static_cast<uint>(_type), static_cast<long long int>(chunk.pos()));
|
||||
|
||||
_argumentValue = ScriptValue(&chunk);
|
||||
_code = new CodeChunk(chunk);
|
||||
}
|
||||
|
||||
ScriptValue EventHandler::execute(uint actorId) {
|
||||
// TODO: The actorId is only passed in for debug visibility, there should be
|
||||
// a better way to handle that.
|
||||
Common::String actorAndType = Common::String::format("(actor %d) (type = %s)", actorId, eventTypeToStr(_type));
|
||||
Common::String argValue = Common::String::format("(%s)", _argumentValue.getDebugString().c_str());
|
||||
debugC(5, kDebugScript, "\n********** EVENT HANDLER %s %s **********", actorAndType.c_str(), argValue.c_str());
|
||||
|
||||
// The only argument that can be provided to an
|
||||
// event handler is the _argumentValue.
|
||||
ScriptValue returnValue = _code->execute();
|
||||
|
||||
debugC(5, kDebugScript, "********** END EVENT HANDLER %s %s **********", actorAndType.c_str(), argValue.c_str());
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
EventHandler::~EventHandler() {
|
||||
delete _code;
|
||||
_code = nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
48
engines/mediastation/mediascript/eventhandler.h
Normal file
48
engines/mediastation/mediascript/eventhandler.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_MEDIASCRIPT_EVENTHANDLER_H
|
||||
#define MEDIASTATION_MEDIASCRIPT_EVENTHANDLER_H
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
#include "mediastation/datafile.h"
|
||||
#include "mediastation/mediascript/codechunk.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class EventHandler {
|
||||
public:
|
||||
EventHandler(Chunk &chunk);
|
||||
~EventHandler();
|
||||
|
||||
ScriptValue execute(uint actorId);
|
||||
EventType _type;
|
||||
ScriptValue _argumentValue;
|
||||
|
||||
private:
|
||||
CodeChunk *_code = nullptr;
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
567
engines/mediastation/mediascript/function.cpp
Normal file
567
engines/mediastation/mediascript/function.cpp
Normal file
@@ -0,0 +1,567 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/mediascript/function.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
#include "mediastation/mediastation.h"
|
||||
|
||||
namespace MediaStation {
|
||||
ScriptFunction::ScriptFunction(Chunk &chunk) {
|
||||
_contextId = chunk.readTypedUint16();
|
||||
// In PROFILE._ST (only present in some titles), the function ID is reported
|
||||
// with 19900 added, so function 100 would be reported as 20000. But in
|
||||
// bytecode, the zero-based ID is used, so that's what we'll store here.
|
||||
_id = chunk.readTypedUint16();
|
||||
_code = new CodeChunk(chunk);
|
||||
}
|
||||
|
||||
ScriptFunction::~ScriptFunction() {
|
||||
delete _code;
|
||||
_code = nullptr;
|
||||
}
|
||||
|
||||
ScriptValue ScriptFunction::execute(Common::Array<ScriptValue> &args) {
|
||||
debugC(5, kDebugScript, "\n********** SCRIPT FUNCTION %d **********", _id);
|
||||
ScriptValue returnValue = _code->execute(&args);
|
||||
debugC(5, kDebugScript, "********** END SCRIPT FUNCTION **********");
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
FunctionManager::~FunctionManager() {
|
||||
for (auto it = _functions.begin(); it != _functions.end(); ++it) {
|
||||
delete it->_value;
|
||||
}
|
||||
_functions.clear();
|
||||
}
|
||||
|
||||
bool FunctionManager::attemptToReadFromStream(Chunk &chunk, uint sectionType) {
|
||||
bool handledParam = true;
|
||||
switch (sectionType) {
|
||||
case 0x31: {
|
||||
ScriptFunction *function = new ScriptFunction(chunk);
|
||||
_functions.setVal(function->_id, function);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
handledParam = false;
|
||||
}
|
||||
|
||||
return handledParam;
|
||||
}
|
||||
|
||||
ScriptValue FunctionManager::call(uint functionId, Common::Array<ScriptValue> &args) {
|
||||
ScriptValue returnValue;
|
||||
|
||||
// The original had a complex function registration system that I deemed too uselessly complex to
|
||||
// reimplement. First, we try executing the title-defined function. We try this first because
|
||||
// later engine versions used some functions IDs that previously mapped to built-in functions in
|
||||
// earlier engine versions. So we will try executing the title-defined function first and only then
|
||||
// fall back to the built-in functions.
|
||||
ScriptFunction *scriptFunction = _functions.getValOrDefault(functionId);
|
||||
if (scriptFunction != nullptr) {
|
||||
returnValue = scriptFunction->execute(args);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// If there was no title-defined function, next check for built-in functions.
|
||||
switch (functionId) {
|
||||
case kRandomFunction:
|
||||
case kLegacy_RandomFunction:
|
||||
assert(args.size() == 2);
|
||||
script_Random(args, returnValue);
|
||||
break;
|
||||
|
||||
case kTimeOfDayFunction:
|
||||
case kLegacy_TimeOfDayFunction:
|
||||
script_TimeOfDay(args, returnValue);
|
||||
break;
|
||||
|
||||
case kEffectTransitionFunction:
|
||||
case kLegacy_EffectTransitionFunction:
|
||||
g_engine->getDisplayManager()->effectTransition(args);
|
||||
break;
|
||||
|
||||
case kEffectTransitionOnSyncFunction:
|
||||
case kLegacy_EffectTransitionOnSyncFunction:
|
||||
g_engine->getDisplayManager()->setTransitionOnSync(args);
|
||||
break;
|
||||
|
||||
case kPlatformFunction:
|
||||
case kLegacy_PlatformFunction:
|
||||
assert(args.empty());
|
||||
script_GetPlatform(args, returnValue);
|
||||
break;
|
||||
|
||||
case kSquareRootFunction:
|
||||
case kLegacy_SquareRootFunction:
|
||||
assert(args.size() == 1);
|
||||
script_SquareRoot(args, returnValue);
|
||||
break;
|
||||
|
||||
case kGetUniqueRandomFunction:
|
||||
case kLegacy_GetUniqueRandomFunction:
|
||||
assert(args.size() >= 2);
|
||||
script_GetUniqueRandom(args, returnValue);
|
||||
break;
|
||||
|
||||
case kCurrentRunTimeFunction:
|
||||
script_CurrentRunTime(args, returnValue);
|
||||
break;
|
||||
|
||||
case kSetGammaCorrectionFunction:
|
||||
script_SetGammaCorrection(args, returnValue);
|
||||
break;
|
||||
|
||||
case kGetDefaultGammaCorrectionFunction:
|
||||
script_GetDefaultGammaCorrection(args, returnValue);
|
||||
break;
|
||||
|
||||
case kGetCurrentGammaCorrectionFunction:
|
||||
script_GetCurrentGammaCorrection(args, returnValue);
|
||||
break;
|
||||
|
||||
case kSetAudioVolumeFunction:
|
||||
assert(args.size() == 1);
|
||||
script_SetAudioVolume(args, returnValue);
|
||||
break;
|
||||
|
||||
case kGetAudioVolumeFunction:
|
||||
assert(args.empty());
|
||||
script_GetAudioVolume(args, returnValue);
|
||||
break;
|
||||
|
||||
case kSystemLanguagePreferenceFunction:
|
||||
case kLegacy_SystemLanguagePreferenceFunction:
|
||||
script_SystemLanguagePreference(args, returnValue);
|
||||
break;
|
||||
|
||||
case kSetRegistryFunction:
|
||||
script_SetRegistry(args, returnValue);
|
||||
break;
|
||||
|
||||
case kGetRegistryFunction:
|
||||
script_GetRegistry(args, returnValue);
|
||||
break;
|
||||
|
||||
case kSetProfileFunction:
|
||||
script_SetProfile(args, returnValue);
|
||||
break;
|
||||
|
||||
case kMazeGenerateFunction:
|
||||
script_MazeGenerate(args, returnValue);
|
||||
break;
|
||||
|
||||
case kMazeApplyMoveMaskFunction:
|
||||
script_MazeApplyMoveMask(args, returnValue);
|
||||
break;
|
||||
|
||||
case kMazeSolveFunction:
|
||||
script_MazeSolve(args, returnValue);
|
||||
break;
|
||||
|
||||
case kBeginTimedIntervalFunction:
|
||||
script_BeginTimedInterval(args, returnValue);
|
||||
break;
|
||||
|
||||
case kEndTimedIntervalFunction:
|
||||
script_EndTimedInterval(args, returnValue);
|
||||
break;
|
||||
|
||||
case kDrawingFunction:
|
||||
script_Drawing(args, returnValue);
|
||||
break;
|
||||
|
||||
case kLegacy_DebugPrintFunction:
|
||||
script_DebugPrint(args, returnValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
// If we got here, that means there was neither a title-defined nor a built-in function
|
||||
// for this ID, so we can now declare it unimplemented. This is a warning instead of an error
|
||||
// so execution can continue, but if the function is expected to return anything, there will
|
||||
// likely be an error about attempting to assign a null value to a variable.
|
||||
warning("%s: Unimplemented function 0x%02x", __func__, functionId);
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void FunctionManager::script_GetPlatform(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
Common::Platform platform = g_engine->getPlatform();
|
||||
switch (platform) {
|
||||
case Common::Platform::kPlatformWindows:
|
||||
returnValue.setToParamToken(kPlatformParamTokenWindows);
|
||||
break;
|
||||
|
||||
case Common::Platform::kPlatformMacintosh:
|
||||
returnValue.setToParamToken(kPlatformParamTokenWindows);
|
||||
break;
|
||||
|
||||
default:
|
||||
warning("%s: Unknown platform %d", __func__, static_cast<int>(platform));
|
||||
returnValue.setToParamToken(kPlatformParamTokenUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
void FunctionManager::script_Random(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
// This function takes in a range, and then generates a random value within that range.
|
||||
ScriptValue bottomArg = args[0];
|
||||
ScriptValue topArg = args[1];
|
||||
if (bottomArg.getType() != topArg.getType()) {
|
||||
error("%s: Both arguments must be of same type", __func__);
|
||||
}
|
||||
|
||||
ScriptValueType type = args[0].getType();
|
||||
double bottom = 0.0;
|
||||
double top = 0.0;
|
||||
bool treatAsInteger = false;
|
||||
switch (type) {
|
||||
case kScriptValueTypeFloat: {
|
||||
// For numeric values, treat them as integers (floor values).
|
||||
bottom = floor(bottomArg.asFloat());
|
||||
top = floor(topArg.asFloat());
|
||||
treatAsInteger = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeBool: {
|
||||
// Convert boolean values to numbers.
|
||||
bottom = bottomArg.asBool() ? 1.0 : 0.0;
|
||||
top = topArg.asBool() ? 1.0 : 0.0;
|
||||
treatAsInteger = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeTime: {
|
||||
// Treat time values as capable of having fractional seconds.
|
||||
bottom = bottomArg.asTime();
|
||||
top = topArg.asTime();
|
||||
treatAsInteger = false;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
error("%s: Invalid argument type: %s", __func__, scriptValueTypeToStr(type));
|
||||
}
|
||||
|
||||
// Ensure proper inclusive ordering of bottom and top.
|
||||
if (top < bottom) {
|
||||
SWAP(top, bottom);
|
||||
}
|
||||
|
||||
// Calculate random value in range.
|
||||
double range = top - bottom;
|
||||
uint randomValue = g_engine->_randomSource.getRandomNumber(UINT32_MAX);
|
||||
double randomFloat = (static_cast<double>(randomValue) * range) / static_cast<double>(UINT32_MAX) + bottom;
|
||||
if (treatAsInteger) {
|
||||
randomFloat = floor(randomFloat);
|
||||
}
|
||||
|
||||
// Set result based on original argument type.
|
||||
switch (type) {
|
||||
case kScriptValueTypeFloat:
|
||||
returnValue.setToFloat(randomFloat);
|
||||
break;
|
||||
|
||||
case kScriptValueTypeBool: {
|
||||
bool boolResult = (randomFloat != 0.0);
|
||||
returnValue.setToBool(boolResult);
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeTime:
|
||||
returnValue.setToTime(randomFloat);
|
||||
break;
|
||||
|
||||
default:
|
||||
error("%s: Invalid argument type: %s", __func__, scriptValueTypeToStr(type));
|
||||
}
|
||||
}
|
||||
|
||||
void FunctionManager::script_TimeOfDay(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
warning("STUB: TimeOfDay");
|
||||
}
|
||||
|
||||
void FunctionManager::script_SquareRoot(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
if (args[0].getType() != kScriptValueTypeFloat) {
|
||||
error("%s: Numeric value required", __func__);
|
||||
}
|
||||
|
||||
double value = args[0].asFloat();
|
||||
if (value < 0.0) {
|
||||
error("%s: Argument must be nonnegative", __func__);
|
||||
}
|
||||
|
||||
double result = sqrt(value);
|
||||
returnValue.setToFloat(result);
|
||||
}
|
||||
|
||||
void FunctionManager::script_GetUniqueRandom(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
// Unlike the regular Random which simply returns any random number in a range, GetUniqueRandom allows the caller
|
||||
// to specify numbers that should NOT be returned (the third arg and onward), making it useful for generating random
|
||||
// values that haven't been used before or avoiding specific unwanted values.
|
||||
for (ScriptValue arg : args) {
|
||||
if (arg.getType() != kScriptValueTypeFloat) {
|
||||
error("%s: All arguments must be numeric", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
// The original forces that the list of excluded numbers (and the range to choose from)
|
||||
// can be at max 100 numbers. With the two args for the range, the max is thus 102.
|
||||
const uint MAX_ARGS_SIZE = 102;
|
||||
if (args.size() > MAX_ARGS_SIZE) {
|
||||
args.resize(MAX_ARGS_SIZE);
|
||||
}
|
||||
|
||||
// Ensure that the range is properly constructed.
|
||||
double bottom = floor(args[0].asFloat());
|
||||
double top = floor(args[1].asFloat());
|
||||
if (top < bottom) {
|
||||
SWAP(top, bottom);
|
||||
}
|
||||
|
||||
// Build list of unused (non-excluded) numbers in the range. For this numeric type,
|
||||
// everything is treated as an integer (even though it's stored as a double).
|
||||
Common::Array<double> unusedNumbers;
|
||||
for (double currentValue = bottom; currentValue < top; currentValue += 1.0) {
|
||||
// Check if this value appears in the exclusion list (args 2 onwards).
|
||||
bool isExcluded = false;
|
||||
for (uint i = 2; i < args.size(); i++) {
|
||||
if (args[i].asFloat() == currentValue) {
|
||||
isExcluded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isExcluded) {
|
||||
unusedNumbers.push_back(currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (unusedNumbers.size() > 0) {
|
||||
uint randomIndex = g_engine->_randomSource.getRandomNumberRng(0, unusedNumbers.size());
|
||||
returnValue.setToFloat(unusedNumbers[randomIndex]);
|
||||
} else {
|
||||
warning("%s: No unused numbers to choose from", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void FunctionManager::script_CurrentRunTime(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
// The current runtime is expected to be returned in seconds.
|
||||
const uint MILLISECONDS_IN_ONE_SECOND = 1000;
|
||||
double runtimeInSeconds = g_system->getMillis() / MILLISECONDS_IN_ONE_SECOND;
|
||||
returnValue.setToFloat(runtimeInSeconds);
|
||||
}
|
||||
|
||||
void FunctionManager::script_SetGammaCorrection(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
if (args.size() != 1 && args.size() != 3) {
|
||||
warning("%s: Expected 1 or 3 arguments, got %u", __func__, args.size());
|
||||
return;
|
||||
}
|
||||
|
||||
double red = 1.0;
|
||||
double green = 1.0;
|
||||
double blue = 1.0;
|
||||
if (args.size() >= 3) {
|
||||
if (args[0].getType() != kScriptValueTypeFloat ||
|
||||
args[1].getType() != kScriptValueTypeFloat ||
|
||||
args[2].getType() != kScriptValueTypeFloat) {
|
||||
warning("%s: Expected float arguments", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
red = args[0].asFloat();
|
||||
green = args[1].asFloat();
|
||||
blue = args[2].asFloat();
|
||||
|
||||
} else if (args.size() >= 1) {
|
||||
if (args[0].getType() != kScriptValueTypeCollection) {
|
||||
warning("%s: Expected collection argument", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
Common::SharedPtr<Collection> collection = args[0].asCollection();
|
||||
if (collection->size() != 3) {
|
||||
warning("%s: Collection must contain exactly 3 elements, got %u", __func__, collection->size());
|
||||
return;
|
||||
}
|
||||
|
||||
if (collection->operator[](0).getType() != kScriptValueTypeFloat ||
|
||||
collection->operator[](1).getType() != kScriptValueTypeFloat ||
|
||||
collection->operator[](2).getType() != kScriptValueTypeFloat) {
|
||||
warning("%s: Expected float arguments", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
red = collection->operator[](0).asFloat();
|
||||
green = collection->operator[](1).asFloat();
|
||||
blue = collection->operator[](2).asFloat();
|
||||
}
|
||||
|
||||
g_engine->getDisplayManager()->setGammaValues(red, green, blue);
|
||||
}
|
||||
|
||||
void FunctionManager::script_GetDefaultGammaCorrection(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
if (args.size() != 0) {
|
||||
warning("%s: Expected 0 arguments, got %u", __func__, args.size());
|
||||
return;
|
||||
}
|
||||
|
||||
double red, green, blue;
|
||||
g_engine->getDisplayManager()->getDefaultGammaValues(red, green, blue);
|
||||
|
||||
Common::SharedPtr<Collection> collection = Common::SharedPtr<Collection>(new Collection());
|
||||
ScriptValue redValue;
|
||||
redValue.setToFloat(red);
|
||||
collection->push_back(redValue);
|
||||
|
||||
ScriptValue greenValue;
|
||||
greenValue.setToFloat(green);
|
||||
collection->push_back(greenValue);
|
||||
|
||||
ScriptValue blueValue;
|
||||
blueValue.setToFloat(blue);
|
||||
collection->push_back(blueValue);
|
||||
|
||||
returnValue.setToCollection(collection);
|
||||
}
|
||||
|
||||
void FunctionManager::script_GetCurrentGammaCorrection(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
if (args.size() != 0) {
|
||||
warning("%s: Expected 0 arguments, got %u", __func__, args.size());
|
||||
return;
|
||||
}
|
||||
|
||||
double red, green, blue;
|
||||
g_engine->getDisplayManager()->getGammaValues(red, green, blue);
|
||||
Common::SharedPtr<Collection> collection = Common::SharedPtr<Collection>(new Collection());
|
||||
|
||||
ScriptValue redValue;
|
||||
redValue.setToFloat(red);
|
||||
collection->push_back(redValue);
|
||||
|
||||
ScriptValue greenValue;
|
||||
greenValue.setToFloat(green);
|
||||
collection->push_back(greenValue);
|
||||
|
||||
ScriptValue blueValue;
|
||||
blueValue.setToFloat(blue);
|
||||
collection->push_back(blueValue);
|
||||
|
||||
returnValue.setToCollection(collection);
|
||||
}
|
||||
|
||||
void FunctionManager::script_SetAudioVolume(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
if (args[0].getType() != kScriptValueTypeFloat) {
|
||||
warning("%s: Expected float argument", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert from 0.0 - 1.0 to ScummVM's mixer range.
|
||||
double volume = args[0].asFloat();
|
||||
volume = CLIP(volume, 0.0, 1.0);
|
||||
int mixerVolume = static_cast<int>(volume * Audio::Mixer::kMaxMixerVolume);
|
||||
g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, mixerVolume);
|
||||
}
|
||||
|
||||
void FunctionManager::script_GetAudioVolume(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
// Convert from ScummVM's mixer range to 0.0 - 1.0.
|
||||
int mixerVolume = g_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kPlainSoundType);
|
||||
double volume = static_cast<double>(mixerVolume) / static_cast<double>(Audio::Mixer::kMaxMixerVolume);
|
||||
CLIP(volume, 0.0, 1.0);
|
||||
returnValue.setToFloat(volume);
|
||||
}
|
||||
|
||||
void FunctionManager::script_SystemLanguagePreference(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
warning("STUB: SystemLanguagePreference");
|
||||
}
|
||||
|
||||
void FunctionManager::script_SetRegistry(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
warning("STUB: SetRegistry");
|
||||
}
|
||||
|
||||
void FunctionManager::script_GetRegistry(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
warning("STUB: GetRegistry");
|
||||
}
|
||||
|
||||
void FunctionManager::script_SetProfile(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
warning("STUB: SetProfile");
|
||||
}
|
||||
|
||||
void FunctionManager::script_DebugPrint(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
// The original reports time in seconds, but milliseconds is fine.
|
||||
// The "IMT @ clock ..." format is from the original's debug printing style.
|
||||
Common::String output = Common::String::format("IMT @ clock %d", g_system->getMillis());
|
||||
for (uint i = 0; i < args.size(); i++) {
|
||||
// Append all provided arguments.
|
||||
if (i != 0) {
|
||||
output += ", ";
|
||||
} else {
|
||||
output += " ";
|
||||
}
|
||||
output += args[i].getDebugString();
|
||||
}
|
||||
debug("%s", output.c_str());
|
||||
}
|
||||
|
||||
void FunctionManager::script_MazeGenerate(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
warning("STUB: MazeGenerate");
|
||||
}
|
||||
|
||||
void FunctionManager::script_MazeApplyMoveMask(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
warning("STUB: MazeApplyMoveMask");
|
||||
}
|
||||
|
||||
void FunctionManager::script_MazeSolve(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
warning("STUB: MazeSolve");
|
||||
}
|
||||
|
||||
void FunctionManager::script_BeginTimedInterval(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
warning("STUB: BeginTimedInterval");
|
||||
}
|
||||
|
||||
void FunctionManager::script_EndTimedInterval(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
warning("STUB: EndTimedInterval");
|
||||
}
|
||||
|
||||
void FunctionManager::script_Drawing(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
|
||||
warning("STUB: Drawing");
|
||||
}
|
||||
|
||||
void FunctionManager::deleteFunctionsForContext(uint contextId) {
|
||||
// Collect function IDs to delete first.
|
||||
Common::Array<ScriptFunction *> functionsToDelete;
|
||||
for (auto it = _functions.begin(); it != _functions.end(); ++it) {
|
||||
ScriptFunction *scriptFunction = it->_value;
|
||||
if (scriptFunction->_contextId == contextId) {
|
||||
functionsToDelete.push_back(scriptFunction);
|
||||
}
|
||||
}
|
||||
|
||||
// Now delete them.
|
||||
for (ScriptFunction *scriptFunction : functionsToDelete) {
|
||||
_functions.erase(scriptFunction->_id);
|
||||
delete scriptFunction;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
96
engines/mediastation/mediascript/function.h
Normal file
96
engines/mediastation/mediascript/function.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_MEDIASCRIPT_FUNCTION_H
|
||||
#define MEDIASTATION_MEDIASCRIPT_FUNCTION_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/hashmap.h"
|
||||
|
||||
#include "mediastation/clients.h"
|
||||
#include "mediastation/datafile.h"
|
||||
#include "mediastation/mediascript/codechunk.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
enum Platform {
|
||||
kPlatformParamTokenUnknown = 0,
|
||||
kPlatformParamTokenWindows = 0x76D,
|
||||
kPlatformParakTokenMacintosh = 0x76E
|
||||
};
|
||||
|
||||
class ScriptFunction {
|
||||
public:
|
||||
ScriptFunction(Chunk &chunk);
|
||||
~ScriptFunction();
|
||||
|
||||
ScriptValue execute(Common::Array<ScriptValue> &args);
|
||||
|
||||
uint _contextId = 0;
|
||||
uint _id = 0;
|
||||
|
||||
private:
|
||||
CodeChunk *_code = nullptr;
|
||||
};
|
||||
|
||||
class FunctionManager : public ParameterClient {
|
||||
public:
|
||||
FunctionManager() {};
|
||||
virtual ~FunctionManager();
|
||||
|
||||
virtual bool attemptToReadFromStream(Chunk &chunk, uint sectionType) override;
|
||||
ScriptValue call(uint functionId, Common::Array<ScriptValue> &args);
|
||||
void deleteFunctionsForContext(uint contextId);
|
||||
|
||||
private:
|
||||
Common::HashMap<uint, ScriptFunction *> _functions;
|
||||
|
||||
void script_GetPlatform(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_Random(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_TimeOfDay(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_SquareRoot(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_GetUniqueRandom(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_CurrentRunTime(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_SetGammaCorrection(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_GetDefaultGammaCorrection(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_GetCurrentGammaCorrection(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_SetAudioVolume(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_GetAudioVolume(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_SystemLanguagePreference(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_SetRegistry(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_GetRegistry(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_SetProfile(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_DebugPrint(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
|
||||
// 101 Dalmatians.
|
||||
void script_MazeGenerate(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_MazeApplyMoveMask(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_MazeSolve(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_BeginTimedInterval(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
void script_EndTimedInterval(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
|
||||
// IBM/Crayola.
|
||||
void script_Drawing(Common::Array<ScriptValue> &args, ScriptValue &returnValue);
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
491
engines/mediastation/mediascript/scriptconstants.cpp
Normal file
491
engines/mediastation/mediascript/scriptconstants.cpp
Normal file
@@ -0,0 +1,491 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
const char *expressionTypeToStr(ExpressionType type) {
|
||||
switch (type) {
|
||||
case kExpressionTypeEmpty:
|
||||
return "Empty";
|
||||
case kExpressionTypeVariable:
|
||||
return "Variable";
|
||||
case kExpressionTypeValue:
|
||||
return "Value";
|
||||
case kExpressionTypeOperation:
|
||||
return "Operation";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *opcodeToStr(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case kOpcodeIf:
|
||||
return "If";
|
||||
case kOpcodeIfElse:
|
||||
return "IfElse";
|
||||
case kOpcodeAssignVariable:
|
||||
return "AssignVariable";
|
||||
case kOpcodeOr:
|
||||
return "Or";
|
||||
case kOpcodeXor:
|
||||
return "Xor";
|
||||
case kOpcodeAnd:
|
||||
return "And";
|
||||
case kOpcodeEquals:
|
||||
return "==";
|
||||
case kOpcodeNotEquals:
|
||||
return "!=";
|
||||
case kOpcodeLessThan:
|
||||
return "<";
|
||||
case kOpcodeGreaterThan:
|
||||
return ">";
|
||||
case kOpcodeLessThanOrEqualTo:
|
||||
return "<=";
|
||||
case kOpcodeGreaterThanOrEqualTo:
|
||||
return ">=";
|
||||
case kOpcodeAdd:
|
||||
return "+";
|
||||
case kOpcodeSubtract:
|
||||
return "-";
|
||||
case kOpcodeMultiply:
|
||||
return "*";
|
||||
case kOpcodeDivide:
|
||||
return "/";
|
||||
case kOpcodeModulo:
|
||||
return "%";
|
||||
case kOpcodeNegate:
|
||||
return "-";
|
||||
case kOpcodeCallFunction:
|
||||
return "CallFunction";
|
||||
case kOpcodeCallMethod:
|
||||
return "CallMethod";
|
||||
case kOpcodeDeclareLocals:
|
||||
return "DeclareLocals";
|
||||
case kOpcodeReturn:
|
||||
return "Return";
|
||||
case kOpcodeReturnNoValue:
|
||||
return "ReturnNoValue";
|
||||
case kOpcodeWhile:
|
||||
return "While";
|
||||
case kOpcodeCallFunctionInVariable:
|
||||
return "CallFunctionInVariable";
|
||||
case kOpcodeCallMethodInVariable:
|
||||
return "CallMethodInVariable";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *variableScopeToStr(VariableScope scope) {
|
||||
switch (scope) {
|
||||
case kVariableScopeLocal:
|
||||
return "Local";
|
||||
case kVariableScopeParameter:
|
||||
return "Parameter";
|
||||
case kVariableScopeIndirectParameter:
|
||||
return "IndirectParameter";
|
||||
case kVariableScopeGlobal:
|
||||
return "Global";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *builtInFunctionToStr(BuiltInFunction function) {
|
||||
switch (function) {
|
||||
case kRandomFunction:
|
||||
return "Random";
|
||||
case kTimeOfDayFunction:
|
||||
return "TimeOfDay";
|
||||
case kEffectTransitionFunction:
|
||||
return "EffectTransition";
|
||||
case kEffectTransitionOnSyncFunction:
|
||||
return "EffectTransitionOnSync";
|
||||
case kPlatformFunction:
|
||||
return "Platform";
|
||||
case kSquareRootFunction:
|
||||
return "SquareRoot";
|
||||
case kGetUniqueRandomFunction:
|
||||
return "GetUniqueRandom";
|
||||
case kCurrentRunTimeFunction:
|
||||
return "CurrentRunTime";
|
||||
case kSetGammaCorrectionFunction:
|
||||
return "SetGammaCorrection";
|
||||
case kGetDefaultGammaCorrectionFunction:
|
||||
return "GetDefaultGammaCorrection";
|
||||
case kGetCurrentGammaCorrectionFunction:
|
||||
return "GetCurrentGammaCorrection";
|
||||
case kSetAudioVolumeFunction:
|
||||
return "SetAudioVolume";
|
||||
case kGetAudioVolumeFunction:
|
||||
return "GetAudioVolume";
|
||||
case kSystemLanguagePreferenceFunction:
|
||||
return "SystemLanguagePreference";
|
||||
case kSetRegistryFunction:
|
||||
return "SetRegistry";
|
||||
case kGetRegistryFunction:
|
||||
return "GetRegistry";
|
||||
case kSetProfileFunction:
|
||||
return "SetProfile";
|
||||
case kMazeGenerateFunction:
|
||||
return "MazeGenerate";
|
||||
case kMazeApplyMoveMaskFunction:
|
||||
return "MazeApplyMoveMask";
|
||||
case kMazeSolveFunction:
|
||||
return "MazeSolve";
|
||||
case kBeginTimedIntervalFunction:
|
||||
return "BeginTimedInterval";
|
||||
case kEndTimedIntervalFunction:
|
||||
return "EndTimedInterval";
|
||||
case kDrawingFunction:
|
||||
return "Drawing";
|
||||
case kLegacy_RandomFunction:
|
||||
return "Legacy Random";
|
||||
case kLegacy_TimeOfDayFunction:
|
||||
return "Legacy TimeOfDay";
|
||||
case kLegacy_EffectTransitionFunction:
|
||||
return "Legacy EffectTransition";
|
||||
case kLegacy_EffectTransitionOnSyncFunction:
|
||||
return "Legacy EffectTransitionOnSync";
|
||||
case kLegacy_PlatformFunction:
|
||||
return "Legacy Platform";
|
||||
case kLegacy_SquareRootFunction:
|
||||
return "Legacy SquareRoot";
|
||||
case kLegacy_GetUniqueRandomFunction:
|
||||
return "Legacy GetUniqueRandom";
|
||||
case kLegacy_DebugPrintFunction:
|
||||
return "DebugPrint";
|
||||
case kLegacy_SystemLanguagePreferenceFunction:
|
||||
return "Legacy SystemLanguagePreference";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *builtInMethodToStr(BuiltInMethod method) {
|
||||
switch (method) {
|
||||
case kCursorSetMethod:
|
||||
return "CursorSet";
|
||||
case kSpatialHideMethod:
|
||||
return "SpatialHide";
|
||||
case kSpatialMoveToMethod:
|
||||
return "SpatialMoveTo";
|
||||
case kSpatialMoveToByOffsetMethod:
|
||||
return "SpatialMoveToByOffset";
|
||||
case kSpatialZMoveToMethod:
|
||||
return "SpatialZMoveTo";
|
||||
case kSpatialCenterMoveToMethod:
|
||||
return "SpatialCenterMoveTo";
|
||||
case kSpatialShowMethod:
|
||||
return "SpatialShow";
|
||||
case kTimePlayMethod:
|
||||
return "TimePlay";
|
||||
case kTimeStopMethod:
|
||||
return "TimeStop";
|
||||
case kIsPlayingMethod:
|
||||
return "IsPlaying/SetMultipleStreams";
|
||||
case kSetDissolveFactorMethod:
|
||||
return "SetDissolveFactor";
|
||||
case kMouseActivateMethod:
|
||||
return "MouseActivate";
|
||||
case kMouseDeactivateMethod:
|
||||
return "MouseDeactivate";
|
||||
case kGetLeftXMethod:
|
||||
return "GetLeftX";
|
||||
case kGetTopYMethod:
|
||||
return "GetTopY";
|
||||
case kTriggerAbsXPositionMethod:
|
||||
return "TriggerAbsXPosition";
|
||||
case kTriggerAbsYPositionMethod:
|
||||
return "TriggerAbsYPosition";
|
||||
case kIsActiveMethod:
|
||||
return "IsActive";
|
||||
case kGetWidthMethod:
|
||||
return "GetWidth";
|
||||
case kGetHeightMethod:
|
||||
return "GetHeight";
|
||||
case kGetCenterXMethod:
|
||||
return "GetCenterX";
|
||||
case kGetCenterYMethod:
|
||||
return "GetCenterY";
|
||||
case kGetZCoordinateMethod:
|
||||
return "GetZCoordinate";
|
||||
case kIsPointInsideMethod:
|
||||
return "IsPointInside";
|
||||
case kGetMouseXOffsetMethod:
|
||||
return "GetMouseXOffset";
|
||||
case kGetMouseYOffsetMethod:
|
||||
return "GetMouseYOffset";
|
||||
case kIsVisibleMethod:
|
||||
return "IsVisible";
|
||||
case kSetMousePositionMethod:
|
||||
return "SetMousePosition";
|
||||
case kGetXScaleMethod1:
|
||||
case kGetXScaleMethod2:
|
||||
return "GetXScale";
|
||||
case kSetScaleMethod:
|
||||
return "SetScale";
|
||||
case kSetXScaleMethod:
|
||||
return "SetXScale";
|
||||
case kGetYScaleMethod:
|
||||
return "GetYScale";
|
||||
case kSetYScaleMethod:
|
||||
return "SetYScale";
|
||||
case kMovieResetMethod:
|
||||
return "MovieReset";
|
||||
case kSetCurrentClipMethod:
|
||||
return "SetCurrentClip";
|
||||
case kIncrementFrameMethod:
|
||||
return "IncrementFrame";
|
||||
case kDecrementFrameMethod:
|
||||
return "DecrementFrame";
|
||||
case kGetCurrentClipIdMethod:
|
||||
return "GetCurrentClipId";
|
||||
case kSetWorldSpaceExtentMethod:
|
||||
return "SetWorldSpaceExtent";
|
||||
case kSetBoundsMethod:
|
||||
return "SetBounds";
|
||||
case kStageGetWidthMethod:
|
||||
return "StageGetWidth";
|
||||
case kStageGetHeightMethod:
|
||||
return "StageGetHeight";
|
||||
case kAddToStageMethod:
|
||||
return "AddToStage\\OpenLens";
|
||||
case kRemoveFromStageMethod:
|
||||
return "RemoveFromStage\\CloseLens";
|
||||
case kAddedToStageMethod:
|
||||
return "AddedToStage";
|
||||
case kStartPanMethod:
|
||||
return "StartPan";
|
||||
case kStopPanMethod:
|
||||
return "StopPan";
|
||||
case kIsPanningMethod:
|
||||
return "IsPanning";
|
||||
case kViewportMoveToMethod:
|
||||
return "ViewportMoveTo";
|
||||
case kAdjustCameraViewportMethod:
|
||||
return "AdjustCameraViewport";
|
||||
case kAdjustCameraViewportSpatialCenterMethod:
|
||||
return "AdjustCameraViewportSpatialCenter";
|
||||
case kSetCameraBoundsMethod:
|
||||
return "SetCameraBounds";
|
||||
case kXViewportPositionMethod:
|
||||
return "XViewportPosition";
|
||||
case kYViewportPositionMethod:
|
||||
return "YViewportPosition";
|
||||
case kPanToMethod:
|
||||
return "PanTo";
|
||||
case kClearToPaletteMethod:
|
||||
return "ClearToPalette";
|
||||
case kDocumentLoadContextMethod:
|
||||
return "LoadContext";
|
||||
case kDocumentReleaseContextMethod:
|
||||
return "ReleaseContext";
|
||||
case kDocumentBranchToScreenMethod:
|
||||
return "BranchToScreen";
|
||||
case kDocumentQuitMethod:
|
||||
return "Quit";
|
||||
case kDocumentContextLoadInProgressMethod:
|
||||
return "ContextLoadInProgress";
|
||||
case kDocumentSetMultipleSoundsMethod:
|
||||
return "SetMultipleSounds";
|
||||
case kDocumentContextIsLoadedMethod:
|
||||
return "IsLoaded";
|
||||
case kSetDurationMethod:
|
||||
return "SetDuration";
|
||||
case kPercentCompleteMethod:
|
||||
return "PercentComplete";
|
||||
case kTextMethod:
|
||||
return "Text";
|
||||
case kSetTextMethod:
|
||||
return "SetText";
|
||||
case kSetMaximumTextLengthMethod:
|
||||
return "SetMaximumTextLength";
|
||||
case kAppendMethod:
|
||||
return "Append";
|
||||
case kApplyMethod:
|
||||
return "Apply";
|
||||
case kCountMethod:
|
||||
return "Count";
|
||||
case kDeleteFirstMethod:
|
||||
return "DeleteFirst";
|
||||
case kDeleteLastMethod:
|
||||
return "DeleteLast";
|
||||
case kEmptyMethod:
|
||||
return "Empty";
|
||||
case kGetAtMethod:
|
||||
return "GetAt";
|
||||
case kIsEmptyMethod:
|
||||
return "IsEmpty";
|
||||
case kJumbleMethod:
|
||||
return "Jumble";
|
||||
case kSeekMethod:
|
||||
return "Seek";
|
||||
case kSendMethod:
|
||||
return "Send";
|
||||
case kDeleteAtMethod:
|
||||
return "DeleteAt";
|
||||
case kInsertAtMethod:
|
||||
return "InsertAt";
|
||||
case kReplaceAtMethod:
|
||||
return "ReplaceAt";
|
||||
case kPrependListMethod:
|
||||
return "PrependList";
|
||||
case kSortMethod:
|
||||
return "Sort";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *eventTypeToStr(EventType type) {
|
||||
switch (type) {
|
||||
case kTimerEvent:
|
||||
return "Timer";
|
||||
case kMouseDownEvent:
|
||||
return "MouseDown";
|
||||
case kMouseUpEvent:
|
||||
return "MouseUp";
|
||||
case kMouseMovedEvent:
|
||||
return "MouseMoved";
|
||||
case kMouseEnteredEvent:
|
||||
return "MouseEntered";
|
||||
case kMouseExitedEvent:
|
||||
return "MouseExited";
|
||||
case kKeyDownEvent:
|
||||
return "KeyDown";
|
||||
case kSoundEndEvent:
|
||||
return "SoundEnd";
|
||||
case kSoundAbortEvent:
|
||||
return "SoundAbort";
|
||||
case kSoundFailureEvent:
|
||||
return "SoundFailure";
|
||||
case kSoundStoppedEvent:
|
||||
return "SoundStopped";
|
||||
case kSoundBeginEvent:
|
||||
return "SoundBegin";
|
||||
case kMovieEndEvent:
|
||||
return "MovieEnd";
|
||||
case kMovieAbortEvent:
|
||||
return "MovieAbort";
|
||||
case kMovieFailureEvent:
|
||||
return "MovieFailure";
|
||||
case kMovieStoppedEvent:
|
||||
return "MovieStopped";
|
||||
case kMovieBeginEvent:
|
||||
return "MovieBegin";
|
||||
case kSpriteMovieEndEvent:
|
||||
return "SpriteMovieEnd";
|
||||
case kScreenEntryEvent:
|
||||
return "ScreenEntry";
|
||||
case kScreenExitEvent:
|
||||
return "ScreenExit";
|
||||
case kContextLoadCompleteEvent:
|
||||
return "ContextLoadComplete";
|
||||
case kContextLoadCompleteEvent2:
|
||||
return "ContextLoadComplete2";
|
||||
case kContextLoadAbortEvent:
|
||||
return "ContextLoadAbort";
|
||||
case kContextLoadFailureEvent:
|
||||
return "ContextLoadFailure";
|
||||
case kTextInputEvent:
|
||||
return "TextInput";
|
||||
case kTextErrorEvent:
|
||||
return "TextError";
|
||||
case kCameraPanStepEvent:
|
||||
return "CameraPanStep";
|
||||
case kCameraPanAbortEvent:
|
||||
return "CameraPanAbort";
|
||||
case kCameraPanEndEvent:
|
||||
return "CameraPanEnd";
|
||||
case kPathStepEvent:
|
||||
return "PathStep";
|
||||
case kPathStoppedEvent:
|
||||
return "PathStopped";
|
||||
case kPathEndEvent:
|
||||
return "PathEnd";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *operandTypeToStr(OperandType type) {
|
||||
switch (type) {
|
||||
case kOperandTypeEmpty:
|
||||
return "Empty";
|
||||
case kOperandTypeBool:
|
||||
return "Bool";
|
||||
case kOperandTypeFloat:
|
||||
return "Float";
|
||||
case kOperandTypeInt:
|
||||
return "Int";
|
||||
case kOperandTypeString:
|
||||
return "String";
|
||||
case kOperandTypeParamToken:
|
||||
return "DollarSignVariable";
|
||||
case kOperandTypeActorId:
|
||||
return "ActorId";
|
||||
case kOperandTypeTime:
|
||||
return "Time";
|
||||
case kOperandTypeVariable:
|
||||
return "Variable";
|
||||
case kOperandTypeFunctionId:
|
||||
return "FunctionId";
|
||||
case kOperandTypeMethodId:
|
||||
return "MethodId";
|
||||
case kOperandTypeCollection:
|
||||
return "Collection";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *scriptValueTypeToStr(ScriptValueType type) {
|
||||
switch (type) {
|
||||
case kScriptValueTypeEmpty:
|
||||
return "Empty";
|
||||
case kScriptValueTypeFloat:
|
||||
return "Float";
|
||||
case kScriptValueTypeBool:
|
||||
return "Bool";
|
||||
case kScriptValueTypeTime:
|
||||
return "Time";
|
||||
case kScriptValueTypeParamToken:
|
||||
return "Int";
|
||||
case kScriptValueTypeActorId:
|
||||
return "ActorId";
|
||||
case kScriptValueTypeString:
|
||||
return "String";
|
||||
case kScriptValueTypeCollection:
|
||||
return "Collection";
|
||||
case kScriptValueTypeFunctionId:
|
||||
return "FunctionId";
|
||||
case kScriptValueTypeMethodId:
|
||||
return "MethodId";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
315
engines/mediastation/mediascript/scriptconstants.h
Normal file
315
engines/mediastation/mediascript/scriptconstants.h
Normal file
@@ -0,0 +1,315 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_MEDIASCRIPT_BUILTINS_H
|
||||
#define MEDIASTATION_MEDIASCRIPT_BUILTINS_H
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
enum ExpressionType {
|
||||
kExpressionTypeEmpty = 0x0000,
|
||||
kExpressionTypeVariable = 0x0065,
|
||||
kExpressionTypeValue = 0x0066,
|
||||
kExpressionTypeOperation = 0x0067,
|
||||
};
|
||||
const char *expressionTypeToStr(ExpressionType type);
|
||||
|
||||
enum Opcode {
|
||||
kOpcodeIf = 0xC9,
|
||||
kOpcodeIfElse = 0xCA,
|
||||
kOpcodeAssignVariable = 0xCB,
|
||||
kOpcodeOr = 0xCC,
|
||||
kOpcodeXor = 0xCD,
|
||||
kOpcodeAnd = 0xCE,
|
||||
kOpcodeEquals = 0xCF,
|
||||
kOpcodeNotEquals = 0xD0,
|
||||
kOpcodeLessThan = 0xD1,
|
||||
kOpcodeGreaterThan = 0xD2,
|
||||
kOpcodeLessThanOrEqualTo = 0xD3,
|
||||
kOpcodeGreaterThanOrEqualTo = 0xD4,
|
||||
kOpcodeAdd = 0xD5,
|
||||
kOpcodeSubtract = 0xD6,
|
||||
kOpcodeMultiply = 0xD7,
|
||||
kOpcodeDivide = 0xD8,
|
||||
kOpcodeModulo = 0xD9,
|
||||
kOpcodeNegate = 0xDA,
|
||||
kOpcodeCallFunction = 0xDB,
|
||||
kOpcodeCallMethod = 0xDC,
|
||||
kOpcodeDeclareLocals = 0xDD,
|
||||
kOpcodeReturn = 0xDE,
|
||||
kOpcodeReturnNoValue = 0xDF,
|
||||
kOpcodeWhile = 0xE0,
|
||||
kOpcodeCallFunctionInVariable = 0xE1, // IndirectCall
|
||||
kOpcodeCallMethodInVariable = 0xE2 // IndirectMsg
|
||||
};
|
||||
const char *opcodeToStr(Opcode opcode);
|
||||
|
||||
enum VariableScope {
|
||||
kVariableScopeLocal = 0x1,
|
||||
kVariableScopeParameter = 0x2,
|
||||
kVariableScopeIndirectParameter = 0x3,
|
||||
kVariableScopeGlobal = 0x4
|
||||
};
|
||||
const char *variableScopeToStr(VariableScope scope);
|
||||
|
||||
enum BuiltInFunction {
|
||||
kRandomFunction = 0x0A,
|
||||
kTimeOfDayFunction = 0x0B,
|
||||
kEffectTransitionFunction = 0x0C,
|
||||
kEffectTransitionOnSyncFunction = 0x0D,
|
||||
kPlatformFunction = 0x0E,
|
||||
kSquareRootFunction = 0x0F,
|
||||
kGetUniqueRandomFunction = 0x10,
|
||||
kCurrentRunTimeFunction = 0x11,
|
||||
kSetGammaCorrectionFunction = 0x12,
|
||||
kGetDefaultGammaCorrectionFunction = 0x13,
|
||||
kGetCurrentGammaCorrectionFunction = 0x14,
|
||||
kSetAudioVolumeFunction = 0x17,
|
||||
kGetAudioVolumeFunction = 0x18,
|
||||
kSystemLanguagePreferenceFunction = 0x19,
|
||||
kSetRegistryFunction = 0x1A,
|
||||
kGetRegistryFunction = 0x1B,
|
||||
kSetProfileFunction = 0x1C,
|
||||
kMazeGenerateFunction = 0x1F,
|
||||
kMazeApplyMoveMaskFunction = 0x20,
|
||||
kMazeSolveFunction = 0x21,
|
||||
kBeginTimedIntervalFunction = 0x22,
|
||||
kEndTimedIntervalFunction = 0x23,
|
||||
kDrawingFunction = 0x25,
|
||||
|
||||
// Early engine versions (like for Lion King and such), had different opcodes
|
||||
// for some functions, even though the functions were the same. So those are
|
||||
// defined here.
|
||||
kLegacy_RandomFunction = 0x64,
|
||||
kLegacy_TimeOfDayFunction = 0x65,
|
||||
kLegacy_EffectTransitionFunction = 0x66,
|
||||
kLegacy_EffectTransitionOnSyncFunction = 0x67,
|
||||
kLegacy_PlatformFunction = 0x68,
|
||||
kLegacy_SquareRootFunction = 0x69,
|
||||
kLegacy_GetUniqueRandomFunction = 0x6A,
|
||||
kLegacy_DebugPrintFunction = 0xB4,
|
||||
kLegacy_SystemLanguagePreferenceFunction = 0xC8,
|
||||
};
|
||||
const char *builtInFunctionToStr(BuiltInFunction function);
|
||||
|
||||
enum BuiltInMethod {
|
||||
kInvalidMethod = 0,
|
||||
// TODO: What object types does CursorSet apply to?
|
||||
// Currently it's only in var_7be1_cursor_currentTool in
|
||||
// IBM/Crayola.
|
||||
kCursorSetMethod = 0xC8,
|
||||
|
||||
// SPATIAL ENTITY METHODS.
|
||||
kSpatialHideMethod = 0xCB,
|
||||
kSpatialMoveToMethod = 0xCC,
|
||||
kSpatialMoveToByOffsetMethod = 0xCD,
|
||||
kSpatialZMoveToMethod = 0xD8,
|
||||
kSpatialShowMethod = 0xCA,
|
||||
kTimePlayMethod = 0xCE,
|
||||
kTimeStopMethod = 0xCF,
|
||||
kIsPlayingMethod = 0x174,
|
||||
kSetDissolveFactorMethod = 0xF1,
|
||||
kSpatialCenterMoveToMethod = 0xE6,
|
||||
kGetLeftXMethod = 0xE9,
|
||||
kGetTopYMethod = 0xEA,
|
||||
kGetWidthMethod = 0xEB,
|
||||
kGetHeightMethod = 0xEC,
|
||||
kGetCenterXMethod = 0xED,
|
||||
kGetCenterYMethod = 0xEE,
|
||||
kGetZCoordinateMethod = 0xEF,
|
||||
kIsPointInsideMethod = 0xF6,
|
||||
kGetMouseXOffsetMethod = 0x108,
|
||||
kGetMouseYOffsetMethod = 0x109,
|
||||
kIsVisibleMethod = 0x10D,
|
||||
kSetMousePositionMethod = 0x129,
|
||||
// It isn't clear what the difference is meant to be
|
||||
// between these two, as the code looks the same for both.
|
||||
kGetXScaleMethod1 = 0x16E,
|
||||
kGetXScaleMethod2 = 0x17E,
|
||||
kSetScaleMethod = 0x16F,
|
||||
kSetXScaleMethod = 0x17F,
|
||||
kGetYScaleMethod = 0x180,
|
||||
kSetYScaleMethod = 0x181,
|
||||
|
||||
// HOTSPOT METHODS.
|
||||
// NOTE: IDs 0xD2 and 0xD3 seem to be double-assigned
|
||||
// between two hotspot methods and two stage methods.
|
||||
kMouseActivateMethod = 0xD2,
|
||||
kMouseDeactivateMethod = 0xD3,
|
||||
kTriggerAbsXPositionMethod = 0x141,
|
||||
kTriggerAbsYPositionMethod = 0x142,
|
||||
kIsActiveMethod = 0x173,
|
||||
|
||||
// SPRITE METHODS.
|
||||
kMovieResetMethod = 0xDB,
|
||||
kSetCurrentClipMethod = 0xDC,
|
||||
kIncrementFrameMethod = 0xDD,
|
||||
kDecrementFrameMethod = 0xDE,
|
||||
kGetCurrentClipIdMethod = 0xF0,
|
||||
|
||||
// STAGE METHODS.
|
||||
// NOTE: IDs 0xD2 and 0xD3 seem to be double-assigned
|
||||
// between two hotspot methods and two stage methods.
|
||||
kAddActorToStageMethod = 0xD2,
|
||||
kRemoveActorFromStageMethod = 0xD3,
|
||||
kSetWorldSpaceExtentMethod = 0x16B,
|
||||
kSetBoundsMethod = 0x11F,
|
||||
kStageSetSizeMethod = 0x16B,
|
||||
kStageGetWidthMethod = 0x16C,
|
||||
kStageGetHeightMethod = 0x16D,
|
||||
|
||||
// CAMERA METHODS.
|
||||
// NOTE: IDs 0x15A and 0x15B seem to be double-assigned
|
||||
// between two camera methods and two printer methods.
|
||||
kAddToStageMethod = 0x15A,
|
||||
kRemoveFromStageMethod = 0x15B,
|
||||
kAddedToStageMethod = 0x15C,
|
||||
kStartPanMethod = 0x15D,
|
||||
kStopPanMethod = 0x15E,
|
||||
kIsPanningMethod = 0x15F,
|
||||
kViewportMoveToMethod = 0x160,
|
||||
kAdjustCameraViewportMethod = 0x161,
|
||||
kAdjustCameraViewportSpatialCenterMethod = 0x162,
|
||||
kSetCameraBoundsMethod = 0x163,
|
||||
kXViewportPositionMethod = 0x164,
|
||||
kYViewportPositionMethod = 0x165,
|
||||
kPanToMethod = 0x172,
|
||||
|
||||
// CANVAS METHODS.
|
||||
kClearToPaletteMethod = 0x17B,
|
||||
|
||||
// DOCUMENT METHODS.
|
||||
kDocumentBranchToScreenMethod = 0xC9,
|
||||
kDocumentQuitMethod = 0xD9,
|
||||
kDocumentContextLoadInProgressMethod = 0x169,
|
||||
kDocumentSetMultipleStreamsMethod = 0x174,
|
||||
kDocumentSetMultipleSoundsMethod = 0x175,
|
||||
kDocumentLoadContextMethod = 0x176,
|
||||
kDocumentReleaseContextMethod = 0x177,
|
||||
kDocumentContextIsLoadedMethod = 0x178,
|
||||
|
||||
// PATH METHODS.
|
||||
kSetDurationMethod = 0x106,
|
||||
kPercentCompleteMethod = 0x107,
|
||||
|
||||
// TEXT METHODS.
|
||||
kTextMethod = 0x122,
|
||||
kSetTextMethod = 0x123,
|
||||
kSetMaximumTextLengthMethod = 0x125,
|
||||
|
||||
// COLLECTION METHODS.
|
||||
// These are arrays used in Media Script.
|
||||
kAppendMethod = 0xF7,
|
||||
kApplyMethod = 0xF8,
|
||||
kCountMethod = 0xF9,
|
||||
kDeleteFirstMethod = 0xFA,
|
||||
kDeleteLastMethod = 0xFB,
|
||||
kEmptyMethod = 0xFC,
|
||||
kGetAtMethod = 0xFD,
|
||||
kIsEmptyMethod = 0xFE,
|
||||
kJumbleMethod = 0xFF,
|
||||
kSeekMethod = 0x100,
|
||||
kSendMethod = 0x101,
|
||||
kDeleteAtMethod = 0x102,
|
||||
kInsertAtMethod = 0x103,
|
||||
kReplaceAtMethod = 0x104,
|
||||
kPrependListMethod = 0x105,
|
||||
kSortMethod = 0x10A,
|
||||
|
||||
// PRINTER METHODS.
|
||||
// NOTE: IDs 0x15A and 0x15B seem to be double-assigned
|
||||
// between two camera methods and two printer methods.
|
||||
kOpenLensMethod = 0x15A,
|
||||
kCloseLensMethod = 0x15B,
|
||||
};
|
||||
const char *builtInMethodToStr(BuiltInMethod method);
|
||||
|
||||
enum EventType {
|
||||
kTimerEvent = 0x05,
|
||||
kMouseDownEvent = 0x06,
|
||||
kMouseUpEvent = 0x07,
|
||||
kMouseMovedEvent = 0x08,
|
||||
kMouseEnteredEvent = 0x09,
|
||||
kMouseExitedEvent = 0x0A,
|
||||
kKeyDownEvent = 0x0D,
|
||||
kSoundEndEvent = 0x0E,
|
||||
kMovieEndEvent = 0x0F,
|
||||
kPathEndEvent = 0x10,
|
||||
kScreenEntryEvent = 0x11,
|
||||
kSoundAbortEvent = 0x13,
|
||||
kSoundFailureEvent = 0x14,
|
||||
kMovieAbortEvent = 0x15,
|
||||
kMovieFailureEvent = 0x16,
|
||||
kSpriteMovieEndEvent = 0x17,
|
||||
kScreenExitEvent = 0x1B,
|
||||
kPathStepEvent = 0x1C,
|
||||
kSoundStoppedEvent = 0x1D,
|
||||
kSoundBeginEvent = 0x1E,
|
||||
kMovieStoppedEvent = 0x1F,
|
||||
kMovieBeginEvent = 0x20,
|
||||
kPathStoppedEvent = 0x21,
|
||||
kTextInputEvent = 0x25,
|
||||
kTextErrorEvent = 0x26,
|
||||
kCameraPanStepEvent = 0x29,
|
||||
kCameraPanEndEvent = 0x2A,
|
||||
kCameraPanAbortEvent = 0x2B,
|
||||
kContextLoadCompleteEvent = 0x2C,
|
||||
// TODO: These last 3 events appear as valid event types, but I haven't found
|
||||
// scripts that actually use them. So the names might be wrong.
|
||||
kContextLoadCompleteEvent2 = 0x2D,
|
||||
kContextLoadAbortEvent = 0x2E,
|
||||
kContextLoadFailureEvent = 0x2F,
|
||||
};
|
||||
const char *eventTypeToStr(EventType type);
|
||||
|
||||
enum OperandType {
|
||||
kOperandTypeEmpty = 0x0,
|
||||
kOperandTypeBool = 0x97,
|
||||
kOperandTypeFloat = 0x98,
|
||||
kOperandTypeInt = 0x99,
|
||||
kOperandTypeString = 0x9A,
|
||||
kOperandTypeParamToken = 0x9B,
|
||||
kOperandTypeActorId = 0x9C,
|
||||
kOperandTypeTime = 0x9D,
|
||||
kOperandTypeVariable = 0x9E,
|
||||
kOperandTypeFunctionId = 0x9F,
|
||||
kOperandTypeMethodId = 0xA0,
|
||||
kOperandTypeCollection = 0xA1
|
||||
};
|
||||
const char *operandTypeToStr(OperandType type);
|
||||
|
||||
enum ScriptValueType {
|
||||
kScriptValueTypeEmpty = 0x0,
|
||||
kScriptValueTypeFloat = 0x1,
|
||||
kScriptValueTypeBool = 0x2,
|
||||
kScriptValueTypeTime = 0x3,
|
||||
kScriptValueTypeParamToken = 0x4,
|
||||
kScriptValueTypeActorId = 0x5,
|
||||
kScriptValueTypeString = 0x6,
|
||||
kScriptValueTypeCollection = 0x7,
|
||||
kScriptValueTypeFunctionId = 0x8,
|
||||
kScriptValueTypeMethodId = 0x9
|
||||
};
|
||||
const char *scriptValueTypeToStr(ScriptValueType type);
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
574
engines/mediastation/mediascript/scriptvalue.cpp
Normal file
574
engines/mediastation/mediascript/scriptvalue.cpp
Normal file
@@ -0,0 +1,574 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mediastation/mediascript/scriptvalue.h"
|
||||
#include "mediastation/mediascript/function.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
ScriptValue::ScriptValue(ParameterReadStream *stream) {
|
||||
_type = static_cast<ScriptValueType>(stream->readTypedByte());
|
||||
|
||||
switch (_type) {
|
||||
case kScriptValueTypeEmpty:
|
||||
break;
|
||||
|
||||
case kScriptValueTypeFloat: {
|
||||
double d = stream->readTypedDouble();
|
||||
setToFloat(d);
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeBool: {
|
||||
uint rawValue = stream->readTypedByte();
|
||||
if (rawValue != 0 && rawValue != 1) {
|
||||
error("%s: Got invalid literal bool value %d", __func__, rawValue);
|
||||
}
|
||||
setToBool(rawValue);
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeTime: {
|
||||
double d = stream->readTypedTime();
|
||||
setToFloat(d);
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeParamToken: {
|
||||
uint paramToken = stream->readTypedUint16();
|
||||
setToParamToken(paramToken);
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeActorId: {
|
||||
uint actorId = stream->readTypedUint16();
|
||||
setToActorId(actorId);
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeString: {
|
||||
uint size = stream->readTypedUint16();
|
||||
Common::String string = stream->readString('\0', size);
|
||||
setToString(string);
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeCollection: {
|
||||
uint totalItems = stream->readTypedUint16();
|
||||
Common::SharedPtr<Collection> collection(new Collection);
|
||||
for (uint i = 0; i < totalItems; i++) {
|
||||
ScriptValue collectionValue = ScriptValue(stream);
|
||||
collection->push_back(collectionValue);
|
||||
}
|
||||
setToCollection(collection);
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeFunctionId: {
|
||||
uint functionId = stream->readTypedUint16();
|
||||
setToFunctionId(functionId);
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeMethodId: {
|
||||
BuiltInMethod methodId = static_cast<BuiltInMethod>(stream->readTypedUint16());
|
||||
setToMethodId(methodId);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
error("%s: Got unknown script value type %s", __func__, scriptValueTypeToStr(_type));
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptValue::setToFloat(uint i) {
|
||||
setToFloat(static_cast<double>(i));
|
||||
}
|
||||
|
||||
void ScriptValue::setToFloat(int i) {
|
||||
setToFloat(static_cast<double>(i));
|
||||
}
|
||||
|
||||
void ScriptValue::setToFloat(double d) {
|
||||
_type = kScriptValueTypeFloat;
|
||||
_u.d = d;
|
||||
}
|
||||
|
||||
double ScriptValue::asFloat() const {
|
||||
if (_type == kScriptValueTypeFloat) {
|
||||
return _u.d;
|
||||
} else {
|
||||
issueValueMismatchWarning(kScriptValueTypeFloat);
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptValue::setToBool(bool b) {
|
||||
_type = kScriptValueTypeBool;
|
||||
_u.b = b;
|
||||
}
|
||||
|
||||
bool ScriptValue::asBool() const {
|
||||
if (_type == kScriptValueTypeBool) {
|
||||
return _u.b;
|
||||
} else {
|
||||
issueValueMismatchWarning(kScriptValueTypeBool);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptValue::setToTime(double d) {
|
||||
_type = kScriptValueTypeTime;
|
||||
_u.d = d;
|
||||
}
|
||||
|
||||
double ScriptValue::asTime() const {
|
||||
if (_type == kScriptValueTypeTime) {
|
||||
return _u.d;
|
||||
} else {
|
||||
issueValueMismatchWarning(kScriptValueTypeTime);
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptValue::setToParamToken(uint paramToken) {
|
||||
_type = kScriptValueTypeParamToken;
|
||||
_u.paramToken = paramToken;
|
||||
}
|
||||
|
||||
uint ScriptValue::asParamToken() const {
|
||||
if (_type == kScriptValueTypeParamToken) {
|
||||
return _u.paramToken;
|
||||
} else {
|
||||
issueValueMismatchWarning(kScriptValueTypeParamToken);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptValue::setToActorId(uint actorId) {
|
||||
_type = kScriptValueTypeActorId;
|
||||
_u.actorId = actorId;
|
||||
}
|
||||
|
||||
uint ScriptValue::asActorId() const {
|
||||
if (_type == kScriptValueTypeActorId) {
|
||||
return _u.actorId;
|
||||
} else {
|
||||
issueValueMismatchWarning(kScriptValueTypeActorId);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptValue::setToString(const Common::String &string) {
|
||||
_type = kScriptValueTypeString;
|
||||
_string = string;
|
||||
}
|
||||
|
||||
Common::String ScriptValue::asString() const {
|
||||
if (_type == kScriptValueTypeString) {
|
||||
return _string;
|
||||
} else {
|
||||
return Common::String("");
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptValue::setToCollection(Common::SharedPtr<Collection> collection) {
|
||||
_type = kScriptValueTypeCollection;
|
||||
_collection = collection;
|
||||
}
|
||||
|
||||
Common::SharedPtr<Collection> ScriptValue::asCollection() const {
|
||||
if (_type == kScriptValueTypeCollection) {
|
||||
return _collection;
|
||||
} else {
|
||||
issueValueMismatchWarning(kScriptValueTypeCollection);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptValue::setToFunctionId(uint functionId) {
|
||||
_type = kScriptValueTypeFunctionId;
|
||||
_u.functionId = functionId;
|
||||
}
|
||||
|
||||
uint ScriptValue::asFunctionId() const {
|
||||
if (_type == kScriptValueTypeFunctionId) {
|
||||
return _u.functionId;
|
||||
} else {
|
||||
issueValueMismatchWarning(kScriptValueTypeFunctionId);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptValue::setToMethodId(BuiltInMethod methodId) {
|
||||
_type = kScriptValueTypeMethodId;
|
||||
_u.methodId = methodId;
|
||||
}
|
||||
|
||||
BuiltInMethod ScriptValue::asMethodId() const {
|
||||
if (_type == kScriptValueTypeMethodId) {
|
||||
return _u.methodId;
|
||||
} else {
|
||||
issueValueMismatchWarning(kScriptValueTypeMethodId);
|
||||
return kInvalidMethod;
|
||||
}
|
||||
}
|
||||
|
||||
Common::String ScriptValue::getDebugString() {
|
||||
switch (getType()) {
|
||||
case kScriptValueTypeEmpty:
|
||||
return "empty";
|
||||
|
||||
case kScriptValueTypeFloat:
|
||||
return Common::String::format("float: %f", asFloat());
|
||||
|
||||
case kScriptValueTypeActorId:
|
||||
return Common::String::format("actor: %d", asActorId());
|
||||
|
||||
case kScriptValueTypeTime:
|
||||
return Common::String::format("time: %f", asTime());
|
||||
|
||||
case kScriptValueTypeParamToken:
|
||||
return Common::String::format("token: %d", asParamToken());
|
||||
|
||||
default:
|
||||
return Common::String::format("arg type %s", scriptValueTypeToStr(getType()));
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValue::compare(Opcode op, const ScriptValue &lhs, const ScriptValue &rhs) {
|
||||
if (lhs.getType() != rhs.getType()) {
|
||||
warning("%s: Attempt to compare mismatched types %s and %s", __func__, scriptValueTypeToStr(lhs.getType()), scriptValueTypeToStr(rhs.getType()));
|
||||
}
|
||||
|
||||
switch (lhs.getType()) {
|
||||
case kScriptValueTypeEmpty:
|
||||
return compareEmptyValues(op);
|
||||
|
||||
case kScriptValueTypeFloat:
|
||||
return compare(op, lhs.asFloat(), rhs.asFloat());
|
||||
break;
|
||||
|
||||
case kScriptValueTypeBool:
|
||||
return compare(op, lhs.asBool(), rhs.asBool());
|
||||
break;
|
||||
|
||||
case kScriptValueTypeTime:
|
||||
return compare(op, lhs.asTime(), rhs.asTime());
|
||||
break;
|
||||
|
||||
case kScriptValueTypeParamToken:
|
||||
return compare(op, lhs.asParamToken(), rhs.asParamToken());
|
||||
break;
|
||||
|
||||
case kScriptValueTypeActorId:
|
||||
return compare(op, lhs.asActorId(), rhs.asActorId());
|
||||
break;
|
||||
|
||||
case kScriptValueTypeString:
|
||||
return compareStrings(op, lhs.asString(), rhs.asString());
|
||||
break;
|
||||
|
||||
case kScriptValueTypeCollection:
|
||||
return compare(op, lhs.asCollection(), rhs.asCollection());
|
||||
break;
|
||||
|
||||
case kScriptValueTypeFunctionId:
|
||||
return compare(op, lhs.asFunctionId(), rhs.asFunctionId());
|
||||
break;
|
||||
|
||||
case kScriptValueTypeMethodId:
|
||||
return compare(op, static_cast<uint>(lhs.asMethodId()), static_cast<uint>(rhs.asMethodId()));
|
||||
break;
|
||||
|
||||
default:
|
||||
error("%s: Got unknown script value type %d", __func__, lhs.getType());
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValue::compareEmptyValues(Opcode op) {
|
||||
// Empty values are considered equal.
|
||||
switch (op) {
|
||||
case kOpcodeEquals:
|
||||
return true;
|
||||
|
||||
case kOpcodeNotEquals:
|
||||
return false;
|
||||
|
||||
default:
|
||||
warning("%s: Got invalid empty value operation %s", __func__, opcodeToStr(op));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValue::compareStrings(Opcode op, const Common::String &left, const Common::String &right) {
|
||||
switch (op) {
|
||||
case kOpcodeEquals:
|
||||
return (left == right);
|
||||
|
||||
case kOpcodeNotEquals:
|
||||
return (left != right);
|
||||
|
||||
case kOpcodeLessThan:
|
||||
return (left < right);
|
||||
|
||||
case kOpcodeGreaterThan:
|
||||
return (left > right);
|
||||
|
||||
case kOpcodeLessThanOrEqualTo:
|
||||
return (left <= right);
|
||||
|
||||
case kOpcodeGreaterThanOrEqualTo:
|
||||
return (left >= right);
|
||||
|
||||
default:
|
||||
error("%s: Got invalid string operation %s", __func__, opcodeToStr(op));
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValue::compare(Opcode op, uint left, uint right) {
|
||||
switch (op) {
|
||||
case kOpcodeEquals:
|
||||
return (left == right);
|
||||
|
||||
case kOpcodeNotEquals:
|
||||
return (left != right);
|
||||
|
||||
default:
|
||||
error("%s: Got invalid param token operation %s", __func__, opcodeToStr(op));
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValue::compare(Opcode op, bool left, bool right) {
|
||||
switch (op) {
|
||||
case kOpcodeEquals:
|
||||
return (left == right);
|
||||
|
||||
case kOpcodeNotEquals:
|
||||
return (left != right);
|
||||
|
||||
default:
|
||||
error("%s: Got invalid bool operation %s", __func__, opcodeToStr(op));
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValue::compare(Opcode op, double left, double right) {
|
||||
switch (op) {
|
||||
case kOpcodeEquals:
|
||||
return (left == right);
|
||||
|
||||
case kOpcodeNotEquals:
|
||||
return (left != right);
|
||||
|
||||
case kOpcodeLessThan:
|
||||
return (left < right);
|
||||
|
||||
case kOpcodeGreaterThan:
|
||||
return (left > right);
|
||||
|
||||
case kOpcodeLessThanOrEqualTo:
|
||||
return (left <= right);
|
||||
|
||||
case kOpcodeGreaterThanOrEqualTo:
|
||||
return (left >= right);
|
||||
|
||||
default:
|
||||
error("%s: Got invalid float operation %s", __func__, opcodeToStr(op));
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValue::compare(Opcode op, Common::SharedPtr<Collection> left, Common::SharedPtr<Collection> right) {
|
||||
switch (op) {
|
||||
case kOpcodeEquals:
|
||||
return (left == right);
|
||||
|
||||
case kOpcodeNotEquals:
|
||||
return (left != right);
|
||||
|
||||
default:
|
||||
error("%s: Got invalid collection operation %s", __func__, opcodeToStr(op));
|
||||
}
|
||||
}
|
||||
|
||||
ScriptValue ScriptValue::evalMathOperation(Opcode op, const ScriptValue &left, const ScriptValue &right) {
|
||||
ScriptValue returnValue;
|
||||
double result = 0.0;
|
||||
|
||||
switch (left.getType()) {
|
||||
case kScriptValueTypeFloat: {
|
||||
if (right.getType() == kScriptValueTypeTime) {
|
||||
result = binaryMathOperation(op, left.asFloat(), right.asTime());
|
||||
} else if (right.getType() == kScriptValueTypeFloat) {
|
||||
result = binaryMathOperation(op, left.asFloat(), right.asFloat());
|
||||
} else {
|
||||
error("%s: Attempted to do math operation on unsupported value type %s", __func__, scriptValueTypeToStr(right.getType()));
|
||||
}
|
||||
returnValue.setToFloat(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeTime: {
|
||||
if (right.getType() == kScriptValueTypeTime) {
|
||||
result = binaryMathOperation(op, left.asTime(), right.asTime());
|
||||
} else if (right.getType() == kScriptValueTypeFloat) {
|
||||
result = binaryMathOperation(op, left.asTime(), right.asFloat());
|
||||
} else {
|
||||
error("%s: Attempted to do math operation on unsupported value type %s", __func__, scriptValueTypeToStr(right.getType()));
|
||||
}
|
||||
returnValue.setToFloat(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case kScriptValueTypeString: {
|
||||
returnValue.setToString(left.asString() + right.asString());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
error("%s: Attempted to do math operation on unsupported value type %s", __func__, scriptValueTypeToStr(right.getType()));
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
double ScriptValue::binaryMathOperation(Opcode op, double left, double right) {
|
||||
switch (op) {
|
||||
case kOpcodeAdd:
|
||||
return left + right;
|
||||
|
||||
case kOpcodeSubtract:
|
||||
return left - right;
|
||||
|
||||
case kOpcodeMultiply:
|
||||
return left * right;
|
||||
|
||||
case kOpcodeDivide:
|
||||
if (right != 0.0) {
|
||||
return left / right;
|
||||
} else {
|
||||
error("%s: Division by zero", __func__);
|
||||
}
|
||||
|
||||
case kOpcodeModulo:
|
||||
if (right != 0.0) {
|
||||
return fmod(left, right);
|
||||
} else {
|
||||
error("%s: Division by zero", __func__);
|
||||
}
|
||||
|
||||
default:
|
||||
error("%s: Got unvalid binary math operation %s", __func__, opcodeToStr(op));
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptValue::operator==(const ScriptValue &other) const {
|
||||
return compare(kOpcodeEquals, *this, other);
|
||||
}
|
||||
|
||||
bool ScriptValue::operator!=(const ScriptValue &other) const {
|
||||
return compare(kOpcodeNotEquals, *this, other);
|
||||
}
|
||||
|
||||
bool ScriptValue::operator<(const ScriptValue &other) const {
|
||||
return compare(kOpcodeLessThan, *this, other);
|
||||
}
|
||||
|
||||
bool ScriptValue::operator>(const ScriptValue &other) const {
|
||||
return compare(kOpcodeGreaterThan, *this, other);
|
||||
}
|
||||
|
||||
bool ScriptValue::operator<=(const ScriptValue &other) const {
|
||||
return compare(kOpcodeLessThanOrEqualTo, *this, other);
|
||||
}
|
||||
|
||||
bool ScriptValue::operator>=(const ScriptValue &other) const {
|
||||
return compare(kOpcodeGreaterThanOrEqualTo, *this, other);
|
||||
}
|
||||
|
||||
bool ScriptValue::operator||(const ScriptValue &other) const {
|
||||
if (getType() != kScriptValueTypeBool || other.getType() != kScriptValueTypeBool) {
|
||||
error("%s: Expected bools for binary comparison, got %s and %s", __func__, scriptValueTypeToStr(getType()), scriptValueTypeToStr(other.getType()));
|
||||
}
|
||||
|
||||
return asBool() || other.asBool();
|
||||
}
|
||||
|
||||
bool ScriptValue::operator^(const ScriptValue &other) const {
|
||||
if (getType() != kScriptValueTypeBool || other.getType() != kScriptValueTypeBool) {
|
||||
error("%s: Expected bools for binary comparison, got %s and %s", __func__, scriptValueTypeToStr(getType()), scriptValueTypeToStr(other.getType()));
|
||||
}
|
||||
|
||||
return asBool() ^ other.asBool();
|
||||
}
|
||||
|
||||
bool ScriptValue::operator&&(const ScriptValue &other) const {
|
||||
if (getType() != kScriptValueTypeBool || other.getType() != kScriptValueTypeBool) {
|
||||
error("%s: Expected bools for binary comparison, got %s and %s", __func__, scriptValueTypeToStr(getType()), scriptValueTypeToStr(other.getType()));
|
||||
}
|
||||
|
||||
return asBool() && other.asBool();
|
||||
}
|
||||
|
||||
ScriptValue ScriptValue::operator+(const ScriptValue &other) const {
|
||||
return evalMathOperation(kOpcodeAdd, *this, other);
|
||||
}
|
||||
|
||||
ScriptValue ScriptValue::operator-(const ScriptValue &other) const {
|
||||
return evalMathOperation(kOpcodeSubtract, *this, other);
|
||||
}
|
||||
|
||||
ScriptValue ScriptValue::operator*(const ScriptValue &other) const {
|
||||
return evalMathOperation(kOpcodeMultiply, *this, other);
|
||||
}
|
||||
|
||||
ScriptValue ScriptValue::operator/(const ScriptValue &other) const {
|
||||
return evalMathOperation(kOpcodeDivide, *this, other);
|
||||
}
|
||||
|
||||
ScriptValue ScriptValue::operator%(const ScriptValue &other) const {
|
||||
return evalMathOperation(kOpcodeModulo, *this, other);
|
||||
}
|
||||
|
||||
ScriptValue ScriptValue::operator-() const {
|
||||
ScriptValue returnValue;
|
||||
switch (getType()) {
|
||||
case kScriptValueTypeFloat:
|
||||
returnValue.setToFloat(-asFloat());
|
||||
break;
|
||||
|
||||
case kScriptValueTypeTime:
|
||||
returnValue.setToTime(-asTime());
|
||||
break;
|
||||
|
||||
default:
|
||||
error("%s: Attempted to negate type %s", __func__, scriptValueTypeToStr(getType()));
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void ScriptValue::issueValueMismatchWarning(ScriptValueType expectedType) const {
|
||||
// The original just blithely returns 0 (or equivalent) when you call a
|
||||
// getter for the wrong type (for instance, calling asFloat() on a bool),
|
||||
// but for debugging purposes we'll issue a warning.
|
||||
warning("%s: Script value type mismatch: Expected %s, got %s", __func__, scriptValueTypeToStr(expectedType), scriptValueTypeToStr(_type));
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
122
engines/mediastation/mediascript/scriptvalue.h
Normal file
122
engines/mediastation/mediascript/scriptvalue.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEDIASTATION_MEDIASCRIPT_SCRIPTVALUE_H
|
||||
#define MEDIASTATION_MEDIASCRIPT_SCRIPTVALUE_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/str.h"
|
||||
|
||||
#include "mediastation/datafile.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
#include "mediastation/mediascript/collection.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
class Actor;
|
||||
|
||||
class ScriptValue {
|
||||
public:
|
||||
ScriptValue() : _type(kScriptValueTypeEmpty) {}
|
||||
ScriptValue(ParameterReadStream *stream);
|
||||
|
||||
ScriptValueType getType() const { return _type; }
|
||||
|
||||
void setToFloat(uint i);
|
||||
void setToFloat(int i);
|
||||
void setToFloat(double d);
|
||||
double asFloat() const;
|
||||
int asIntFromFloat() const;
|
||||
|
||||
void setToBool(bool b);
|
||||
bool asBool() const;
|
||||
|
||||
void setToTime(double d);
|
||||
double asTime() const;
|
||||
|
||||
void setToParamToken(uint paramToken);
|
||||
uint asParamToken() const;
|
||||
|
||||
void setToActorId(uint actorId);
|
||||
uint asActorId() const;
|
||||
|
||||
void setToString(const Common::String &string);
|
||||
Common::String asString() const;
|
||||
|
||||
void setToCollection(Common::SharedPtr<Collection> collection);
|
||||
Common::SharedPtr<Collection> asCollection() const;
|
||||
|
||||
void setToFunctionId(uint functionId);
|
||||
uint asFunctionId() const;
|
||||
|
||||
void setToMethodId(BuiltInMethod methodId);
|
||||
BuiltInMethod asMethodId() const;
|
||||
|
||||
Common::String getDebugString();
|
||||
|
||||
bool operator==(const ScriptValue &other) const;
|
||||
bool operator!=(const ScriptValue &other) const;
|
||||
bool operator<(const ScriptValue &other) const;
|
||||
bool operator>(const ScriptValue &other) const;
|
||||
bool operator<=(const ScriptValue &other) const;
|
||||
bool operator>=(const ScriptValue &other) const;
|
||||
|
||||
bool operator||(const ScriptValue &other) const;
|
||||
bool operator^(const ScriptValue &other) const;
|
||||
bool operator&&(const ScriptValue &other) const;
|
||||
|
||||
ScriptValue operator+(const ScriptValue &other) const;
|
||||
ScriptValue operator-(const ScriptValue &other) const;
|
||||
ScriptValue operator*(const ScriptValue &other) const;
|
||||
ScriptValue operator/(const ScriptValue &other) const;
|
||||
ScriptValue operator%(const ScriptValue &other) const;
|
||||
ScriptValue operator-() const;
|
||||
|
||||
private:
|
||||
ScriptValueType _type = kScriptValueTypeEmpty;
|
||||
union {
|
||||
double d = 0;
|
||||
bool b;
|
||||
uint paramToken;
|
||||
uint actorId;
|
||||
uint functionId;
|
||||
BuiltInMethod methodId;
|
||||
} _u;
|
||||
Common::String _string;
|
||||
Common::SharedPtr<Collection> _collection;
|
||||
|
||||
static bool compare(Opcode op, const ScriptValue &left, const ScriptValue &right);
|
||||
static bool compareEmptyValues(Opcode op);
|
||||
static bool compareStrings(Opcode op, const Common::String &left, const Common::String &right);
|
||||
static bool compare(Opcode op, uint left, uint right);
|
||||
static bool compare(Opcode op, bool left, bool right);
|
||||
static bool compare(Opcode op, double left, double right);
|
||||
static bool compare(Opcode op, Common::SharedPtr<Collection> left, Common::SharedPtr<Collection> right);
|
||||
|
||||
static ScriptValue evalMathOperation(Opcode op, const ScriptValue &left, const ScriptValue &right);
|
||||
static double binaryMathOperation(Opcode op, double left, double right);
|
||||
|
||||
void issueValueMismatchWarning(ScriptValueType actualType) const;
|
||||
};
|
||||
|
||||
} // End of namespace MediaStation
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user