/* 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 . * */ #ifndef MTROPOLIS_RUNTIME_H #define MTROPOLIS_RUNTIME_H #include "common/archive.h" #include "common/array.h" #include "common/events.h" #include "common/language.h" #include "common/platform.h" #include "common/ptr.h" #include "common/stream.h" #include "common/hashmap.h" #include "common/hash-str.h" #include "graphics/pixelformat.h" #include "mtropolis/actions.h" #include "mtropolis/core.h" #include "mtropolis/coroutine_protos.h" #include "mtropolis/data.h" #include "mtropolis/debug.h" #include "mtropolis/hacks.h" #include "mtropolis/miniscript_protos.h" #include "mtropolis/subtitles.h" #include "mtropolis/vthread.h" class OSystem; namespace Audio { class Mixer; } // End of namespace Audio namespace Common { class RandomSource; } // End of namespace Common namespace Graphics { struct WinCursorGroup; class MacCursor; class MacFontManager; class ManagedSurface; class Cursor; struct PixelFormat; struct Surface; } // End of namespace Graphics namespace MTropolis { class Asset; class AssetManagerInterface; class CoroutineManager; class CursorGraphic; class CursorGraphicCollection; class Element; class GraphicModifier; class KeyboardInputEvent; class MessageDispatch; class MiniscriptThread; class Modifier; class ObjectLinkingScope; class PlugInModifier; class RuntimeObject; class PlugIn; class Project; class Runtime; class Structural; class SystemInterface; class VisualElement; class Window; class WorldManagerInterface; struct DynamicValue; struct DynamicValueWriteProxy; struct IBoundaryDetector; struct ICollider; struct ILoadUIProvider; struct IMessageConsumer; struct IModifierContainer; struct IPlugInModifierFactory; struct IPlugInModifierFactoryAndDataFactory; struct IPostEffect; struct ISaveUIProvider; struct ISaveWriter; struct IStructuralReferenceVisitor; struct MessageProperties; struct ModifierLoaderContext; struct PlugInModifierLoaderContext; struct SIModifierFactory; template class ElementFactory; #ifdef MTROPOLIS_DEBUG_ENABLE class DebugPrimaryTaskList; #endif char invariantToLower(char c); Common::String toCaseInsensitive(const Common::String &str); bool caseInsensitiveEqual(const Common::String &str1, const Common::String &str2); size_t caseInsensitiveFind(const Common::String &stringToSearch, const Common::String &stringToFind); enum ColorDepthMode { kColorDepthMode1Bit, kColorDepthMode2Bit, kColorDepthMode4Bit, kColorDepthMode8Bit, kColorDepthMode16Bit, kColorDepthMode32Bit, kColorDepthModeCount, kColorDepthModeInvalid, }; namespace SceneTransitionTypes { enum SceneTransitionType { kNone, kPatternDissolve, kRandomDissolve, // No steps kFade, kSlide, // Directional kPush, // Directional kZoom, kWipe, // Directional }; bool loadFromData(SceneTransitionType &transType, int32 data); } // End of namespace SceneTransitionTypes namespace SceneTransitionDirections { enum SceneTransitionDirection { kUp, kDown, kLeft, kRight, }; bool loadFromData(SceneTransitionDirection &transDir, int32 data); } // End of namespace SceneTransitionDirections enum ConstraintDirection { kConstraintDirectionNone, kConstraintDirectionHorizontal, kConstraintDirectionVertical, }; enum MouseInteractivityTestType { kMouseInteractivityTestAnything, kMouseInteractivityTestMouseClick, }; namespace DynamicValueSourceTypes { enum DynamicValueSourceType { kInvalid, kConstant, kVariableReference, kIncomingData, }; } namespace DynamicValueTypes { // These values are stored in the saved game format, so they must be stable enum DynamicValueType { kInvalid = 0, kNull = 1, kInteger = 2, kFloat = 3, kPoint = 4, kIntegerRange = 5, kBoolean = 6, kVector = 7, kLabel = 8, kEvent = 9, kString = 12, kList = 13, kObject = 14, kWriteProxy = 15, kUnspecified = 16, }; } // End of namespace DynamicValuesTypes namespace AttributeIDs { enum AttributeID { kAttribCache = 55, kAttribDirect = 56, kAttribVisible = 58, kAttribLayer = 24, kAttribPaused = 25, kAttribLoop = 57, kAttribPosition = 1, kAttribWidth = 2, kAttribHeight = 3, kAttribRate = 4, kAttribRange = 5, kAttribCel = 6, kAttribLoopBackForth = 59, kAttribPlayEveryFrame = 60, kAttribTimeValue = 16, kAttribTrackDisable = 51, kAttribTrackEnable = 50, kAttribVolume = 13, kAttribBalance = 26, kAttribText = 7, kAttribMasterVolume = 18, kAttribUserTimeout = 19, }; } // End of namespace AttributeIDs namespace EventIDs { enum EventID { kNothing = 0, kElementEnableEdit = 207, kElementDisableEdit = 220, kElementSelect = 209, kElementDeselect = 210, kElementToggleSelect = 213, kElementUpdatedCalculated = 219, kElementShow = 222, kElementHide = 223, kElementScrollUp = 1001, kElementScrollDown = 1002, kElementScrollRight = 1005, kElementScrollLeft = 1006, kMotionStarted = 501, kMotionEnded = 502, kTransitionStarted = 503, kTransitionEnded = 504, kMouseDown = 301, kMouseUp = 302, kMouseOver = 303, kMouseOutside = 304, kMouseTrackedInside = 305, kMouseTrackedOutside = 306, kMouseTracking = 307, kMouseUpInside = 309, kMouseUpOutside = 310, kSceneStarted = 101, kSceneEnded = 102, kSceneDeactivated = 103, kSceneReactivated = 104, kSceneTransitionEnded = 506, kSharedSceneReturnedToScene = 401, kSharedSceneSceneChanged = 402, kSharedSceneNoNextScene = 403, kSharedSceneNoPrevScene = 404, kParentEnabled = 2001, kParentDisabled = 2002, kParentChanged = 227, kPreloadMedia = 1701, kFlushMedia = 1703, kPrerollMedia = 1704, kCloseProject = 1601, kUserTimeout = 1801, kProjectStarted = 1802, kProjectEnded = 1803, kFlushAllMedia = 1804, kAttribGet = 1300, kAttribSet = 1200, kClone = 226, kKill = 228, kPlay = 201, kStop = 202, kPause = 801, kUnpause = 802, kTogglePause = 803, kAtFirstCel = 804, kAtLastCel = 805, kAuthorMessage = 900, }; bool isCommand(EventID eventID); } // End of namespace EventIDs MiniscriptInstructionOutcome pointWriteRefAttrib(Common::Point &point, MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib); Common::String pointToString(const Common::Point &point); struct IntRange { IntRange(); IntRange(int32 pmin, int32 pmax); int32 min; int32 max; bool load(const Data::IntRange &range); inline bool operator==(const IntRange &other) const { return min == other.min && max == other.max; } inline bool operator!=(const IntRange &other) const { return !((*this) == other); } MiniscriptInstructionOutcome refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib); Common::String toString() const; }; struct Label { Label(); Label(int32 psuperGroupID, int32 pid); uint32 superGroupID; uint32 id; bool load(const Data::Label &label); inline bool operator==(const Label &other) const { return superGroupID == other.superGroupID && id == other.id; } inline bool operator!=(const Label &other) const { return !((*this) == other); } }; struct Event { Event(); Event(EventIDs::EventID peventType, uint32 peventInfo); EventIDs::EventID eventType; uint32 eventInfo; // Returns true if this event, interpreted as a filter, recognizes another event. // Handles cases where eventInfo is ignored (hopefully). bool respondsTo(const Event &otherEvent) const; bool load(const Data::Event &data); inline bool operator==(const Event &other) const { return eventType == other.eventType && eventInfo == other.eventInfo; } inline bool operator!=(const Event &other) const { return !((*this) == other); } }; struct VarReference { VarReference(); VarReference(uint32 pguid, const Common::String &psource); uint32 guid; Common::String source; Common::WeakPtr resolution; // NOTE: This may not be a variable inline bool operator==(const VarReference &other) const { return guid == other.guid && source == other.source; } inline bool operator!=(const VarReference &other) const { return !((*this) == other); } bool resolve(Structural *structuralScope, Common::WeakPtr &outObject) const; bool resolve(Modifier *modifierScope, Common::WeakPtr &outObject) const; void linkInternalReferences(ObjectLinkingScope *scope); void visitInternalReferences(IStructuralReferenceVisitor *visitor); private: bool resolveContainer(IModifierContainer *modifierContainer, Common::WeakPtr &outObject) const; bool resolveSingleModifier(Modifier *modifier, Common::WeakPtr &outObject) const; }; struct ObjectReference { Common::WeakPtr object; inline ObjectReference() { } inline explicit ObjectReference(const Common::WeakPtr &objectPtr) : object(objectPtr) { } inline bool operator==(const ObjectReference &other) const { return !object.owner_before(other.object) && !other.object.owner_before(object); } inline bool operator!=(const ObjectReference &other) const { return !((*this) == other); } inline void reset() { object.reset(); } }; struct AngleMagVector { AngleMagVector(); double angleDegrees; // These are stored as radians in the data but scripts treat them as degrees so it's just pointless constantly doing conversion... double magnitude; inline bool operator==(const AngleMagVector &other) const { return angleDegrees == other.angleDegrees && magnitude == other.magnitude; } inline bool operator!=(const AngleMagVector &other) const { return !((*this) == other); } inline static AngleMagVector createRadians(double angleRadians, double magnitude) { return AngleMagVector(angleRadians * (180.0 / M_PI), magnitude); } inline static AngleMagVector createDegrees(double angleDegrees, double magnitude) { return AngleMagVector(angleDegrees, magnitude); } MiniscriptInstructionOutcome refAttrib(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, const Common::String &attrib); Common::String toString() const; private: AngleMagVector(double angleDegrees, double magnitude); }; struct ColorRGB8 { ColorRGB8(); ColorRGB8(uint8 pr, uint8 pg, uint8 pb); uint8 r; uint8 g; uint8 b; bool load(const Data::ColorRGB16 &color); inline bool operator==(const ColorRGB8 &other) const { return r == other.r && g == other.g && b == other.b; } inline bool operator!=(const ColorRGB8 &other) const { return !((*this) == other); } }; struct MessageFlags { MessageFlags(); bool relay : 1; bool cascade : 1; bool immediate : 1; }; struct DynamicValue; struct DynamicList; // This should be an interface, but since this exists to make global singletons that JUST supply a vtable, // GCC complains about there being a global destructor, unless we delete the destructor, in which case // it complains about a class having virtual functions but not having a virtual destructor, so we have to // do this dispatch table stuff just to make GCC be quiet. struct DynamicValueWriteInterface { typedef MiniscriptInstructionOutcome (*writeFunc_t)(MiniscriptThread *thread, const DynamicValue &dest, void *objectRef, uintptr ptrOrOffset); typedef MiniscriptInstructionOutcome (*refAttribFunc_t)(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib); typedef MiniscriptInstructionOutcome (*refAttribIndexedFunc_t)(MiniscriptThread *thread, DynamicValueWriteProxy &proxy, void *objectRef, uintptr ptrOrOffset, const Common::String &attrib, const DynamicValue &index); writeFunc_t write; refAttribFunc_t refAttrib; refAttribIndexedFunc_t refAttribIndexed; }; template class DynamicValueWriteInterfaceGlue { public: static const DynamicValueWriteInterface *getInstance(); private: static DynamicValueWriteInterface _instance; }; template inline const DynamicValueWriteInterface *DynamicValueWriteInterfaceGlue::getInstance() { return &_instance; } template DynamicValueWriteInterface DynamicValueWriteInterfaceGlue::_instance = { static_cast(T::write), static_cast(T::refAttrib), static_cast(T::refAttribIndexed), }; struct DynamicValueWriteProxyPOD { uintptr ptrOrOffset; void *objectRef; const DynamicValueWriteInterface *ifc; static DynamicValueWriteProxyPOD createDefault(); }; struct DynamicValueWriteProxy { DynamicValueWriteProxy(); DynamicValueWriteProxyPOD pod; Common::SharedPtr containerList; }; struct Point16POD { int16 x; int16 y; Common::Point toScummVMPoint() const; }; class DynamicListContainerBase { public: virtual ~DynamicListContainerBase(); virtual bool setAtIndex(size_t index, const DynamicValue &dynValue) = 0; virtual bool getAtIndex(size_t index, DynamicValue &dynValue) const = 0; virtual void truncateToSize(size_t sz) = 0; virtual bool expandToMinimumSize(size_t sz) = 0; virtual void setFrom(const DynamicListContainerBase &other) = 0; // Only supports setting same type! virtual const void *getConstArrayPtr() const = 0; virtual void *getArrayPtr() = 0; virtual size_t getSize() const = 0; virtual bool compareEqual(const DynamicListContainerBase &other) const = 0; virtual DynamicListContainerBase *clone() const = 0; }; struct DynamicListDefaultSetter { static void defaultSet(int32 &value); static void defaultSet(double &value); static void defaultSet(Common::Point &value); static void defaultSet(IntRange &value); static void defaultSet(bool &value); static void defaultSet(AngleMagVector &value); static void defaultSet(Label &value); static void defaultSet(Event &value); static void defaultSet(Common::String &value); static void defaultSet(Common::SharedPtr &value); static void defaultSet(ObjectReference &value); }; template struct DynamicListValueConverter { typedef T DynamicValuePODType_t; static const T &dereference(const T *source) { return *source; } }; struct DynamicListValueImporter { static bool importValue(const DynamicValue &dynValue, const int32 *&outPtr); static bool importValue(const DynamicValue &dynValue, const double *&outPtr); static bool importValue(const DynamicValue &dynValue, const Common::Point *&outPtr); static bool importValue(const DynamicValue &dynValue, const IntRange *&outPtr); static bool importValue(const DynamicValue &dynValue, const bool *&outPtr); static bool importValue(const DynamicValue &dynValue, const AngleMagVector *&outPtr); static bool importValue(const DynamicValue &dynValue, const Label *&outPtr); static bool importValue(const DynamicValue &dynValue, const Event *&outPtr); static bool importValue(const DynamicValue &dynValue, const Common::String *&outPtr); static bool importValue(const DynamicValue &dynValue, const Common::SharedPtr *&outPtr); static bool importValue(const DynamicValue &dynValue, const ObjectReference *&outPtr); }; struct DynamicListValueExporter { static void exportValue(DynamicValue &dynValue, const int32 &value); static void exportValue(DynamicValue &dynValue, const double &value); static void exportValue(DynamicValue &dynValue, const Common::Point &value); static void exportValue(DynamicValue &dynValue, const IntRange &value); static void exportValue(DynamicValue &dynValue, const bool &value); static void exportValue(DynamicValue &dynValue, const AngleMagVector &value); static void exportValue(DynamicValue &dynValue, const Label &value); static void exportValue(DynamicValue &dynValue, const Event &value); static void exportValue(DynamicValue &dynValue, const Common::String &value); static void exportValue(DynamicValue &dynValue, const Common::SharedPtr &value); static void exportValue(DynamicValue &dynValue, const ObjectReference &value); }; template class DynamicListContainer : public DynamicListContainerBase { public: bool setAtIndex(size_t index, const DynamicValue &dynValue) override; bool getAtIndex(size_t index, DynamicValue &dynValue) const override; void truncateToSize(size_t sz) override; bool expandToMinimumSize(size_t sz) override; void setFrom(const DynamicListContainerBase &other) override; const void *getConstArrayPtr() const override; void *getArrayPtr() override; size_t getSize() const override; bool compareEqual(const DynamicListContainerBase &other) const override; DynamicListContainerBase *clone() const override; private: Common::Array _array; }; template<> class DynamicListContainer : public DynamicListContainerBase { public: DynamicListContainer(); bool setAtIndex(size_t index, const DynamicValue &dynValue) override; bool getAtIndex(size_t index, DynamicValue &dynValue) const override; void truncateToSize(size_t sz) override; bool expandToMinimumSize(size_t sz) override; void setFrom(const DynamicListContainerBase &other) override; const void *getConstArrayPtr() const override; void *getArrayPtr() override; size_t getSize() const override; bool compareEqual(const DynamicListContainerBase &other) const override; DynamicListContainerBase *clone() const override; public: size_t _size; }; template bool DynamicListContainer::setAtIndex(size_t index, const DynamicValue &dynValue) { const typename DynamicListValueConverter::DynamicValuePODType_t *valuePtr = nullptr; if (!DynamicListValueImporter::importValue(dynValue, valuePtr)) return false; _array.reserve(index + 1); if (_array.size() <= index) { if (_array.size() < index) { T defaultValue; DynamicListDefaultSetter::defaultSet(defaultValue); while (_array.size() < index) { _array.push_back(defaultValue); } } _array.push_back(DynamicListValueConverter::dereference(valuePtr)); } else { _array[index] = DynamicListValueConverter::dereference(valuePtr); } return true; } template void DynamicListContainer::truncateToSize(size_t sz) { if (_array.size() > sz) _array.resize(sz); } template bool DynamicListContainer::expandToMinimumSize(size_t sz) { _array.reserve(sz); if (_array.size() < sz) { T defaultValue; DynamicListDefaultSetter::defaultSet(defaultValue); while (_array.size() < sz) { _array.push_back(defaultValue); } } return true; } template bool DynamicListContainer::getAtIndex(size_t index, DynamicValue &dynValue) const { if (index >= _array.size()) return false; DynamicListValueExporter::exportValue(dynValue, _array[index]); return true; } template void DynamicListContainer::setFrom(const DynamicListContainerBase &other) { _array = static_cast &>(other)._array; } template const void *DynamicListContainer::getConstArrayPtr() const { return &_array; } template void *DynamicListContainer::getArrayPtr() { return &_array; } template size_t DynamicListContainer::getSize() const { return _array.size(); } template bool DynamicListContainer::compareEqual(const DynamicListContainerBase &other) const { const DynamicListContainer &otherTyped = static_cast &>(other); return _array == otherTyped._array; } template DynamicListContainerBase *DynamicListContainer::clone() const { return new DynamicListContainer(*this); } struct DynamicList { DynamicList(); DynamicList(const DynamicList &other); ~DynamicList(); DynamicValueTypes::DynamicValueType getType() const; const Common::Array &getInt() const; const Common::Array &getFloat() const; const Common::Array &getPoint() const; const Common::Array &getIntRange() const; const Common::Array &getVector() const; const Common::Array