/* 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 .
*
*/
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/function.h"
namespace MediaStation {
ScriptValue::ScriptValue(ParameterReadStream *stream) {
_type = static_cast(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(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(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(i));
}
void ScriptValue::setToFloat(int i) {
setToFloat(static_cast(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) {
_type = kScriptValueTypeCollection;
_collection = collection;
}
Common::SharedPtr 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(lhs.asMethodId()), static_cast(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 left, Common::SharedPtr 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