diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/EventDispatcher.cpp | 138 | ||||
-rw-r--r-- | common/EventRecorder.cpp | 366 | ||||
-rw-r--r-- | common/EventRecorder.h | 106 | ||||
-rw-r--r-- | common/events.h | 208 | ||||
-rw-r--r-- | common/file.cpp | 9 | ||||
-rw-r--r-- | common/file.h | 13 | ||||
-rw-r--r-- | common/module.mk | 2 | ||||
-rw-r--r-- | common/str.cpp | 29 | ||||
-rw-r--r-- | common/str.h | 5 | ||||
-rw-r--r-- | common/stream.h | 2 | ||||
-rw-r--r-- | common/unarj.cpp | 14 |
11 files changed, 857 insertions, 35 deletions
diff --git a/common/EventDispatcher.cpp b/common/EventDispatcher.cpp new file mode 100644 index 0000000000..57765f9d13 --- /dev/null +++ b/common/EventDispatcher.cpp @@ -0,0 +1,138 @@ +/* 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/events.h" + +namespace Common { + +EventDispatcher::EventDispatcher() : _mapper(0) { +} + +EventDispatcher::~EventDispatcher() { + for (Common::List<SourceEntry>::iterator i = _sources.begin(); i != _sources.end(); ++i) { + if (i->autoFree) + delete i->source; + } + + for (Common::List<ObserverEntry>::iterator i = _observers.begin(); i != _observers.end(); ++i) { + if (i->autoFree) + delete i->observer; + } + + delete _mapper; + _mapper = 0; +} + +void EventDispatcher::dispatch() { + Common::Event event; + + for (Common::List<SourceEntry>::iterator i = _sources.begin(); i != _sources.end(); ++i) { + const bool allowMapping = i->source->allowMapping(); + + while (i->source->pollEvent(event)) { + // We only try to process the events via the setup event mapper, when + // we have a setup mapper and when the event source allows mapping. + if (_mapper && allowMapping) { + if (_mapper->notifyEvent(event)) { + // We allow the event mapper to create multiple events, when + // eating an event. + while (_mapper->pollEvent(event)) + dispatchEvent(event); + + // Try getting another event from the current EventSource. + continue; + } + } + + dispatchEvent(event); + } + } +} + +void EventDispatcher::registerMapper(EventMapper *mapper) { + if (_mapper) + delete _mapper; + _mapper = mapper; +} + +void EventDispatcher::registerSource(EventSource *source, bool autoFree) { + SourceEntry newEntry; + + newEntry.source = source; + newEntry.autoFree = autoFree; + + _sources.push_back(newEntry); +} + +void EventDispatcher::unregisterSource(EventSource *source) { + for (Common::List<SourceEntry>::iterator i = _sources.begin(); i != _sources.end(); ++i) { + if (i->source == source) { + if (i->autoFree) + delete source; + + _sources.erase(i); + return; + } + } +} + +void EventDispatcher::registerObserver(EventObserver *obs, uint priority, bool autoFree) { + ObserverEntry newEntry; + + newEntry.observer = obs; + newEntry.priority = priority; + newEntry.autoFree = autoFree; + + for (Common::List<ObserverEntry>::iterator i = _observers.begin(); i != _observers.end(); ++i) { + if (i->priority < priority) { + _observers.insert(i, newEntry); + return; + } + } + + _observers.push_back(newEntry); +} + +void EventDispatcher::unregisterObserver(EventObserver *obs) { + for (Common::List<ObserverEntry>::iterator i = _observers.begin(); i != _observers.end(); ++i) { + if (i->observer == obs) { + if (i->autoFree) + delete obs; + + _observers.erase(i); + return; + } + } +} + +void EventDispatcher::dispatchEvent(const Event &event) { + for (Common::List<ObserverEntry>::iterator i = _observers.begin(); i != _observers.end(); ++i) { + if (i->observer->notifyEvent(event)) + break; + } +} + +} // end of namespace Common + diff --git a/common/EventRecorder.cpp b/common/EventRecorder.cpp new file mode 100644 index 0000000000..3d5eee3e52 --- /dev/null +++ b/common/EventRecorder.cpp @@ -0,0 +1,366 @@ +/* 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/EventRecorder.h" + +#include "common/config-manager.h" + +DECLARE_SINGLETON(Common::EventRecorder); + +namespace Common { + +#define RECORD_SIGNATURE 0x54455354 +#define RECORD_VERSION 1 + +void readRecord(Common::InSaveFile *inFile, uint32 &diff, Common::Event &event) { + diff = inFile->readUint32LE(); + + event.type = (Common::EventType)inFile->readUint32LE(); + + switch(event.type) { + case Common::EVENT_KEYDOWN: + case Common::EVENT_KEYUP: + event.kbd.keycode = (Common::KeyCode)inFile->readSint32LE(); + event.kbd.ascii = inFile->readUint16LE(); + event.kbd.flags = inFile->readByte(); + break; + case Common::EVENT_MOUSEMOVE: + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONDOWN: + case Common::EVENT_RBUTTONUP: + case Common::EVENT_WHEELUP: + case Common::EVENT_WHEELDOWN: + event.mouse.x = inFile->readSint16LE(); + event.mouse.y = inFile->readSint16LE(); + break; + default: + break; + } +} + +void writeRecord(Common::OutSaveFile *outFile, uint32 diff, const Common::Event &event) { + outFile->writeUint32LE(diff); + + outFile->writeUint32LE((uint32)event.type); + + switch(event.type) { + case Common::EVENT_KEYDOWN: + case Common::EVENT_KEYUP: + outFile->writeSint32LE(event.kbd.keycode); + outFile->writeUint16LE(event.kbd.ascii); + outFile->writeByte(event.kbd.flags); + break; + case Common::EVENT_MOUSEMOVE: + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONDOWN: + case Common::EVENT_RBUTTONUP: + case Common::EVENT_WHEELUP: + case Common::EVENT_WHEELDOWN: + outFile->writeSint16LE(event.mouse.x); + outFile->writeSint16LE(event.mouse.y); + break; + default: + break; + } +} + +EventRecorder::EventRecorder() { + _recordFile = NULL; + _recordTimeFile = NULL; + _playbackFile = NULL; + _playbackTimeFile = NULL; + _timeMutex = g_system->createMutex(); + _recorderMutex = g_system->createMutex(); + + _eventCount = 0; + _lastEventCount = 0; + _lastMillis = 0; + +} + +EventRecorder::~EventRecorder() { + deinit(); +} + +void EventRecorder::init() { + Common::String recordModeString = ConfMan.get("record_mode"); + if (recordModeString.compareToIgnoreCase("record") == 0) { + _recordMode = kRecorderRecord; + } else { + if (recordModeString.compareToIgnoreCase("playback") == 0) { + _recordMode = kRecorderPlayback; + } else { + _recordMode = kPassthrough; + } + } + + _recordFileName = ConfMan.get("record_file_name"); + if (_recordFileName.empty()) { + _recordFileName = "record.bin"; + } + _recordTempFileName = ConfMan.get("record_temp_file_name"); + if (_recordTempFileName.empty()) { + _recordTempFileName = "record.tmp"; + } + _recordTimeFileName = ConfMan.get("record_time_file_name"); + if (_recordTimeFileName.empty()) { + _recordTimeFileName = "record.time"; + } + + // recorder stuff + if (_recordMode == kRecorderRecord) { + _recordCount = 0; + _recordTimeCount = 0; + _recordFile = g_system->getSavefileManager()->openForSaving(_recordTempFileName); + _recordTimeFile = g_system->getSavefileManager()->openForSaving(_recordTimeFileName); + _recordSubtitles = ConfMan.getBool("subtitles"); + } + + uint32 sign; + uint32 version; + uint32 randomSourceCount; + if (_recordMode == kRecorderPlayback) { + _playbackCount = 0; + _playbackTimeCount = 0; + _playbackFile = g_system->getSavefileManager()->openForLoading(_recordFileName); + _playbackTimeFile = g_system->getSavefileManager()->openForLoading(_recordTimeFileName); + + if (!_playbackFile) { + warning("Cannot open playback file %s. Playback was switched off", _recordFileName.c_str()); + _recordMode = kPassthrough; + } + + if (!_playbackTimeFile) { + warning("Cannot open playback time file %s. Playback was switched off", _recordTimeFileName.c_str()); + _recordMode = kPassthrough; + } + } + + if (_recordMode == kRecorderPlayback) { + sign = _playbackFile->readUint32LE(); + if (sign != RECORD_SIGNATURE) { + error("Unknown record file signature"); + } + version = _playbackFile->readUint32LE(); + + // conf vars + ConfMan.setBool("subtitles", _playbackFile->readByte() != 0); + + _recordCount = _playbackFile->readUint32LE(); + _recordTimeCount = _playbackFile->readUint32LE(); + randomSourceCount = _playbackFile->readUint32LE(); + for (uint i = 0; i < randomSourceCount; ++i) { + RandomSourceRecord rec; + rec.name = ""; + uint32 sLen = _playbackFile->readUint32LE(); + for (uint j = 0; j < sLen; ++j) { + char c = _playbackFile->readSByte(); + rec.name += c; + } + rec.seed = _playbackFile->readUint32LE(); + _randomSourceRecords.push_back(rec); + } + + _hasPlaybackEvent = false; + } + + g_system->getEventManager()->getEventDispatcher()->registerSource(this, false); + g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 1, false); +} + +void EventRecorder::deinit() { + g_system->getEventManager()->getEventDispatcher()->unregisterSource(this); + g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this); + + g_system->lockMutex(_timeMutex); + g_system->lockMutex(_recorderMutex); + _recordMode = kPassthrough; + g_system->unlockMutex(_timeMutex); + g_system->unlockMutex(_recorderMutex); + + if (_playbackFile != NULL) { + delete _playbackFile; + } + if (_playbackTimeFile != NULL) { + delete _playbackTimeFile; + } + + if (_recordFile != NULL) { + _recordFile->finalize(); + delete _recordFile; + _recordTimeFile->finalize(); + delete _recordTimeFile; + + _playbackFile = g_system->getSavefileManager()->openForLoading(_recordTempFileName); + + assert(_playbackFile); + + _recordFile = g_system->getSavefileManager()->openForSaving(_recordFileName); + _recordFile->writeUint32LE(RECORD_SIGNATURE); + _recordFile->writeUint32LE(RECORD_VERSION); + + // conf vars + _recordFile->writeByte(_recordSubtitles ? 1 : 0); + + _recordFile->writeUint32LE(_recordCount); + _recordFile->writeUint32LE(_recordTimeCount); + + _recordFile->writeUint32LE(_randomSourceRecords.size()); + for (uint i = 0; i < _randomSourceRecords.size(); ++i) { + _recordFile->writeUint32LE(_randomSourceRecords[i].name.size()); + _recordFile->writeString(_randomSourceRecords[i].name); + _recordFile->writeUint32LE(_randomSourceRecords[i].seed); + } + + for (uint i = 0; i < _recordCount; ++i) { + uint32 tempDiff; + Common::Event tempEvent; + readRecord(_playbackFile, tempDiff, tempEvent); + writeRecord(_recordFile, tempDiff, tempEvent); + } + + _recordFile->finalize(); + delete _recordFile; + delete _playbackFile; + + //TODO: remove recordTempFileName'ed file + } + + g_system->deleteMutex(_timeMutex); + g_system->deleteMutex(_recorderMutex); +} + +void EventRecorder::registerRandomSource(Common::RandomSource &rnd, const char *name) { + if (_recordMode == kRecorderRecord) { + RandomSourceRecord rec; + rec.name = name; + rec.seed = rnd.getSeed(); + _randomSourceRecords.push_back(rec); + } + + if (_recordMode == kRecorderPlayback) { + for (uint i = 0; i < _randomSourceRecords.size(); ++i) { + if (_randomSourceRecords[i].name == name) { + rnd.setSeed(_randomSourceRecords[i].seed); + _randomSourceRecords.remove_at(i); + break; + } + } + } +} + +void EventRecorder::processMillis(uint32 &millis) { + uint32 d; + if (_recordMode == kPassthrough) { + return; + } + + g_system->lockMutex(_timeMutex); + if (_recordMode == kRecorderRecord) { + //Simple RLE compression + d = millis - _lastMillis; + if (d >= 0xff) { + _recordTimeFile->writeByte(0xff); + _recordTimeFile->writeUint32LE(d); + } else { + _recordTimeFile->writeByte(d); + } + _recordTimeCount++; + } + + if (_recordMode == kRecorderPlayback) { + if (_recordTimeCount > _playbackTimeCount) { + d = _playbackTimeFile->readByte(); + if (d == 0xff) { + d = _playbackTimeFile->readUint32LE(); + } + millis = _lastMillis + d; + _playbackTimeCount++; + } + } + + _lastMillis = millis; + g_system->unlockMutex(_timeMutex); +} + +bool EventRecorder::notifyEvent(const Common::Event &ev) { + if (_recordMode != kRecorderRecord) + return false; + + Common::StackLock lock(_recorderMutex); + ++_eventCount; + + writeRecord(_recordFile, _eventCount - _lastEventCount, ev); + + _recordCount++; + _lastEventCount = _eventCount; + + return false; +} + +bool EventRecorder::pollEvent(Common::Event &ev) { + if (_recordMode != kRecorderPlayback) + return false; + + Common::StackLock lock(_recorderMutex); + ++_eventCount; + + if (!_hasPlaybackEvent) { + if (_recordCount > _playbackCount) { + readRecord(_playbackFile, const_cast<uint32&>(_playbackDiff), _playbackEvent); + _playbackCount++; + _hasPlaybackEvent = true; + } + } + + if (_hasPlaybackEvent) { + if (_playbackDiff <= (_eventCount - _lastEventCount)) { + switch(_playbackEvent.type) { + case Common::EVENT_MOUSEMOVE: + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONDOWN: + case Common::EVENT_RBUTTONUP: + case Common::EVENT_WHEELUP: + case Common::EVENT_WHEELDOWN: + g_system->warpMouse(_playbackEvent.mouse.x, _playbackEvent.mouse.y); + break; + default: + break; + } + ev = _playbackEvent; + _hasPlaybackEvent = false; + _lastEventCount = _eventCount; + return true; + } + } + + return false; +} + +} // end of namespace Common + diff --git a/common/EventRecorder.h b/common/EventRecorder.h new file mode 100644 index 0000000000..e6ea961737 --- /dev/null +++ b/common/EventRecorder.h @@ -0,0 +1,106 @@ +/* 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef COMMON_EVENTRECORDER_H +#define COMMON_EVENTRECORDER_H + +#include "common/scummsys.h" +#include "common/events.h" +#include "common/singleton.h" +#include "common/savefile.h" +#include "common/mutex.h" +#include "common/array.h" + +#define g_eventRec (Common::EventRecorder::instance()) + +namespace Common { + +/** + * Our generic event recorder. + * + * TODO: Add more documentation. + */ +class EventRecorder : private EventSource, private EventObserver, public Singleton<EventRecorder> { + friend class Common::Singleton<SingletonBaseType>; + EventRecorder(); + ~EventRecorder(); +public: + void init(); + void deinit(); + + /** Register random source so it can be serialized in game test purposes */ + void registerRandomSource(Common::RandomSource &rnd, const char *name); + + /** TODO: Add documentation, this is only used by the backend */ + void processMillis(uint32 &millis); + +private: + bool notifyEvent(const Common::Event &ev); + bool pollEvent(Common::Event &ev); + bool allowMapping() const { return false; } + + class RandomSourceRecord { + public: + Common::String name; + uint32 seed; + }; + Common::Array<RandomSourceRecord> _randomSourceRecords; + + bool _recordSubtitles; + volatile uint32 _recordCount; + volatile uint32 _lastRecordEvent; + volatile uint32 _recordTimeCount; + Common::OutSaveFile *_recordFile; + Common::OutSaveFile *_recordTimeFile; + Common::MutexRef _timeMutex; + Common::MutexRef _recorderMutex; + volatile uint32 _lastMillis; + + volatile uint32 _playbackCount; + volatile uint32 _playbackDiff; + volatile bool _hasPlaybackEvent; + volatile uint32 _playbackTimeCount; + Common::Event _playbackEvent; + Common::InSaveFile *_playbackFile; + Common::InSaveFile *_playbackTimeFile; + + volatile uint32 _eventCount; + volatile uint32 _lastEventCount; + + enum RecordMode { + kPassthrough = 0, + kRecorderRecord = 1, + kRecorderPlayback = 2 + }; + volatile RecordMode _recordMode; + Common::String _recordFileName; + Common::String _recordTempFileName; + Common::String _recordTimeFileName; +}; + +} // end of namespace Common + +#endif + diff --git a/common/events.h b/common/events.h index 82b85e60ea..34769729ea 100644 --- a/common/events.h +++ b/common/events.h @@ -31,6 +31,9 @@ #include "common/rect.h" #include "common/noncopyable.h" +#include "common/list.h" +#include "common/singleton.h" + namespace Common { /** @@ -126,6 +129,190 @@ struct Event { Event() : type(EVENT_INVALID), synthetic(false) {} }; +/** + * A source of Events. + * + * An example for this is OSystem, it provides events created by the system + * and or user. + */ +class EventSource { +public: + virtual ~EventSource() {} + + /** + * Queries a event from the source. + * + * @param event a reference to the event struct, where the event should be stored. + * @return true if an event was polled, false otherwise. + */ + virtual bool pollEvent(Event &event) = 0; + + /** + * Checks whether events from this source are allowed to be mapped. + * + * Possible event sources not allowing mapping are: the event recorder/player and/or + * the EventManager, which allows user events to be pushed. + * + * By default we allow mapping for every event source. + */ + virtual bool allowMapping() const { return true; } +}; + +/** + * An artificial event source. This is class is used as an event source, which is + * made up by client specific events. + * + * Example usage cases for this are the Keymapper or the DefaultEventManager. + */ +class ArtificialEventSource : public EventSource { +protected: + Common::Queue<Common::Event> _artificialEventQueue; +public: + void addEvent(const Common::Event &ev) { + _artificialEventQueue.push(ev); + } + + bool pollEvent(Common::Event &ev) { + if (!_artificialEventQueue.empty()) { + ev = _artificialEventQueue.pop(); + return true; + } else { + return false; + } + } + + /** + * By default an artificial event source prevents its events + * from being mapped. + */ + virtual bool allowMapping() const { return false; } +}; + +/** + * Object which catches and processes Events. + * + * An example for this is the Engine object, it is catching events and processing them. + */ +class EventObserver { +public: + virtual ~EventObserver() {} + + /** + * Notifies the source of an incoming event. + * + * An obeser is supposed to eat the event, with returning true, when + * it might want prevent other observers from preventing to receive + * the event. An usage example here is the keymapper: + * If it processes an Event, it should 'eat' it and create a new + * event, which the EventDispatcher will then catch. + * + * @param event the event, which is incoming. + * @return true if this observer uses this event, false otherwise. + */ + virtual bool notifyEvent(const Event &event) = 0; +}; + +/** + * A event mapper, which will map events to others. + * + * An example for this is the Keymapper. + */ +class EventMapper : public EventSource, public EventObserver { +public: + /** For event mappers resulting events should never be mapped */ + bool allowMapping() const { return false; } +}; + +/** + * Dispatches events from various sources to various observers. + * + * EventDispatcher is using a priority based approach. Observers + * with higher priority will be notified before observers with + * lower priority. Because of the possibility that oberservers + * might 'eat' events, not all observers might be notified. + * + * Another speciality is the support for a event mapper, which + * will catch events and create new events out of them. This + * mapper will be processed before an event is sent to the + * observers. + */ +class EventDispatcher { +public: + EventDispatcher(); + ~EventDispatcher(); + + /** + * Tries to catch events from the registered event + * sources and dispatch them to the observers. + * + * This dispatches *all* events the sources offer. + */ + void dispatch(); + + /** + * Registers an event mapper with the dispatcher. + * + * The ownership of the "mapper" variable will pass + * to the EventDispatcher, thus it will be deleted + * with "delete", when EventDispatcher is destroyed. + * + * Note there is only one mapper per EventDispatcher + * possible, thus when this method is called twice, + * the former mapper will be destroied. + */ + void registerMapper(EventMapper *mapper); + + /** + * Queries the setup event mapper. + */ + EventMapper *queryMapper() const { return _mapper; } + + /** + * Registers a new EventSource with the Dispatcher. + */ + void registerSource(EventSource *source, bool autoFree); + + /** + * Unregisters a EventSource. + * + * This takes the "autoFree" flag passed to registerSource into account. + */ + void unregisterSource(EventSource *source); + + /** + * Registers a new EventObserver with the Dispatcher. + */ + void registerObserver(EventObserver *obs, uint priority, bool autoFree); + + /** + * Unregisters a EventObserver. + * + * This takes the "autoFree" flag passed to registerObserver into account. + */ + void unregisterObserver(EventObserver *obs); +private: + EventMapper *_mapper; + + struct Entry { + bool autoFree; + }; + + struct SourceEntry : public Entry { + EventSource *source; + }; + + Common::List<SourceEntry> _sources; + + struct ObserverEntry : public Entry { + uint priority; + EventObserver *observer; + }; + + Common::List<ObserverEntry> _observers; + + void dispatchEvent(const Event &event); +}; + class Keymapper; /** @@ -161,11 +348,6 @@ public: */ virtual void pushEvent(const Common::Event &event) = 0; - /** Register random source so it can be serialized in game test purposes **/ - virtual void registerRandomSource(Common::RandomSource &rnd, const char *name) = 0; - - virtual void processMillis(uint32 &millis) = 0; - /** Return the current mouse position */ virtual Common::Point getMousePos() const = 0; @@ -209,9 +391,21 @@ public: virtual Common::Keymapper *getKeymapper() = 0; #endif -protected: + enum { + /** + * Priority of the event manager, for now it's lowest since it eats + * *all* events, we might to change that in the future though. + */ + kEventManPriority = 0 + }; - Common::Queue<Common::Event> artificialEventQueue; + /** + * Returns the underlying EventDispatcher. + */ + EventDispatcher *getEventDispatcher() { return &_dispatcher; } + +protected: + EventDispatcher _dispatcher; }; } // End of namespace Common diff --git a/common/file.cpp b/common/file.cpp index 7836a7d4a8..dd4281bd03 100644 --- a/common/file.cpp +++ b/common/file.cpp @@ -120,15 +120,6 @@ bool File::isOpen() const { return _handle != NULL; } -bool File::ioFailed() const { - return !_handle || (eos() || err()); -} - -void File::clearIOFailed() { - if (_handle) - _handle->clearErr(); -} - bool File::err() const { assert(_handle); return _handle->err(); diff --git a/common/file.h b/common/file.h index a98d23a96a..6ac633c462 100644 --- a/common/file.h +++ b/common/file.h @@ -126,19 +126,6 @@ public: */ const char *getName() const { return _name.c_str(); } - /** - * DEPRECATED: Use err() or eos() instead. - * Returns true if any I/O failure occurred or the end of the - * stream was reached while reading. - */ - bool ioFailed() const; - - /** - * DEPRECATED: Don't use this unless you are still using ioFailed(). - * Reset the I/O error status. - */ - void clearIOFailed(); - bool err() const; // implement abstract Stream method void clearErr(); // implement abstract Stream method bool eos() const; // implement abstract SeekableReadStream method diff --git a/common/module.mk b/common/module.mk index bdf9590962..d4f92f0791 100644 --- a/common/module.mk +++ b/common/module.mk @@ -5,6 +5,8 @@ MODULE_OBJS := \ config-file.o \ config-manager.o \ debug.o \ + EventDispatcher.o \ + EventRecorder.o \ file.o \ fs.o \ hashmap.o \ diff --git a/common/str.cpp b/common/str.cpp index 0d24f2edac..b422a34c3c 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -28,6 +28,8 @@ #include "common/memorypool.h" +#include <stdarg.h> + #if !defined(__SYMBIAN32__) #include <new> #endif @@ -435,6 +437,33 @@ uint String::hash() const { return hashit(c_str()); } +// static +String String::printf(const char *fmt, ...) { + String output; + assert(output.isStorageIntern()); + + va_list va; + va_start(va, fmt); + int len = vsnprintf(output._str, _builtinCapacity, fmt, va); + va_end(va); + + if (len < (int)_builtinCapacity) { + // vsnprintf succeeded + output._size = len; + } else { + // vsnprintf didn't have enough space, so grow buffer + output.ensureCapacity(len, false); + va_start(va, fmt); + int len2 = vsnprintf(output._str, len+1, fmt, va); + va_end(va); + assert(len == len2); + output._size = len2; + } + + return output; +} + + #pragma mark - bool String::operator ==(const String &x) const { diff --git a/common/str.h b/common/str.h index b7dbd6535a..2fc88e9c91 100644 --- a/common/str.h +++ b/common/str.h @@ -218,6 +218,11 @@ public: uint hash() const; + /** + * Printf-like function. Returns a formatted String. + */ + static Common::String printf(const char *fmt, ...) GCC_PRINTF(1,2); + public: typedef char * iterator; typedef const char * const_iterator; diff --git a/common/stream.h b/common/stream.h index 80e2978fb9..9b03c20bd2 100644 --- a/common/stream.h +++ b/common/stream.h @@ -179,7 +179,7 @@ public: * DEPRECATED * Default implementation for backward compatibility */ - virtual bool ioFailed() { return (eos() || err()); } + inline bool ioFailed() { return (eos() || err()); } /** * Read an unsigned byte from the stream and return it. diff --git a/common/unarj.cpp b/common/unarj.cpp index 428b4a426e..0312cc5b08 100644 --- a/common/unarj.cpp +++ b/common/unarj.cpp @@ -105,7 +105,7 @@ public: void decode(int32 origsize); void decode_f(int32 origsize); - MemoryReadStream *_compressed; + BufferedReadStream *_compressed; MemoryWriteStream *_outstream; //protected: @@ -360,6 +360,9 @@ bool ArjFile::open(const Common::String &filename) { return false; ArjHeader *hdr = _headers[_fileMap[filename]]; + + // TODO: It would be good if ArjFile could decompress files in a streaming + // mode, so it would not need to pre-allocate the entire output. byte *uncompressedData = (byte *)malloc(hdr->origSize); File archiveFile; @@ -372,10 +375,11 @@ bool ArjFile::open(const Common::String &filename) { } else { ArjDecoder *decoder = new ArjDecoder(hdr); - byte *compressedData = (byte *)malloc(hdr->compSize); - archiveFile.read(compressedData, hdr->compSize); - - decoder->_compressed = new MemoryReadStream(compressedData, hdr->compSize, true); + // TODO: It might not be appropriate to use this wrapper inside ArjFile. + // If reading from archiveFile directly is too slow to be usable, + // maybe the filesystem code should instead wrap its files + // in a BufferedReadStream. + decoder->_compressed = new BufferedReadStream(&archiveFile, 4096, false); decoder->_outstream = new MemoryWriteStream(uncompressedData, hdr->origSize); if (hdr->method == 1 || hdr->method == 2 || hdr->method == 3) |