From 1ac6abaeff4813d75abd4ac58a13e22eaef2ab71 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Thu, 15 Sep 2011 20:39:37 -0400 Subject: PEGASUS: Stub off the base timebase code This is not tested, and hopefully just works without anymore changes. I hate QuickTime :P --- engines/pegasus/elements.cpp | 121 +++++++++++++++++++ engines/pegasus/elements.h | 59 +++++++++ engines/pegasus/pegasus.cpp | 8 ++ engines/pegasus/pegasus.h | 7 ++ engines/pegasus/timers.cpp | 282 +++++++++++++++++++++++++++++++++++++++++++ engines/pegasus/timers.h | 166 +++++++++++++++++++++++++ 6 files changed, 643 insertions(+) (limited to 'engines') diff --git a/engines/pegasus/elements.cpp b/engines/pegasus/elements.cpp index 132c19a02a..695a6e54b8 100644 --- a/engines/pegasus/elements.cpp +++ b/engines/pegasus/elements.cpp @@ -23,6 +23,9 @@ * */ +#include "common/macresman.h" +#include "common/stream.h" + #include "pegasus/elements.h" #include "pegasus/graphics.h" @@ -238,4 +241,122 @@ void EnergyBar::calcLevelRect(Common::Rect &r) const { } } +IdlerAnimation::IdlerAnimation(const tDisplayElementID id) : Animation(id) { + _lastTime = 0xffffffff; +} + +void IdlerAnimation::startDisplaying() { + if (!isDisplaying()) { + Animation::startDisplaying(); + startIdling(); + } +} + +void IdlerAnimation::stopDisplaying() { + if (isDisplaying()) { + Animation::stopDisplaying(); + stopIdling(); + } +} + +void IdlerAnimation::useIdleTime() { + uint32 currentTime = getTime(); + + if (currentTime != _lastTime) { + _lastTime = currentTime; + timeChanged(_lastTime); + } +} + +void IdlerAnimation::timeChanged(const TimeValue) { + triggerRedraw(); +} + +FrameSequence::FrameSequence(const tDisplayElementID id) : IdlerAnimation(id) { + _duration = 0; + _currentFrameNum = 0; + _resFork = new Common::MacResManager(); +} + +FrameSequence::~FrameSequence() { + delete _resFork; +} + +void FrameSequence::useFileName(const Common::String &fileName) { + _resFork->open(fileName); +} + +void FrameSequence::openFrameSequence() { + if (!_resFork->hasResFork()) + return; + + Common::SeekableReadStream *res = _resFork->getResource(MKTAG('P', 'F', 'r', 'm'), 0x80); + + if (!res) + return; + + uint32 scale = res->readUint32BE(); + _bounds.top = res->readUint16BE(); + _bounds.left = res->readUint16BE(); + _bounds.bottom = res->readUint16BE(); + _bounds.right = res->readUint16BE(); + _numFrames = res->readUint16BE(); + _duration = 0; + + _frameTimes.clear(); + for (uint32 i = 0; i < _numFrames; i++) { + TimeValue time = res->readUint32BE(); + _duration += time; + _frameTimes.push_back(_duration); + } + + setScale(scale); + setSegment(0, _duration); + setTime(0); + _currentFrameNum = 0; + newFrame(_currentFrameNum); + triggerRedraw(); + + delete res; +} + +void FrameSequence::closeFrameSequence() { + stop(); + _resFork->close(); + _duration = 0; + _numFrames = 0; + _frameTimes.clear(); +} + +void FrameSequence::timeChanged(const TimeValue time) { + int16 frameNum = 0; + for (int16 i = _numFrames - 1; i >= 0; i--) { + if (_frameTimes[i] < time) { + frameNum = i; + break; + } + } + + if (frameNum != _currentFrameNum) { + _currentFrameNum = frameNum; + newFrame(_currentFrameNum); + triggerRedraw(); + } +} + +void FrameSequence::setFrameNum(const int16 frameNum) { + int16 f = CLIP(frameNum, 0, _numFrames); + + if (_currentFrameNum != f) { + _currentFrameNum = f; + setTime(_frameTimes[f]); + newFrame(f); + triggerRedraw(); + } +} + +bool FrameSequence::isSequenceOpen() const { + return _numFrames != 0; +} + } // End of namespace Pegasus diff --git a/engines/pegasus/elements.h b/engines/pegasus/elements.h index 2fc7a3abd3..460b9a71d9 100644 --- a/engines/pegasus/elements.h +++ b/engines/pegasus/elements.h @@ -33,8 +33,13 @@ #include "graphics/surface.h" #include "pegasus/pegasus.h" +#include "pegasus/timers.h" #include "pegasus/util.h" +namespace Common { + class MacResManager; +} + namespace Pegasus { class DisplayElement : public IDObject { @@ -134,6 +139,60 @@ protected: uint32 _barColor; }; +class Animation : public DisplayElement, public DynamicElement { +public: + Animation(const tDisplayElementID id) : DisplayElement(id) {} +}; + +class IdlerAnimation : public Animation, public Idler { +public: + IdlerAnimation(const tDisplayElementID); + + virtual void startDisplaying(); + virtual void stopDisplaying(); + + TimeValue getLastTime() const { return _lastTime; } + +protected: + virtual void useIdleTime(); + virtual void timeChanged(const TimeValue); + + TimeValue _lastTime; +}; + +// This class reads PICT resources and plays them like a movie. +// Assumes there is a resource of type 'PFrm' describing the time values for each +// PICT frame, as well as the total time in the movie. +// Assumes that PICT frames begin at PICT 128 + +class FrameSequence : public IdlerAnimation { +public: + FrameSequence(const tDisplayElementID); + virtual ~FrameSequence(); + + void useFileName(const Common::String &fileName); + + virtual void openFrameSequence(); + virtual void closeFrameSequence(); + bool isSequenceOpen() const; + + uint16 getNumFrames() const { return _numFrames; } + virtual uint16 getFrameNum() const { return _currentFrameNum; } + virtual void setFrameNum(const int16); + +protected: + virtual void timeChanged(const TimeValue); + virtual void newFrame(const uint16) {} + + Common::MacResManager *_resFork; + TimeValue _duration; + + uint16 _numFrames; + Common::Array _frameTimes; + + uint16 _currentFrameNum; +}; + } // End of namespace Pegasus #endif diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp index 39eb7d50c4..dacc2a0a30 100644 --- a/engines/pegasus/pegasus.cpp +++ b/engines/pegasus/pegasus.cpp @@ -332,4 +332,12 @@ void PegasusEngine::giveIdleTime() { (*it)->useIdleTime(); } +void PegasusEngine::addTimeBase(TimeBase *timeBase) { + _timeBases.push_back(timeBase); +} + +void PegasusEngine::removeTimeBase(TimeBase *timeBase) { + _timeBases.remove(timeBase); +} + } // End of namespace Pegasus diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h index 8ab891df9c..5074aaba09 100644 --- a/engines/pegasus/pegasus.h +++ b/engines/pegasus/pegasus.h @@ -50,6 +50,7 @@ class VideoManager; class GraphicsManager; class Idler; class Cursor; +class TimeBase; static const int kViewScreenOffset = 64; @@ -85,6 +86,9 @@ public: void addIdler(Idler *idler); void removeIdler(Idler *idler); + void addTimeBase(TimeBase *timeBase); + void removeTimeBase(TimeBase *timeBase); + protected: Common::Error run(); @@ -141,6 +145,9 @@ private: // Items void createItems(); void createItem(tItemID itemID, tNeighborhoodID neighborhoodID, tRoomID roomID, tDirectionConstant direction); + + // TimeBases + Common::List _timeBases; }; } // End of namespace Pegasus diff --git a/engines/pegasus/timers.cpp b/engines/pegasus/timers.cpp index 5908854d13..fe487e90d2 100755 --- a/engines/pegasus/timers.cpp +++ b/engines/pegasus/timers.cpp @@ -24,6 +24,7 @@ */ #include "pegasus/pegasus.h" +#include "pegasus/notification.h" #include "pegasus/timers.h" namespace Pegasus { @@ -50,4 +51,285 @@ void Idler::stopIdling() { } } +TimeBase::TimeBase(const TimeScale preferredScale) { + _preferredScale = preferredScale; + _callBackList = 0; + _paused = false; + _flags = 0; + ((PegasusEngine *)g_engine)->addTimeBase(this); +} + +TimeBase::~TimeBase() { + if (_master) + _master->_slaves.remove(this); + + ((PegasusEngine *)g_engine)->removeTimeBase(this); + disposeAllCallBacks(); + + // TODO: Remove slaves? Make them remove themselves? +} + +void TimeBase::setTime(const TimeValue time, const TimeScale scale) { + // So, we're saying the *current* time from g_system->getMillis() is + // what time/scale is. Which means we need to subtract time/scale from + // the offset so that g_system->getMillis() - _currentOffset is + // equal to time/scale. + _timeOffset = g_system->getMillis() - time * 1000 / ((scale == 0) ? _preferredScale : scale); + + // TODO: Also adjust the slaves' offsets (once we're actually using the + // masters for calculating time) +} + +TimeValue TimeBase::getTime(const TimeScale scale) { + if (_paused) + return _pausedTime; + + Common::Rational normalTime = Common::Rational((g_system->getMillis() - _timeOffset) * ((scale == 0) ? _preferredScale : scale), 1000); + + // Adjust it according to our rate + return (normalTime * getEffectiveRate()).toInt(); +} + +void TimeBase::setRate(const Common::Rational rate) { + if (rate == 0) { + _paused = false; + _timeOffset = 0; + } else if (_timeOffset != 0) { + // Convert the time from the old rate to the new rate + _timeOffset = g_system->getMillis() - (rate * (g_system->getMillis() - _timeOffset) / _rate).toInt(); + } else { + // TODO: Check this + setTime(_startTime, _startScale); + } + + _rate = rate; +} + +Common::Rational TimeBase::getEffectiveRate() const { + return _rate * ((_master == 0) ? 1 : _master->getEffectiveRate()); +} + +void TimeBase::start() { + if (_paused) + _pausedRate = 1; + else + setRate(1); +} + +void TimeBase::stop() { + setRate(0); + _paused = false; +} + +void TimeBase::pause() { + if (isRunning() && !_paused) { + _pausedRate = getRate(); + stop(); + _pausedTime = getTime(); + _paused = true; + } +} + +void TimeBase::resume() { + if (_paused) { + setRate(_pausedRate); + _paused = false; + } +} + +bool TimeBase::isRunning() { + if (_paused && _pausedRate != 0) + return true; + + Common::Rational rate = getRate(); + + if (rate == 0) + return false; + + if (getFlags() & kLoopTimeBase) + return true; + + if (rate > 0) + return getTime() < getStop(); + + return getTime() > getStart(); +} + +void TimeBase::setStart(const TimeValue startTime, const TimeScale scale) { + _startTime = startTime; + _startScale = (scale == 0) ? _preferredScale : scale; +} + +TimeValue TimeBase::getStart(const TimeScale scale) const { + if (scale) + return _startTime * scale / _startScale; + + return _startTime * _preferredScale / _startScale; +} + +void TimeBase::setStop(const TimeValue stopTime, const TimeScale scale) { + _stopTime = stopTime; + _stopScale = (scale == 0) ? _preferredScale : scale; +} + +TimeValue TimeBase::getStop(const TimeScale scale) const { + if (scale) + return _stopTime * scale / _stopScale; + + return _stopTime * _preferredScale / _stopScale; +} + +void TimeBase::setSegment(const TimeValue startTime, const TimeValue stopTime, const TimeScale scale) { + setStart(startTime, scale); + setStop(stopTime, scale); +} + +void TimeBase::getSegment(TimeValue &startTime, TimeValue &stopTime, const TimeScale scale) const { + startTime = getStart(scale); + stopTime = getStop(scale); +} + +TimeValue TimeBase::getDuration(const TimeScale scale) const { + TimeValue startTime, stopTime; + getSegment(startTime, stopTime, scale); + return stopTime - startTime; +} + +void TimeBase::setMasterTimeBase(TimeBase *tb) { + // TODO: We're just ignoring the master (except for effective rate) + // for now to simplify things + if (_master) + _master->_slaves.remove(this); + + _master = tb; + + if (_master) + _master->_slaves.push_back(this); +} + +void TimeBase::checkCallBacks() { + uint32 time = getTime(); + uint32 startTime = getStart(); + uint32 stopTime = getStop(); + + for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = runner->_nextCallBack) { + if (runner->_type == kCallBackAtTime && runner->_trigger == kTriggerTimeFwd) { + if (time >= (runner->_param2 * _preferredScale / runner->_param3) && getRate() > 0) + runner->callBack(); + } else if (runner->_type == kCallBackAtExtremes) { + if (runner->_trigger == kTriggerAtStop) { + if (time >= stopTime) + runner->callBack(); + } else if (runner->_trigger == kTriggerAtStart) { + if (time <= startTime) + runner->callBack(); + } + } + } + + if (getFlags() & kLoopTimeBase) { + // Loop + if (time >= startTime) + setTime(stopTime); + else if (time <= stopTime) + setTime(startTime); + } +} + +// Protected functions only called by TimeBaseCallBack. + +void TimeBase::addCallBack(TimeBaseCallBack *callBack) { + callBack->_nextCallBack = _callBackList; + _callBackList = callBack; +} + +void TimeBase::removeCallBack(TimeBaseCallBack *callBack) { + if (_callBackList == callBack) { + _callBackList = callBack->_nextCallBack; + } else { + TimeBaseCallBack *runner, *prevRunner; + + for (runner = _callBackList->_nextCallBack, prevRunner = _callBackList; runner != callBack; prevRunner = runner, runner = runner->_nextCallBack) + ; + + prevRunner->_nextCallBack = runner->_nextCallBack; + } + + callBack->_nextCallBack = 0; +} + +void TimeBase::disposeAllCallBacks() { + TimeBaseCallBack *nextRunner; + + for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = nextRunner) { + nextRunner = runner->_nextCallBack; + runner->disposeCallBack(); + runner->_nextCallBack = 0; + } + + _callBackList = 0; +} + +TimeBaseCallBack::TimeBaseCallBack() { + _timeBase = 0; + _nextCallBack = 0; + _trigger = kTriggerNone; + _type = kCallBackNone; +} + +TimeBaseCallBack::~TimeBaseCallBack() { + releaseCallBack(); +} + +void TimeBaseCallBack::initCallBack(TimeBase *tb, CallBackType type) { + releaseCallBack(); + _timeBase = tb; + _timeBase->addCallBack(this); + _type = type; +} + +void TimeBaseCallBack::releaseCallBack() { + if (_timeBase) + _timeBase->removeCallBack(this); + disposeCallBack(); +} + +void TimeBaseCallBack::disposeCallBack() { + _timeBase = 0; +} + +void TimeBaseCallBack::scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3) { + // TODO: Rename param2/param3? + _trigger = trigger; + _param2 = param2; + _param3 = param3; +} + +void TimeBaseCallBack::cancelCallBack() { + _trigger = kTriggerNone; +} + +IdlerTimeBase::IdlerTimeBase() { + _lastTime = 0xffffffff; + startIdling(); +} + +void IdlerTimeBase::useIdleTime() { + uint32 currentTime = getTime(); + if (currentTime != _lastTime) { + _lastTime = currentTime; + timeChanged(_lastTime); + } +} + +NotificationCallBack::NotificationCallBack() { + _callBackFlag = 0; + _notifier = 0; +} + +void NotificationCallBack::callBack() { + if (_notifier) + _notifier->setNotificationFlags(_callBackFlag, _callBackFlag); +} + } // End of namespace Pegasus diff --git a/engines/pegasus/timers.h b/engines/pegasus/timers.h index e4bb41086f..9ce3093820 100755 --- a/engines/pegasus/timers.h +++ b/engines/pegasus/timers.h @@ -26,6 +26,10 @@ #ifndef PEGASUS_TIMERS_H #define PEGASUS_TIMERS_H +#include "common/rational.h" + +#include "pegasus/constants.h" + namespace Pegasus { class Idler { @@ -45,6 +49,168 @@ protected: bool _isIdling; }; +enum { + kLoopTimeBase = 1, + kPalindromeLoopTimeBase = 2, + kMaintainTimeBaseZero = 4 +}; + +class TimeBaseCallBack; + +class TimeBase { +friend class TimeBaseCallBack; +public: + TimeBase(const TimeScale = kDefaultTimeScale); + virtual ~TimeBase(); + + virtual void setTime(const TimeValue, const TimeScale = 0); + virtual TimeValue getTime(const TimeScale = 0); + + virtual void setScale(const TimeScale scale) { _preferredScale = scale; } + virtual TimeScale getScale() const { return _preferredScale; } + + virtual void setRate(const Common::Rational); + virtual Common::Rational getRate() const { return _rate; } + + virtual void start(); + virtual void stop(); + virtual bool isRunning(); + + virtual void pause(); + virtual void resume(); + bool isPaused() const { return _paused; } + + virtual void setFlags(const uint32 flags) { _flags = flags; } + virtual uint32 getFlags() const { return _flags; } + + virtual void setStart(const TimeValue, const TimeScale = 0); + virtual TimeValue getStart(const TimeScale = 0) const; + + virtual void setStop(const TimeValue, const TimeScale = 0); + virtual TimeValue getStop(const TimeScale = 0) const; + + virtual void setSegment(const TimeValue, const TimeValue, const TimeScale = 0); + virtual void getSegment(TimeValue&, TimeValue&, const TimeScale = 0) const; + + virtual TimeValue getDuration(const TimeScale = 0) const; + + virtual void setMasterTimeBase(TimeBase *timeBase); + + void disposeAllCallBacks(); + + // ScummVM's API additions (to replace the need for actual timers) + void checkCallBacks(); + +protected: + void addCallBack(TimeBaseCallBack *); + void removeCallBack(TimeBaseCallBack *); + + TimeBase *_master; + TimeScale _preferredScale; + TimeBaseCallBack *_callBackList; + Common::Rational _rate, _pausedRate; + bool _paused; + uint32 _startTime, _startScale; + uint32 _stopTime, _stopScale; + int32 _timeOffset; + uint32 _flags; + TimeValue _pausedTime; + +private: + Common::Rational getEffectiveRate() const; + + Common::List _slaves; +}; + +// Type passed to initCallBack() +enum CallBackType { + kCallBackNone = 0, + kCallBackAtTime = 1, + kCallBackAtExtremes = 4 +}; + +// Trigger passed to scheduleCallBack() +enum CallBackTrigger { + kTriggerNone = 0, + + // AtTime flags + kTriggerTimeFwd = 1, + + // AtExtremes flags + kTriggerAtStart = 1, + kTriggerAtStop = 2 +}; + +class TimeBaseCallBack { +friend class TimeBase; + +public: + TimeBaseCallBack(); + virtual ~TimeBaseCallBack(); + + void initCallBack(TimeBase *, CallBackType type); + + void releaseCallBack(); + + void scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3); + void cancelCallBack(); + +protected: + virtual void callBack() = 0; + + TimeBase *_timeBase; + + // Owned and operated by TimeBase; + TimeBaseCallBack *_nextCallBack; + + // Our storage of the QuickTime timer crap + CallBackType _type; + CallBackTrigger _trigger; + uint32 _param2, _param3; + +private: + void disposeCallBack(); +}; + +class IdlerTimeBase : public Idler, public TimeBase { +public: + IdlerTimeBase(); + virtual ~IdlerTimeBase() { stopIdling(); } + + TimeValue getLastTime() const { return _lastTime; } + +protected: + virtual void useIdleTime(); + virtual void timeChanged(const TimeValue) {} + + TimeValue _lastTime; + +}; + +class Notification; + +class NotificationCallBack : public TimeBaseCallBack { +public: + NotificationCallBack(); + virtual ~NotificationCallBack() {} + + void setNotification(Notification *notifier) { _notifier = notifier; } + + void setCallBackFlag(const tNotificationFlags flag) { _callBackFlag = flag; } + tNotificationFlags getCallBackFlag() const { return _callBackFlag; } + +protected: + void callBack(); + + Notification *_notifier; + tNotificationFlags _callBackFlag; +}; + +class DynamicElement : public TimeBase { +public: + TimeValue percentSeconds(const uint32 percent) { return getScale() * percent / 100; } +}; + } // End of namespace Pegasus #endif -- cgit v1.2.3