/* 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 "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" #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" 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; } size_t caseInsensitiveFind(const Common::String &strToSearch, const Common::String &stringToFind) { if (stringToFind.size() > strToSearch.size()) return Common::String::npos; size_t lastValidStart = strToSearch.size() - stringToFind.size(); size_t searchLength = stringToFind.size(); for (size_t startIndex = 0; startIndex <= lastValidStart; startIndex++) { bool matches = true; for (size_t i = 0; i < searchLength; i++) { char ca = strToSearch[i + startIndex]; char cb = stringToFind[i]; if (ca != cb && invariantToLower(ca) != invariantToLower(cb)) { matches = false; break; } } if (matches) return startIndex; } return Common::String::npos; } 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; } } MiniscriptInstructionOutcome pointWriteRefAttrib(Common::Point &point, MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib) { if (attrib == "x") { DynamicValueWriteIntegerHelper::create(&point.x, proxy); return kMiniscriptInstructionOutcomeContinue; } if (attrib == "y") { DynamicValueWriteIntegerHelper::create(&point.y, proxy); return kMiniscriptInstructionOutcomeContinue; } return kMiniscriptInstructionOutcomeFailed; } Common::String pointToString(const Common::Point &point) { return Common::String::format("(%i,%i)", point.x, point.y); } 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 * 0xff * 2 + 1) / (0xffff * 2); this->g = (color.green * 0xff * 2 + 1) / (0xffff * 2); this->b = (color.blue * 0xff * 2 + 1) / (0xffff * 2); 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) { } Common::Point Point16POD::toScummVMPoint() const { return Common::Point(x, y); } DynamicListContainerBase::~DynamicListContainerBase() { } void DynamicListDefaultSetter::defaultSet(int32 &value) { value = 0; } void DynamicListDefaultSetter::defaultSet(double &value) { value = 0.0; } void DynamicListDefaultSetter::defaultSet(Common::Point &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) { } Common::Point DynamicListValueConverter::dereference(const Point16POD *source) { return source->toScummVMPoint(); } 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 Point16POD *&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 Common::Point &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; i++) { _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