/* 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