Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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