/* 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 "mtropolis/runtime.h"
#include "mtropolis/data.h"
#include "mtropolis/vthread.h"
#include "mtropolis/asset_factory.h"
#include "mtropolis/element_factory.h"
#include "mtropolis/miniscript.h"
#include "mtropolis/modifier_factory.h"
#include "mtropolis/modifiers.h"
#include "mtropolis/render.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/random.h"
#include "common/substream.h"
#include "common/system.h"
#include "graphics/cursorman.h"
#include "graphics/managed_surface.h"
#include "graphics/surface.h"
#include "graphics/wincursor.h"
#include "graphics/maccursor.h"
#include "graphics/macgui/macfontmanager.h"
#include "audio/mixer.h"
namespace MTropolis {
class MainWindow : public Window {
public:
MainWindow(const WindowParameters &windowParams);
void onMouseDown(int32 x, int32 y, int mouseButton) override;
void onMouseMove(int32 x, int32 y) override;
void onMouseUp(int32 x, int32 y, int mouseButton) override;
void onKeyboardEvent(const Common::EventType evtType, bool repeat, const Common::KeyState &keyEvt) override;
private:
bool _mouseButtonStates[Actions::kMouseButtonCount];
};
MainWindow::MainWindow(const WindowParameters &windowParams) : Window(windowParams) {
for (int i = 0; i < Actions::kMouseButtonCount; i++)
_mouseButtonStates[i] = false;
}
void MainWindow::onMouseDown(int32 x, int32 y, int mouseButton) {
if (!_mouseButtonStates[mouseButton]) {
_mouseButtonStates[mouseButton] = true;
if (mouseButton == Actions::kMouseButtonLeft) {
_runtime->queueOSEvent(Common::SharedPtr(new MouseInputEvent(kOSEventTypeMouseDown, x, y, static_cast(mouseButton))));
}
}
}
void MainWindow::onMouseMove(int32 x, int32 y) {
_runtime->queueOSEvent(Common::SharedPtr(new MouseInputEvent(kOSEventTypeMouseMove, x, y, Actions::kMouseButtonLeft)));
}
void MainWindow::onMouseUp(int32 x, int32 y, int mouseButton) {
if (_mouseButtonStates[mouseButton]) {
_mouseButtonStates[mouseButton] = false;
if (mouseButton == Actions::kMouseButtonLeft) {
_runtime->queueOSEvent(Common::SharedPtr(new MouseInputEvent(kOSEventTypeMouseUp, x, y, static_cast(mouseButton))));
}
}
}
void MainWindow::onKeyboardEvent(const Common::EventType evtType, bool repeat, const Common::KeyState &keyEvt) {
_runtime->queueOSEvent(Common::SharedPtr(new KeyboardInputEvent(kOSEventTypeKeyboard, evtType, repeat, keyEvt)));
}
class ModifierInnerScopeBuilder : public IStructuralReferenceVisitor {
public:
ModifierInnerScopeBuilder(ObjectLinkingScope *scope);
void visitChildStructuralRef(Common::SharedPtr &structural) override;
void visitChildModifierRef(Common::SharedPtr &modifier) override;
void visitWeakStructuralRef(Common::WeakPtr &structural) override;
void visitWeakModifierRef(Common::WeakPtr &modifier) override;
private:
ObjectLinkingScope *_scope;
};
ModifierInnerScopeBuilder::ModifierInnerScopeBuilder(ObjectLinkingScope *scope) : _scope(scope) {
}
void ModifierInnerScopeBuilder::visitChildStructuralRef(Common::SharedPtr &structural) {
assert(false);
}
void ModifierInnerScopeBuilder::visitChildModifierRef(Common::SharedPtr &modifier) {
_scope->addObject(modifier->getStaticGUID(), modifier->getName(), modifier);
}
void ModifierInnerScopeBuilder::visitWeakStructuralRef(Common::WeakPtr &structural) {
}
void ModifierInnerScopeBuilder::visitWeakModifierRef(Common::WeakPtr &modifier) {
// Do nothing
}
class ModifierChildMaterializer : public IStructuralReferenceVisitor {
public:
ModifierChildMaterializer(Runtime *runtime, Modifier *modifier, ObjectLinkingScope *outerScope);
void visitChildStructuralRef(Common::SharedPtr &structural) override;
void visitChildModifierRef(Common::SharedPtr &modifier) override;
void visitWeakStructuralRef(Common::WeakPtr &structural) override;
void visitWeakModifierRef(Common::WeakPtr &modifier) override;
private:
Runtime *_runtime;
Modifier *_modifier;
ObjectLinkingScope *_outerScope;
};
class ModifierChildCloner : public IStructuralReferenceVisitor {
public:
ModifierChildCloner(Runtime *runtime, const Common::WeakPtr &relinkParent);
void visitChildStructuralRef(Common::SharedPtr &structural) override;
void visitChildModifierRef(Common::SharedPtr &modifier) override;
void visitWeakStructuralRef(Common::WeakPtr &structural) override;
void visitWeakModifierRef(Common::WeakPtr &modifier) override;
private:
Runtime *_runtime;
Common::WeakPtr _relinkParent;
};
ModifierChildMaterializer::ModifierChildMaterializer(Runtime *runtime, Modifier *modifier, ObjectLinkingScope *outerScope)
: _runtime(runtime), _modifier(modifier), _outerScope(outerScope) {
}
void ModifierChildMaterializer::visitChildStructuralRef(Common::SharedPtr &structural) {
assert(false);
}
void ModifierChildMaterializer::visitChildModifierRef(Common::SharedPtr &modifier) {
_runtime->instantiateIfAlias(modifier, _modifier->getSelfReference());
modifier->materialize(_runtime, _outerScope);
}
void ModifierChildMaterializer::visitWeakStructuralRef(Common::WeakPtr &structural) {
// Do nothing
}
void ModifierChildMaterializer::visitWeakModifierRef(Common::WeakPtr &modifier) {
// Do nothing
}
ModifierChildCloner::ModifierChildCloner(Runtime *runtime, const Common::WeakPtr &relinkParent)
: _runtime(runtime), _relinkParent(relinkParent) {
}
void ModifierChildCloner::visitChildStructuralRef(Common::SharedPtr &structural) {
assert(false);
}
void ModifierChildCloner::visitChildModifierRef(Common::SharedPtr &modifier) {
modifier = modifier->shallowClone();
modifier->setSelfReference(modifier);
modifier->setParent(_relinkParent);
ModifierChildCloner recursiveCloner(_runtime, modifier);
modifier->visitInternalReferences(&recursiveCloner);
}
void ModifierChildCloner::visitWeakStructuralRef(Common::WeakPtr &structural) {
// Do nothing
}
void ModifierChildCloner::visitWeakModifierRef(Common::WeakPtr &modifier) {
// Do nothing
}
char invariantToLower(char c) {
if (c >= 'A' && c <= 'Z')
return static_cast(c - 'A' + 'a');
return c;
}
Common::String toCaseInsensitive(const Common::String &str) {
uint strLen = str.size();
if (strLen == 0)
return str;
// TODO: Figure out how this is supposed to behave with respect to non-ASCII characters
Common::Array lowered;
lowered.resize(strLen);
for (uint i = 0; i < strLen; i++)
lowered[i] = invariantToLower(str[i]);
return Common::String(&lowered[0], strLen);
}
bool caseInsensitiveEqual(const Common::String& str1, const Common::String& str2) {
size_t length1 = str1.size();
size_t length2 = str2.size();
if (length1 != length2)
return false;
for (size_t i = 0; i < length1; i++) {
if (invariantToLower(str1[i]) != invariantToLower(str2[i]))
return false;
}
return true;
}
bool EventIDs::isCommand(EventID eventID) {
switch (eventID) {
case kElementShow:
case kElementHide:
case kElementSelect:
case kElementDeselect:
case kElementToggleSelect:
case kElementEnableEdit:
case kElementDisableEdit:
case kElementUpdatedCalculated:
case kElementScrollUp:
case kElementScrollDown:
case kElementScrollLeft:
case kElementScrollRight:
case kPreloadMedia:
case kFlushMedia:
case kPrerollMedia:
case kClone:
case kKill:
case kPlay:
case kStop:
case kPause:
case kUnpause:
case kTogglePause:
case kCloseProject:
case kFlushAllMedia:
case kAttribGet:
case kAttribSet:
return true;
default:
return false;
}
}
bool Point16::load(const Data::Point &point) {
x = point.x;
y = point.y;
return true;
}
MiniscriptInstructionOutcome Point16::refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib) {
if (attrib == "x") {
DynamicValueWriteIntegerHelper::create(&x, proxy);
return kMiniscriptInstructionOutcomeContinue;
}
if (attrib == "y") {
DynamicValueWriteIntegerHelper::create(&y, proxy);
return kMiniscriptInstructionOutcomeContinue;
}
return kMiniscriptInstructionOutcomeFailed;
}
Common::String Point16::toString() const {
return Common::String::format("(%i,%i)", x, y);
}
bool Rect16::load(const Data::Rect &rect) {
top = rect.top;
left = rect.left;
bottom = rect.bottom;
right = rect.right;
if (bottom < top || right < left)
return false;
return true;
}
bool Rect16::loadUnchecked(const Data::Rect &rect) {
top = rect.top;
left = rect.left;
bottom = rect.bottom;
right = rect.right;
return true;
}
Common::Rect Rect16::toScummvmRect() const {
return Common::Rect(left, top, right, bottom);
}
bool IntRange::load(const Data::IntRange &range) {
max = range.max;
min = range.min;
return true;
}
MiniscriptInstructionOutcome IntRange::refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib) {
if (attrib == "start") {
DynamicValueWriteIntegerHelper::create(&min, proxy);
return kMiniscriptInstructionOutcomeContinue;
}
if (attrib == "end") {
DynamicValueWriteIntegerHelper::create(&max, proxy);
return kMiniscriptInstructionOutcomeContinue;
}
thread->error("Couldn't reference int range attribute '" + attrib + "'");
return kMiniscriptInstructionOutcomeFailed;
}
Common::String IntRange::toString() const {
return Common::String::format("(%i thru %i)", min, max);
}
bool Label::load(const Data::Label &label) {
id = label.labelID;
superGroupID = label.superGroupID;
return true;
}
bool ColorRGB8::load(const Data::ColorRGB16 &color) {
this->r = (color.red * 510 + 1) / 131070;
this->g = (color.green * 510 + 1) / 131070;
this->b = (color.blue * 510 + 1) / 131070;
return true;
}
ColorRGB8 ColorRGB8::create(uint8 r, uint8 g, uint8 b) {
ColorRGB8 result;
result.r = r;
result.g = g;
result.b = b;
return result;
}
MessageFlags::MessageFlags() : relay(true), cascade(true), immediate(true) {
}
DynamicListContainerBase::~DynamicListContainerBase() {
}
void DynamicListDefaultSetter::defaultSet(int32 &value) {
value = 0;
}
void DynamicListDefaultSetter::defaultSet(double &value) {
value = 0.0;
}
void DynamicListDefaultSetter::defaultSet(Point16 &value) {
value.x = 0;
value.y = 0;
}
void DynamicListDefaultSetter::defaultSet(IntRange &value) {
value.min = 0;
value.max = 0;
}
void DynamicListDefaultSetter::defaultSet(bool &value) {
value = false;
}
void DynamicListDefaultSetter::defaultSet(AngleMagVector &value) {
value.angleDegrees = 0.0;
value.magnitude = 0.0;
}
void DynamicListDefaultSetter::defaultSet(Label &value) {
value.id = 0;
value.superGroupID = 0;
}
void DynamicListDefaultSetter::defaultSet(Event &value) {
value.eventType = EventIDs::EventID::kNothing;
value.eventInfo = 0;
}
void DynamicListDefaultSetter::defaultSet(Common::String &value) {
}
void DynamicListDefaultSetter::defaultSet(Common::SharedPtr &value) {
}
void DynamicListDefaultSetter::defaultSet(ObjectReference &value) {
}
bool DynamicListValueImporter::importValue(const DynamicValue &dynValue, const int32 *&outPtr) {
if (dynValue.getType() != DynamicValueTypes::kInteger)
return false;
outPtr = &dynValue.getInt();
return true;
}
bool DynamicListValueImporter::importValue(const DynamicValue &dynValue, const double *&outPtr) {
if (dynValue.getType() != DynamicValueTypes::kFloat)
return false;
outPtr = &dynValue.getFloat();
return true;
}
bool DynamicListValueImporter::importValue(const DynamicValue &dynValue, const Point16 *&outPtr) {
if (dynValue.getType() != DynamicValueTypes::kPoint)
return false;
outPtr = &dynValue.getPoint();
return true;
}
bool DynamicListValueImporter::importValue(const DynamicValue &dynValue, const IntRange *&outPtr) {
if (dynValue.getType() != DynamicValueTypes::kIntegerRange)
return false;
outPtr = &dynValue.getIntRange();
return true;
}
bool DynamicListValueImporter::importValue(const DynamicValue &dynValue, const bool *&outPtr) {
if (dynValue.getType() != DynamicValueTypes::kBoolean)
return false;
outPtr = &dynValue.getBool();
return true;
}
bool DynamicListValueImporter::importValue(const DynamicValue &dynValue, const AngleMagVector *&outPtr) {
if (dynValue.getType() != DynamicValueTypes::kVector)
return false;
outPtr = &dynValue.getVector();
return true;
}
bool DynamicListValueImporter::importValue(const DynamicValue &dynValue, const Label *&outPtr) {
if (dynValue.getType() != DynamicValueTypes::kLabel)
return false;
outPtr = &dynValue.getLabel();
return true;
}
bool DynamicListValueImporter::importValue(const DynamicValue &dynValue, const Event *&outPtr) {
if (dynValue.getType() != DynamicValueTypes::kEvent)
return false;
outPtr = &dynValue.getEvent();
return true;
}
bool DynamicListValueImporter::importValue(const DynamicValue &dynValue, const Common::String *&outPtr) {
if (dynValue.getType() != DynamicValueTypes::kString)
return false;
outPtr = &dynValue.getString();
return true;
}
bool DynamicListValueImporter::importValue(const DynamicValue &dynValue, const Common::SharedPtr *&outPtr) {
if (dynValue.getType() != DynamicValueTypes::kList)
return false;
outPtr = &dynValue.getList();
return true;
}
bool DynamicListValueImporter::importValue(const DynamicValue &dynValue, const ObjectReference *&outPtr) {
if (dynValue.getType() != DynamicValueTypes::kObject)
return false;
outPtr = &dynValue.getObject();
return true;
}
void DynamicListValueExporter::exportValue(DynamicValue &dynValue, const int32 &value) {
dynValue.setInt(value);
}
void DynamicListValueExporter::exportValue(DynamicValue &dynValue, const double &value) {
dynValue.setFloat(value);
}
void DynamicListValueExporter::exportValue(DynamicValue &dynValue, const Point16 &value) {
dynValue.setPoint(value);
}
void DynamicListValueExporter::exportValue(DynamicValue &dynValue, const IntRange &value) {
dynValue.setIntRange(value);
}
void DynamicListValueExporter::exportValue(DynamicValue &dynValue, const bool &value) {
dynValue.setBool(value);
}
void DynamicListValueExporter::exportValue(DynamicValue &dynValue, const AngleMagVector &value) {
dynValue.setVector(value);
}
void DynamicListValueExporter::exportValue(DynamicValue &dynValue, const Label &value) {
dynValue.setLabel(value);
}
void DynamicListValueExporter::exportValue(DynamicValue &dynValue, const Event &value) {
dynValue.setEvent(value);
}
void DynamicListValueExporter::exportValue(DynamicValue &dynValue, const Common::String &value) {
dynValue.setString(value);
}
void DynamicListValueExporter::exportValue(DynamicValue &dynValue, const Common::SharedPtr &value) {
dynValue.setList(value);
}
void DynamicListValueExporter::exportValue(DynamicValue &dynValue, const ObjectReference &value) {
dynValue.setObject(value);
}
DynamicListContainer::DynamicListContainer() : _size(0) {
}
bool DynamicListContainer::setAtIndex(size_t index, const DynamicValue &dynValue) {
return true;
}
void DynamicListContainer::truncateToSize(size_t sz) {
}
bool DynamicListContainer::expandToMinimumSize(size_t sz) {
return false;
}
bool DynamicListContainer::getAtIndex(size_t index, DynamicValue &dynValue) const {
dynValue.clear();
return true;
}
void DynamicListContainer::setFrom(const DynamicListContainerBase &other) {
_size = other.getSize(); // ... the only thing we have anyway...
}
const void *DynamicListContainer::getConstArrayPtr() const {
return nullptr;
}
void *DynamicListContainer::getArrayPtr() {
return nullptr;
}
size_t DynamicListContainer::getSize() const {
return _size;
}
bool DynamicListContainer::compareEqual(const DynamicListContainerBase &other) const {
return true;
}
DynamicListContainerBase *DynamicListContainer::clone() const {
return new DynamicListContainer(*this);
}
bool DynamicListContainer::setAtIndex(size_t index, const DynamicValue &dynValue) {
if (dynValue.getType() != DynamicValueTypes::kVariableReference)
return false;
size_t requiredSize = index + 1;
if (_array.size() < requiredSize) {
size_t prevSize = _array.size();
_array.resize(requiredSize);
_strings.resize(requiredSize);
for (size_t i = prevSize; i < index; i++) {
_array[i].guid = 0;
}
const VarReference &varRef = dynValue.getVarReference();
_array[index].guid = varRef.guid;
_strings[index] = *varRef.source;
rebuildStringPointers();
} else {
const VarReference &varRef = dynValue.getVarReference();
_array[index].guid = varRef.guid;
_strings[index] = *varRef.source;
}
return true;
}
void DynamicListContainer::truncateToSize(size_t sz) {
if (_array.size() > sz)
_array.resize(sz);
}
bool DynamicListContainer::expandToMinimumSize(size_t sz) {
if (_array.size() < sz) {
size_t prevSize = _array.size();
_array.resize(sz);
_strings.resize(sz);
for (size_t i = prevSize; i < sz; prevSize++) {
_array[i].guid = 0;
_array[i].source = nullptr;
}
}
return true;
}
bool DynamicListContainer::getAtIndex(size_t index, DynamicValue &dynValue) const {
// TODO: Refactor this whole thing to use linkInternalReferences
if (index >= _array.size())
return false;
assert(false);
return false;
}
void DynamicListContainer::setFrom(const DynamicListContainerBase &other) {
const DynamicListContainer &otherTyped = static_cast &>(other);
_array = otherTyped._array;
_strings = otherTyped._strings;
rebuildStringPointers();
}
const void *DynamicListContainer::getConstArrayPtr() const {
return &_array;
}
void *DynamicListContainer::getArrayPtr() {
return &_array;
}
size_t DynamicListContainer::getSize() const {
return _array.size();
}
bool DynamicListContainer::compareEqual(const DynamicListContainerBase &other) const {
const DynamicListContainer &otherTyped = static_cast &>(other);
return _array == otherTyped._array;
}
DynamicListContainerBase *DynamicListContainer::clone() const {
return new DynamicListContainer(*this);
}
void DynamicListContainer::rebuildStringPointers() {
assert(_strings.size() == _array.size());
size_t numStrings = _array.size();
for (size_t i = 0; i < numStrings; i++) {
_array[i].source = &_strings[i];
}
}
DynamicList::DynamicList() : _type(DynamicValueTypes::kEmpty), _container(nullptr) {
}
DynamicList::DynamicList(const DynamicList &other) : _type(DynamicValueTypes::kEmpty), _container(nullptr) {
initFromOther(other);
}
DynamicList::~DynamicList() {
clear();
}
DynamicValueTypes::DynamicValueType DynamicList::getType() const {
return _type;
}
const Common::Array &DynamicList::getInt() const {
assert(_type == DynamicValueTypes::kInteger);
return *static_cast *>(_container->getConstArrayPtr());
}
const Common::Array &DynamicList::getFloat() const {
assert(_type == DynamicValueTypes::kFloat);
return *static_cast *>(_container->getConstArrayPtr());
}
const Common::Array &DynamicList::getPoint() const {
assert(_type == DynamicValueTypes::kPoint);
return *static_cast *>(_container->getConstArrayPtr());
}
const Common::Array &DynamicList::getIntRange() const {
assert(_type == DynamicValueTypes::kIntegerRange);
return *static_cast *>(_container->getConstArrayPtr());
}
const Common::Array &DynamicList::getVector() const {
assert(_type == DynamicValueTypes::kVector);
return *static_cast *>(_container->getConstArrayPtr());
}
const Common::Array