diff --git a/Makefile b/Makefile index 8189107a9c8..291826a8dbc 100644 --- a/Makefile +++ b/Makefile @@ -20,21 +20,29 @@ MODULE_DIRS := # Load the make rules generated by configure -include config.mk -CXXFLAGS:= -Wall $(CXXFLAGS) -# Turn off some annoying and not-so-useful warnings -CXXFLAGS+= -Wno-long-long -Wno-multichar -Wno-unknown-pragmas -Wno-reorder -Wno-unused-parameter -# Enable even more warnings... -#CXXFLAGS+= -Wpointer-arith -Wcast-qual -Wcast-align -#CXXFLAGS+= -Wshadow -Wimplicit -Wnon-virtual-dtor -Wwrite-strings +ifeq "$(HAVE_GCC)" "1" + CXXFLAGS:= -Wall $(CXXFLAGS) + # Turn off some annoying and not-so-useful warnings + CXXFLAGS+= -Wno-long-long -Wno-multichar -Wno-unknown-pragmas -Wno-reorder + # Enable even more warnings... + CXXFLAGS+= -Wpointer-arith -Wcast-qual -Wcast-align + CXXFLAGS+= -Wshadow -Wimplicit -Wnon-virtual-dtor -Wwrite-strings -# Disable RTTI and exceptions, and enabled checking of pointers returned by "new" -#CXXFLAGS+= -fno-rtti -fno-exceptions -fcheck-new + # Currently we disable this gcc flag, since it will also warn in cases, + # where using GCC_PRINTF (means: __attribute__((format(printf, x, y)))) + # is not possible, thus it would fail compiliation with -Werror without + # being helpful. + #CXXFLAGS+= -Wmissing-format-attribute -# There is a nice extra warning that flags variables that are potentially -# used before being initialized. Very handy to catch a certain kind of -# bugs. Unfortunately, it only works when optimizations are turned on, -# which is why we normally don't use it. -#CXXFLAGS+= -O -Wuninitialized + # Disable RTTI and exceptions, and enabled checking of pointers returned by "new" + CXXFLAGS+= -fno-rtti -fno-exceptions -fcheck-new + + # There is a nice extra warning that flags variables that are potentially + # used before being initialized. Very handy to catch a certain kind of + # bugs. Unfortunately, it only works when optimizations are turned on, + # which is why we normally don't use it. + #CXXFLAGS+= -O -Wuninitialized +endif ####################################################################### # Default commands - put the necessary replacements in config.mk # @@ -62,10 +70,15 @@ config.h config.mk: $(srcdir)/configure ifeq "$(findstring config.mk,$(MAKEFILE_LIST))" "config.mk" @echo "Running $(srcdir)/configure with the last specified parameters" @sleep 2 - LDFLAGS="$(SAVED_LDFLAGS)" CXX="$(SAVED_CXX)" CXXFLAGS="$(SAVED_CXXFLAGS)" CPPFLAGS="$(SAVED_CPPFLAGS)" \ - $(srcdir)/configure $(SAVED_CONFIGFLAGS) + LDFLAGS="$(SAVED_LDFLAGS)" CXX="$(SAVED_CXX)" \ + CXXFLAGS="$(SAVED_CXXFLAGS)" CPPFLAGS="$(SAVED_CPPFLAGS)" \ + ASFLAGS="$(SAVED_ASFLAGS)" WINDRESFLAGS="$(SAVED_WINDRESFLAGS)" \ + $(srcdir)/configure $(SAVED_CONFIGFLAGS) else $(error You need to run $(srcdir)/configure before you can run make. Check $(srcdir)/configure --help for a list of parameters) endif -include $(srcdir)/ports.mk +ifneq ($(origin port_mk), undefined) +include $(srcdir)/$(port_mk) +endif + diff --git a/Makefile.common b/Makefile.common index ee28180fde9..fc48f39a279 100644 --- a/Makefile.common +++ b/Makefile.common @@ -55,6 +55,8 @@ ifneq ($(findstring $(MAKEFLAGS),s),s) ifneq ($(VERBOSE_BUILD),1) ifneq ($(VERBOSE_BUILD),yes) QUIET_CXX = @echo ' ' C++ ' ' $@; +QUIET_AS = @echo ' ' AS ' ' $@; +QUIET_NASM = @echo ' ' NASM ' ' $@; QUIET_AR = @echo ' ' AR ' ' $@; QUIET_RANLIB = @echo ' ' RANLIB ' ' $@; QUIET_PLUGIN = @echo ' ' PLUGIN ' ' $@; @@ -75,26 +77,40 @@ clean: $(RM_REC) $(DEPDIRS) $(RM) $(OBJS) $(EXECUTABLE) -ifndef HAVE_GCC3 -# If you use GCC, disable the above and enable this for intelligent + +# +# The build rules for object files. +# + +ifdef CXX_UPDATE_DEP_FLAG + +# Build rule for C++ files. Makes use of CXX_UPDATE_DEP_FLAG for advanced # dependency tracking. %.o: %.cpp $(QUIET)$(MKDIR) $(*D)/$(DEPDIR) - $(QUIET_CXX)$(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d2" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o - $(QUIET)$(ECHO) "$(*D)/" > $(*D)/$(DEPDIR)/$(*F).d - $(QUIET)$(CAT) "$(*D)/$(DEPDIR)/$(*F).d2" >> "$(*D)/$(DEPDIR)/$(*F).d" - $(QUIET)$(RM) "$(*D)/$(DEPDIR)/$(*F).d2" -else -# If you even have GCC 3.x, you can use this build rule, which is safer; the above -# rule can get you into a bad state if you Ctrl-C at the wrong moment. -# Also, with this GCC inserts additional dummy rules for the involved headers, -# which ensures a smooth compilation even if said headers become obsolete. -%.o: %.cpp - $(QUIET)$(MKDIR) $(*D)/$(DEPDIR) - $(QUIET_CXX)$(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o + $(QUIET_CXX)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o + +# Build rule for Objective-C files. Strictly speaking, this is for OS X only. %.o: %.m $(QUIET)$(MKDIR) $(*D)/$(DEPDIR) - $(QUIET_CXX)$(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(OBJCFLAGS) -c $(<) -o $*.o + $(QUIET_CXX)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(OBJCFLAGS) -c $(<) -o $*.o + +else + +# Dumb compile rule, for C++ compilers that don't allow dependency tracking or +# where it is broken (such as GCC 2.95). +.cpp.o: + $(QUIET_CXX)$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o + +endif + + +%.o: %.s + $(QUIET_AS)$(AS) $(ASFLAGS) $(<) -o $*.o + +ifdef HAVE_NASM +%.o: %.asm + $(QUIET_NASM)$(NASM) -O1 $(NASMFLAGS) -g -o $*.o $(<) endif # Include the dependency tracking files. @@ -134,25 +150,6 @@ CXXFLAGS+= -DRESIDUAL_SVN_REVISION=\"$(VER_SVNREV)\" endif -###################################################################### -# Create the files that depend on the version -###################################################################### - -VERSION_FILES = \ - $(srcdir)/dists/iphone/Info.plist \ - $(srcdir)/dists/macosx/Info.plist - -$(VERSION_FILES): %: %.in - @echo "Creating $@" - @cat $< | sed \ - -e "s/@VER_MAJOR@/$(VER_MAJOR)/g" \ - -e "s/@VER_MINOR@/$(VER_MINOR)/g" \ - -e "s/@VER_PATCH@/$(VER_PATCH)/g" \ - -e "s/@VER_EXTRA@/$(VER_EXTRA)/g" \ - -e "s/@VERSION@/$(VERSION)/g" \ - > $@ - - ###################################################################### # Distribution settings ###################################################################### @@ -198,10 +195,16 @@ dist-src: \ @#RPM-src? @#DEB-src? +# Common files +DIST_FILES_DOCS:=$(addprefix $(srcdir)/,AUTHORS COPYING COPYING.LGPL COPYRIGHT NEWS README) # Themes files DIST_FILES_THEMES:=$(addprefix $(srcdir)/gui/themes/,scummmodern.zip) +# Engine data files +DIST_FILES_ENGINEDATA= +DIST_FILES_ENGINEDATA:=$(addprefix $(srcdir)/dists/engine-data/,$(DIST_FILES_ENGINEDATA)) + # Plugin files DIST_FILES_PLUGINS:=$(addprefix $(srcdir)/,$(PLUGINS)) diff --git a/backends/base-backend.h b/backends/base-backend.h index 6398fafdff3..7ebedcdd75f 100644 --- a/backends/base-backend.h +++ b/backends/base-backend.h @@ -27,10 +27,9 @@ #define BACKENDS_BASE_BACKEND_H #include "common/system.h" -#include "common/fs.h" #include "backends/events/default/default-events.h" -class BaseBackend : public OSystem, EventProvider { +class BaseBackend : public OSystem, Common::EventSource { public: virtual Common::EventManager *getEventManager(); virtual void displayMessageOnOSD(const char *msg); diff --git a/backends/events/default/default-events.cpp b/backends/events/default/default-events.cpp index 60c17fd30cc..88072d5aba1 100644 --- a/backends/events/default/default-events.cpp +++ b/backends/events/default/default-events.cpp @@ -35,242 +35,38 @@ #include "engines/engine.h" #include "gui/message.h" -#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, 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; - } -} - -DefaultEventManager::DefaultEventManager(EventProvider *boss) : - _boss(boss), +DefaultEventManager::DefaultEventManager(Common::EventSource *boss) : _buttonState(0), _modifierState(0), _shouldQuit(false), _shouldRTL(false), _confirmExitDialogActive(false) { - assert(_boss); + assert(boss); - _recordFile = NULL; - _recordTimeFile = NULL; - _playbackFile = NULL; - _playbackTimeFile = NULL; - _timeMutex = g_system->createMutex(); - _recorderMutex = g_system->createMutex(); + _dispatcher.registerSource(boss, false); + _dispatcher.registerSource(&_artificialEventSource, false); - _eventCount = 0; - _lastEventCount = 0; - _lastMillis = 0; - - 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"; - } + _dispatcher.registerObserver(this, kEventManPriority, false); // Reset key repeat _currentKeyDown.keycode = 0; - // 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; - } - #ifdef ENABLE_VKEYBD _vk = new Common::VirtualKeyboard(); #endif #ifdef ENABLE_KEYMAPPER _keymapper = new Common::Keymapper(this); + // EventDispatcher will automatically free the keymapper + _dispatcher.registerMapper(_keymapper); _remap = false; #endif } DefaultEventManager::~DefaultEventManager() { -#ifdef ENABLE_KEYMAPPER - delete _keymapper; -#endif #ifdef ENABLE_VKEYBD delete _vk; #endif - g_system->lockMutex(_timeMutex); - g_system->lockMutex(_recorderMutex); - _recordMode = kPassthrough; - g_system->unlockMutex(_timeMutex); - g_system->unlockMutex(_recorderMutex); - - if (!artificialEventQueue.empty()) - artificialEventQueue.clear(); - - 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 DefaultEventManager::init() { @@ -283,145 +79,14 @@ void DefaultEventManager::init() { #endif } -bool DefaultEventManager::playback(Common::Event &event) { - - if (!_hasPlaybackEvent) { - if (_recordCount > _playbackCount) { - readRecord(_playbackFile, const_cast(_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; - } - event = _playbackEvent; - _hasPlaybackEvent = false; - _lastEventCount = _eventCount; - return true; - } - } - - return false; -} - -void DefaultEventManager::record(Common::Event &event) { - writeRecord(_recordFile, _eventCount - _lastEventCount, event); - - _recordCount++; - _lastEventCount = _eventCount; -} - -void DefaultEventManager::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 DefaultEventManager::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 DefaultEventManager::pollEvent(Common::Event &event) { uint32 time = g_system->getMillis(); - bool result; + bool result = false; - if (!artificialEventQueue.empty()) { - event = artificialEventQueue.pop(); + _dispatcher.dispatch(); + if (!_eventQueue.empty()) { + event = _eventQueue.pop(); result = true; - } else { - result = _boss->pollEvent(event); - -#ifdef ENABLE_KEYMAPPER - if (result) { - // send key press events to keymapper - if (event.type == Common::EVENT_KEYDOWN) { - if (_keymapper->mapKeyDown(event.kbd)) { - result = false; - } - } else if (event.type == Common::EVENT_KEYUP) { - if (_keymapper->mapKeyUp(event.kbd)) { - result = false; - } - } - } -#endif - } - - if (_recordMode != kPassthrough) { - - g_system->lockMutex(_recorderMutex); - _eventCount++; - - if (_recordMode == kRecorderPlayback) { - if (event.type != Common::EVENT_QUIT) { - result = playback(event); - } - } else { - if (_recordMode == kRecorderRecord) { - if (result) { - record(event); - } - } - } - g_system->unlockMutex(_recorderMutex); } if (result) { @@ -497,6 +162,16 @@ bool DefaultEventManager::pollEvent(Common::Event &event) { } } #endif + else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE) { + // WORKAROUND: Some engines incorrectly attempt to use the + // ascii value instead of the keycode to detect the backspace + // key (a non-portable behavior). This fails at least on + // Mac OS X, possibly also on other systems. + // As a workaround, we force the ascii value for backspace + // key pressed. A better fix would be for engines to stop + // making invalid assumptions about ascii values. + event.kbd.ascii = Common::KEYCODE_BACKSPACE; + } break; case Common::EVENT_KEYUP: @@ -598,13 +273,12 @@ bool DefaultEventManager::pollEvent(Common::Event &event) { } void DefaultEventManager::pushEvent(const Common::Event &event) { - // If already received an EVENT_QUIT, don't add another one if (event.type == Common::EVENT_QUIT) { if (!_shouldQuit) - artificialEventQueue.push(event); + _artificialEventSource.addEvent(event); } else - artificialEventQueue.push(event); + _artificialEventSource.addEvent(event); } #endif // !defined(DISABLE_DEFAULT_EVENTMANAGER) diff --git a/backends/events/default/default-events.h b/backends/events/default/default-events.h index 69e0ec29d3c..ac31010ed91 100644 --- a/backends/events/default/default-events.h +++ b/backends/events/default/default-events.h @@ -27,8 +27,6 @@ #define BACKEND_EVENTS_DEFAULT_H #include "common/events.h" -#include "common/savefile.h" -#include "common/mutex.h" #include "common/queue.h" namespace Common { @@ -41,21 +39,7 @@ namespace Common { } -class EventProvider { -public: - virtual ~EventProvider() {} - /** - * Get the next event in the event queue. - * @param event point to an Common::Event struct, which will be filled with the event data. - * @return true if an event was retrieved. - */ - virtual bool pollEvent(Common::Event &event) = 0; -}; - - -class DefaultEventManager : public Common::EventManager { - EventProvider *_boss; - +class DefaultEventManager : public Common::EventManager, Common::EventObserver { #ifdef ENABLE_VKEYBD Common::VirtualKeyboard *_vk; #endif @@ -65,7 +49,13 @@ class DefaultEventManager : public Common::EventManager { bool _remap; #endif - Common::Queue _artificialEventQueue; + Common::ArtificialEventSource _artificialEventSource; + + Common::Queue _eventQueue; + bool notifyEvent(const Common::Event &ev) { + _eventQueue.push(ev); + return true; + } Common::Point _mousePos; int _buttonState; @@ -74,44 +64,6 @@ class DefaultEventManager : public Common::EventManager { bool _shouldRTL; bool _confirmExitDialogActive; - class RandomSourceRecord { - public: - Common::String name; - uint32 seed; - }; - Common::Array _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; - // for continuous events (keyDown) enum { kKeyRepeatInitialDelay = 400, @@ -124,18 +76,13 @@ class DefaultEventManager : public Common::EventManager { int keycode; } _currentKeyDown; uint32 _keyRepeatTime; - - void record(Common::Event &event); - bool playback(Common::Event &event); public: - DefaultEventManager(EventProvider *boss); + DefaultEventManager(Common::EventSource *boss); ~DefaultEventManager(); virtual void init(); virtual bool pollEvent(Common::Event &event); virtual void pushEvent(const Common::Event &event); - virtual void registerRandomSource(Common::RandomSource &rnd, const char *name); - virtual void processMillis(uint32 &millis); virtual Common::Point getMousePos() const { return _mousePos; } virtual int getButtonState() const { return _buttonState; } @@ -143,6 +90,9 @@ public: virtual int shouldQuit() const { return _shouldQuit; } virtual int shouldRTL() const { return _shouldRTL; } virtual void resetRTL() { _shouldRTL = false; } +#ifdef FORCE_RTL + virtual void resetQuit() { _shouldQuit = false; } +#endif #ifdef ENABLE_KEYMAPPER virtual Common::Keymapper *getKeymapper() { return _keymapper; } diff --git a/backends/fs/abstract-fs.cpp b/backends/fs/abstract-fs.cpp index 2b8d328f59e..ba8794b8ac5 100644 --- a/backends/fs/abstract-fs.cpp +++ b/backends/fs/abstract-fs.cpp @@ -26,7 +26,7 @@ const char *AbstractFSNode::lastPathComponent(const Common::String &str, const char sep) { // TODO: Get rid of this eventually! Use Common::lastPathComponent instead - if(str.empty()) + if (str.empty()) return ""; const char *start = str.c_str(); diff --git a/backends/fs/amigaos4/amigaos4-fs.cpp b/backends/fs/amigaos4/amigaos4-fs.cpp index acee1607954..9e4943faf25 100644 --- a/backends/fs/amigaos4/amigaos4-fs.cpp +++ b/backends/fs/amigaos4/amigaos4-fs.cpp @@ -43,8 +43,6 @@ #define ENTER() /* debug(6, "Enter") */ #define LEAVE() /* debug(6, "Leave") */ -const uint32 kExAllBufferSize = 40960; // TODO: is this okay for sure? - /** * Implementation of the ScummVM file system API. * @@ -57,38 +55,41 @@ protected: Common::String _sPath; bool _bIsDirectory; bool _bIsValid; + uint32 _nProt; /** - * Obtain the FileInfoBlock protection value for this FSNode, - * as defined in the header. - * - * @return -1 if there were errors, 0 or a positive integer otherwise. + * Creates a list with all the volumes present in the root node. */ - virtual int getFibProtection() const; + virtual AbstractFSList listVolumes() const; public: /** - * Creates a AmigaOSFilesystemNode with the root node as path. + * Creates an AmigaOSFilesystemNode with the root node as path. */ AmigaOSFilesystemNode(); /** - * Creates a AmigaOSFilesystemNode for a given path. + * Creates an AmigaOSFilesystemNode for a given path. * * @param path Common::String with the path the new node should point to. */ AmigaOSFilesystemNode(const Common::String &p); /** - * FIXME: document this constructor. + * Creates an AmigaOSFilesystemNode given its lock and display name + * + * @param pLock BPTR to the lock. + * @param pDisplayName name to be used for display, in case not supplied the FilePart() of the filename will be used. + * + * @note This shouldn't even be public as it's only internally, at best it should have been protected if not private */ AmigaOSFilesystemNode(BPTR pLock, const char *pDisplayName = 0); - /** - * Copy constructor. - * - * @note Needed because it duplicates the file lock - */ + /** + * Copy constructor. + * + * @note Needed because it duplicates the file lock + */ AmigaOSFilesystemNode(const AmigaOSFilesystemNode &node); /** @@ -110,11 +111,6 @@ public: virtual Common::SeekableReadStream *createReadStream(); virtual Common::WriteStream *createWriteStream(); - - /** - * Creates a list with all the volumes present in the root node. - */ - virtual AbstractFSList listVolumes() const; }; /** @@ -149,6 +145,7 @@ AmigaOSFilesystemNode::AmigaOSFilesystemNode() { _bIsDirectory = true; _sPath = ""; _pFileLock = 0; + _nProt = 0; // protection is ignored for the root volume LEAVE(); } @@ -169,37 +166,27 @@ AmigaOSFilesystemNode::AmigaOSFilesystemNode(const Common::String &p) { _pFileLock = 0; _bIsDirectory = false; - struct FileInfoBlock *fib = (struct FileInfoBlock *)IDOS->AllocDosObject(DOS_FIB, NULL); - if (!fib) { - debug(6, "FileInfoBlock is NULL"); - LEAVE(); - return; - } - // Check whether the node exists and if it is a directory - BPTR pLock = IDOS->Lock((STRPTR)_sPath.c_str(), SHARED_LOCK); - if (pLock) { - if (IDOS->Examine(pLock, fib) != DOSFALSE) { - if (FIB_IS_DRAWER(fib)) { - _bIsDirectory = true; - _pFileLock = IDOS->DupLock(pLock); - _bIsValid = (_pFileLock != 0); + struct ExamineData * pExd = IDOS->ExamineObjectTags(EX_StringNameInput,_sPath.c_str(),TAG_END); + if (pExd) { + _nProt = pExd->Protection; + if (EXD_IS_DIRECTORY(pExd)) { + _bIsDirectory = true; + _pFileLock = IDOS->Lock((CONST_STRPTR)_sPath.c_str(), SHARED_LOCK);; + _bIsValid = (_pFileLock != 0); - // Add a trailing slash if it is needed - const char c = _sPath.lastChar(); - if (c != '/' && c != ':') - _sPath += '/'; - } - else { - //_bIsDirectory = false; - _bIsValid = true; - } + // Add a trailing slash if it is needed + const char c = _sPath.lastChar(); + if (c != '/' && c != ':') + _sPath += '/'; + } else { + //_bIsDirectory = false; + _bIsValid = true; } - IDOS->UnLock(pLock); + IDOS->FreeDosObject(DOS_EXAMINEDATA, pExd); } - IDOS->FreeDosObject(DOS_FIB, fib); LEAVE(); } @@ -232,15 +219,10 @@ AmigaOSFilesystemNode::AmigaOSFilesystemNode(BPTR pLock, const char *pDisplayNam _bIsValid = false; _bIsDirectory = false; - struct FileInfoBlock *fib = (struct FileInfoBlock *)IDOS->AllocDosObject(DOS_FIB, NULL); - if (!fib) { - debug(6, "FileInfoBlock is NULL"); - LEAVE(); - return; - } - - if (IDOS->Examine(pLock, fib) != DOSFALSE) { - if (FIB_IS_DRAWER(fib)) { + struct ExamineData * pExd = IDOS->ExamineObjectTags(EX_FileLockInput,pLock,TAG_END); + if (pExd) { + _nProt = pExd->Protection; + if (EXD_IS_DIRECTORY(pExd)) { _bIsDirectory = true; _pFileLock = IDOS->DupLock(pLock); _bIsValid = _pFileLock != 0; @@ -248,25 +230,29 @@ AmigaOSFilesystemNode::AmigaOSFilesystemNode(BPTR pLock, const char *pDisplayNam const char c = _sPath.lastChar(); if (c != '/' && c != ':') _sPath += '/'; - } - else { + } else { //_bIsDirectory = false; _bIsValid = true; } - } - IDOS->FreeDosObject(DOS_FIB, fib); + IDOS->FreeDosObject(DOS_EXAMINEDATA, pExd); + } else { + debug(6, "ExamineObject() returned NULL"); + } + LEAVE(); } // We need the custom copy constructor because of DupLock() -AmigaOSFilesystemNode::AmigaOSFilesystemNode(const AmigaOSFilesystemNode& node) { +AmigaOSFilesystemNode::AmigaOSFilesystemNode(const AmigaOSFilesystemNode& node) +: AbstractFSNode() { ENTER(); _sDisplayName = node._sDisplayName; _bIsValid = node._bIsValid; _bIsDirectory = node._bIsDirectory; _sPath = node._sPath; _pFileLock = IDOS->DupLock(node._pFileLock); + _nProt = node._nProt; LEAVE(); } @@ -279,26 +265,34 @@ AmigaOSFilesystemNode::~AmigaOSFilesystemNode() { bool AmigaOSFilesystemNode::exists() const { ENTER(); - if(_sPath.empty()) + if (_sPath.empty()) return false; bool nodeExists = false; - struct FileInfoBlock *fib = (struct FileInfoBlock *)IDOS->AllocDosObject(DOS_FIB, NULL); - if (!fib) { - debug(6, "FileInfoBlock is NULL"); - LEAVE(); - return false; - } - - BPTR pLock = IDOS->Lock((STRPTR)_sPath.c_str(), SHARED_LOCK); + // previously we were trying to examine the node in order + // to determine if the node exists or not. + // I don't see the point : once you have been granted a + // lock on it then it means it exists... + // + // ============================= Old code + // BPTR pLock = IDOS->Lock((STRPTR)_sPath.c_str(), SHARED_LOCK); + // if (pLock) + // { + // if (IDOS->Examine(pLock, fib) != DOSFALSE) + // nodeExists = true; + // IDOS->UnLock(pLock); + // } + // + // IDOS->FreeDosObject(DOS_FIB, fib); + // + // ============================= New code + BPTR pLock = IDOS->Lock(_sPath.c_str(), SHARED_LOCK); if (pLock) { - if (IDOS->Examine(pLock, fib) != DOSFALSE) - nodeExists = true; + nodeExists = true; IDOS->UnLock(pLock); } - IDOS->FreeDosObject(DOS_FIB, fib); LEAVE(); return nodeExists; } @@ -323,8 +317,10 @@ AbstractFSNode *AmigaOSFilesystemNode::getChild(const Common::String &n) const { bool AmigaOSFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const { ENTER(); + bool ret = false; //TODO: honor the hidden flag + // There is nothing like a hidden flag under AmigaOS... if (!_bIsValid) { debug(6, "Invalid node"); @@ -345,85 +341,49 @@ bool AmigaOSFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, b return true; } - struct ExAllControl *eac = (struct ExAllControl *)IDOS->AllocDosObject(DOS_EXALLCONTROL, 0); - if (eac) { - struct ExAllData *data = (struct ExAllData *)IExec->AllocVec(kExAllBufferSize, MEMF_ANY); - if (data) { - BOOL bExMore; - eac->eac_LastKey = 0; - do { - // Examine directory - bExMore = IDOS->ExAll(_pFileLock, data, kExAllBufferSize, ED_TYPE, eac); + APTR context = IDOS->ObtainDirContextTags( EX_FileLockInput, _pFileLock, + EX_DoCurrentDir, TRUE, /* for softlinks */ + EX_DataFields, (EXF_NAME|EXF_LINK|EXF_TYPE), + TAG_END); + if (context) { + struct ExamineData * pExd = NULL; // NB: no need to free value after usage, all is dealt by the DirContext release - LONG error = IDOS->IoErr(); - if (!bExMore && error != ERROR_NO_MORE_ENTRIES) - break; // Abnormal failure - - if (eac->eac_Entries == 0) - continue; // Normal failure, no entries - - struct ExAllData *ead = data; - do { - if ((mode == Common::FSNode::kListAll) || - (EAD_IS_DRAWER(ead) && (mode == Common::FSNode::kListDirectoriesOnly)) || - (EAD_IS_FILE(ead) && (mode == Common::FSNode::kListFilesOnly))) { - Common::String full_path = _sPath; - full_path += (char*)ead->ed_Name; - - BPTR lock = IDOS->Lock((STRPTR)full_path.c_str(), SHARED_LOCK); - if (lock) { - AmigaOSFilesystemNode *entry = new AmigaOSFilesystemNode(lock, (char *)ead->ed_Name); - if (entry) { - //FIXME: since the isValid() function is no longer part of the AbstractFSNode - // specification, the following call had to be changed: - // if (entry->isValid()) - // Please verify that the logic of the code remains coherent. Also, remember - // that the isReadable() and isWritable() methods are available. - if (entry->exists()) - myList.push_back(entry); - else - delete entry; - } - IDOS->UnLock(lock); - } + AmigaOSFilesystemNode *entry ; + while ( (pExd = IDOS->ExamineDir(context)) ) { + if ( (EXD_IS_FILE(pExd) && ( Common::FSNode::kListFilesOnly == mode )) + || (EXD_IS_DIRECTORY(pExd) && ( Common::FSNode::kListDirectoriesOnly == mode )) + || Common::FSNode::kListAll == mode + ) + { + BPTR pLock = IDOS->Lock( pExd->Name, SHARED_LOCK ); + if (pLock) { + entry = new AmigaOSFilesystemNode( pLock, pExd->Name ); + if (entry) { + myList.push_back(entry); } - ead = ead->ed_Next; - } while (ead); - } while (bExMore); - IExec->FreeVec(data); + IDOS->UnLock(pLock); + } + } } - IDOS->FreeDosObject(DOS_EXALLCONTROL, eac); + if (ERROR_NO_MORE_ENTRIES != IDOS->IoErr() ) { + debug(6, "An error occured during ExamineDir"); + ret = false; + } else { + ret = true; + } + + + IDOS->ReleaseDirContext(context); + } else { + debug(6, "Unable to ObtainDirContext"); + ret = false; } LEAVE(); - return true; -} - -int AmigaOSFilesystemNode::getFibProtection() const { - ENTER(); - - int fibProt = -1; - struct FileInfoBlock *fib = (struct FileInfoBlock *)IDOS->AllocDosObject(DOS_FIB, NULL); - if (!fib) { - debug(6, "FileInfoBlock is NULL"); - LEAVE(); - return fibProt; - } - - BPTR pLock = IDOS->Lock((STRPTR)_sPath.c_str(), SHARED_LOCK); - if (pLock) { - if (IDOS->Examine(pLock, fib) != DOSFALSE) { - fibProt = fib->fib_Protection; - } - IDOS->UnLock(pLock); - } - - IDOS->FreeDosObject(DOS_FIB, fib); - LEAVE(); - return fibProt; + return ret; } AbstractFSNode *AmigaOSFilesystemNode::getParent() const { @@ -447,8 +407,7 @@ AbstractFSNode *AmigaOSFilesystemNode::getParent() const { if (parentDir) { node = new AmigaOSFilesystemNode(parentDir); IDOS->UnLock(parentDir); - } - else + } else node = new AmigaOSFilesystemNode(); LEAVE(); @@ -457,38 +416,24 @@ AbstractFSNode *AmigaOSFilesystemNode::getParent() const { } bool AmigaOSFilesystemNode::isReadable() const { - bool readable = false; - int fibProt = getFibProtection(); - - if (fibProt >= 0) { - /* The fib_Protection flag is low-active or inverted, thus the negation. - * - * For more information, consult the compiler/include/dos/dos.h - * file from the AROS source (http://aros.sourceforge.net/). - */ - readable = !(fibProt & FIBF_READ); - } + // Regular RWED protection flags are low-active or inverted, thus the negation. + // moreover pseudo root filesystem (null _pFileLock) is readable whatever the + // protection says + bool readable = !(_nProt & EXDF_READ) || _pFileLock == 0; return readable; } bool AmigaOSFilesystemNode::isWritable() const { - bool writable = false; - int fibProt = getFibProtection(); - - if (fibProt >= 0) { - /* The fib_Protection flag is low-active or inverted, thus the negation. - * - * For more information, consult the compiler/include/dos/dos.h - * file from the AROS source (http://aros.sourceforge.net/). - */ - writable = !(fibProt & FIBF_WRITE); - } + // Regular RWED protection flags are low-active or inverted, thus the negation. + // moreover pseudo root filesystem (null _pFileLock) is never writable whatever + // the protection says (because of the pseudo nature) + bool writable = !(_nProt & EXDF_WRITE) && _pFileLock !=0; return writable; } -AbstractFSList AmigaOSFilesystemNode::listVolumes() const { +AbstractFSList AmigaOSFilesystemNode::listVolumes() const { ENTER(); AbstractFSList myList; @@ -508,13 +453,10 @@ AbstractFSList AmigaOSFilesystemNode::listVolumes() const { if (dosList->dol_Type == DLT_VOLUME && dosList->dol_Name && dosList->dol_Task) { - //const char *volName = (const char *)BADDR(dosList->dol_Name)+1; // Copy name to buffer IDOS->CopyStringBSTRToC(dosList->dol_Name, buffer, MAXPATHLEN); - //const char *devName = (const char *)((struct Task *)dosList->dol_Task->mp_SigTask)->tc_Node.ln_Name; - // Volume name + '\0' char *volName = new char [strlen(buffer) + 1]; @@ -536,15 +478,7 @@ AbstractFSList AmigaOSFilesystemNode::listVolumes() const { AmigaOSFilesystemNode *entry = new AmigaOSFilesystemNode(volumeLock, buffer); if (entry) { - //FIXME: since the isValid() function is no longer part of the AbstractFSNode - // specification, the following call had to be changed: - // if (entry->isValid()) - // Please verify that the logic of the code remains coherent. Also, remember - // that the isReadable() and isWritable() methods are available. - if(entry->exists()) - myList.push_back(entry); - else - delete entry; + myList.push_back(entry); } IDOS->UnLock(volumeLock); diff --git a/backends/fs/psp/psp-fs-factory.cpp b/backends/fs/psp/psp-fs-factory.cpp index fed0b23f9a7..b1a4d58a2bf 100644 --- a/backends/fs/psp/psp-fs-factory.cpp +++ b/backends/fs/psp/psp-fs-factory.cpp @@ -33,7 +33,14 @@ AbstractFSNode *PSPFilesystemFactory::makeRootFileNode() const { } AbstractFSNode *PSPFilesystemFactory::makeCurrentDirectoryFileNode() const { - return new PSPFilesystemNode(); + char buf[MAXPATHLEN]; + char * ret = 0; + + PowerMan.beginCriticalSection(); + ret = getcwd(buf, MAXPATHLEN); + PowerMan.endCriticalSection(); + + return (ret ? new PSPFilesystemNode(buf) : NULL); } AbstractFSNode *PSPFilesystemFactory::makeFileNodePath(const Common::String &path) const { diff --git a/backends/fs/psp/psp-fs.cpp b/backends/fs/psp/psp-fs.cpp index be36753d83d..7e801e635ae 100644 --- a/backends/fs/psp/psp-fs.cpp +++ b/backends/fs/psp/psp-fs.cpp @@ -26,7 +26,7 @@ #include "engines/engine.h" #include "backends/fs/abstract-fs.h" -#include "backends/fs/stdiostream.h" +#include "backends/fs/psp/psp-stream.h" #include #include @@ -35,6 +35,8 @@ #define ROOT_PATH "ms0:/" +#include "backends/platform/psp/trace.h" + /** * Implementation of the ScummVM file system API based on PSPSDK API. * @@ -59,15 +61,15 @@ public: * @param path Common::String with the path the new node should point to. * @param verify true if the isValid and isDirectory flags should be verified during the construction. */ - PSPFilesystemNode(const Common::String &p, bool verify); + PSPFilesystemNode(const Common::String &p, bool verify = true); - virtual bool exists() const { return access(_path.c_str(), F_OK) == 0; } + virtual bool exists() const; virtual Common::String getDisplayName() const { return _displayName; } virtual Common::String getName() const { return _displayName; } virtual Common::String getPath() const { return _path; } virtual bool isDirectory() const { return _isDirectory; } - virtual bool isReadable() const { return access(_path.c_str(), R_OK) == 0; } - virtual bool isWritable() const { return access(_path.c_str(), W_OK) == 0; } + virtual bool isReadable() const; + virtual bool isWritable() const; virtual AbstractFSNode *getChild(const Common::String &n) const; virtual bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const; @@ -94,11 +96,51 @@ PSPFilesystemNode::PSPFilesystemNode(const Common::String &p, bool verify) { if (verify) { struct stat st; + if (PowerMan.beginCriticalSection()==PowerManager::Blocked) + PSPDebugSuspend("Suspended in PSPFilesystemNode::PSPFilesystemNode\n"); _isValid = (0 == stat(_path.c_str(), &st)); + PowerMan.endCriticalSection(); _isDirectory = S_ISDIR(st.st_mode); } } +bool PSPFilesystemNode::exists() const { + int ret = 0; + + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugSuspend("Suspended in PSPFilesystemNode::exists()\n"); // Make sure to block in case of suspend + + ret = access(_path.c_str(), F_OK); + PowerMan.endCriticalSection(); + + return ret == 0; +} + +bool PSPFilesystemNode::isReadable() const { + int ret = 0; + + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugSuspend("Suspended in PSPFilesystemNode::isReadable()\n"); // Make sure to block in case of suspend + + ret = access(_path.c_str(), R_OK); + PowerMan.endCriticalSection(); + + return ret == 0; +} + +bool PSPFilesystemNode::isWritable() const { + int ret = 0; + + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugSuspend("Suspended in PSPFilesystemNode::isWritable()\n"); // Make sure to block in case of suspend + + ret = access(_path.c_str(), W_OK); + PowerMan.endCriticalSection(); + + return ret == 0; +} + + AbstractFSNode *PSPFilesystemNode::getChild(const Common::String &n) const { // FIXME: Pretty lame implementation! We do no error checking to speak // of, do not check if this is a special node, etc. @@ -117,6 +159,11 @@ bool PSPFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool //TODO: honor the hidden flag + bool ret = true; + + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugSuspend("Suspended in PSPFilesystemNode::getChildren\n"); // Make sure to block in case of suspend + int dfd = sceIoDopen(_path.c_str()); if (dfd > 0) { SceIoDirent dir; @@ -149,10 +196,13 @@ bool PSPFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool } sceIoDclose(dfd); - return true; - } else { - return false; + ret = true; + } else { // dfd <= 0 + ret = false; } + + PowerMan.endCriticalSection(); + return ret; } AbstractFSNode *PSPFilesystemNode::getParent() const { @@ -166,11 +216,11 @@ AbstractFSNode *PSPFilesystemNode::getParent() const { } Common::SeekableReadStream *PSPFilesystemNode::createReadStream() { - return StdioStream::makeFromPath(getPath().c_str(), false); + return PSPIoStream::makeFromPath(getPath(), false); } Common::WriteStream *PSPFilesystemNode::createWriteStream() { - return StdioStream::makeFromPath(getPath().c_str(), true); + return PSPIoStream::makeFromPath(getPath(), true); } #endif //#ifdef __PSP__ diff --git a/backends/fs/psp/psp-stream.cpp b/backends/fs/psp/psp-stream.cpp new file mode 100644 index 00000000000..2f4a370694e --- /dev/null +++ b/backends/fs/psp/psp-stream.cpp @@ -0,0 +1,303 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * 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$ + * + */ +#ifdef __PSP__ + +#include +#include + +#include "backends/platform/psp/trace.h" +#include "backends/platform/psp/powerman.h" +#include "backends/fs/psp/psp-stream.h" + +#include + +PSPIoStream::PSPIoStream(const Common::String &path, bool writeMode) +: StdioStream((void *)1), _path(path), _writeMode(writeMode) { + + assert(!path.empty()); + + _handle = (void *)0; // Need to do this since base class asserts not 0. + _ferror = false; + _feof = false; + _pos = 0; + +#ifdef __PSP_DEBUG_SUSPEND__ + _errorSuspend = 0; + _errorSource = 0; + _errorPos = 0; + _errorHandle = 0; + _suspendCount = 0; +#endif +} + +PSPIoStream::~PSPIoStream() { + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugSuspend("Suspended in PSPIoStream::~PSPIoStream()\n"); + + PowerMan.unregisterSuspend(this); // Unregister with powermanager to be suspended + // Must do this before fclose() or resume() will reopen. + + fclose((FILE *)_handle); // We don't need a critical section(?). Worst case, the handle gets closed on its own + + PowerMan.endCriticalSection(); +} + +// Function to open the file pointed to by the path. +// +// +void *PSPIoStream::open() { + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) { + // No need to open. Just return the _handle resume() already opened. + PSPDebugSuspend("Suspended in PSPIoStream::open\n"); + } + + _handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb"); // open + + PowerMan.registerSuspend(this); // Register with the powermanager to be suspended + + PowerMan.endCriticalSection(); + + return _handle; +} + +bool PSPIoStream::err() const { + if (_ferror) + PSPDebugSuspend("In PSPIoStream::err - mem_ferror=%d, source=%d, suspend error=%d, pos=%d, _errorPos=%d, _errorHandle=%p, suspendCount=%d _handle\n", + _ferror, _errorSource, _errorSuspend, _pos, _errorPos, _errorHandle, _suspendCount); + + return _ferror; +} + +void PSPIoStream::clearErr() { + _ferror = false; // Remove regular error bit + +} + +bool PSPIoStream::eos() const { + return _feof; +} + +int32 PSPIoStream::pos() const { + return _pos; +} + + +int32 PSPIoStream::size() const { + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugSuspend("Suspended in PSPIoStream::size()\n"); + + fseek((FILE *)_handle, 0, SEEK_END); + int32 length = ftell((FILE *)_handle); + fseek((FILE *)_handle, _pos, SEEK_SET); + + if (_pos < 0 || length < 0) { // Check for errors + PSPDebugSuspend("In PSPIoStream::size(). encountered an error!\n"); + _ferror = true; + length = -1; // If our oldPos is bad, we want length to be bad too to signal + clearerr((FILE *)_handle); + +#ifdef __PSP_DEBUG_SUSPEND__ + _errorSource = 2; +#endif + } + + PowerMan.endCriticalSection(); + + return length; +} + +bool PSPIoStream::seek(int32 offs, int whence) { + // Check if we can access the file + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugSuspend("Suspended in PSPIoStream::seek()\n"); + + int ret = fseek((FILE *)_handle, offs, whence); + + if (ret != 0) { + _ferror = true; + PSPDebugSuspend("In PSPIoStream::seek(). encountered an error!\n"); + clearerr((FILE *)_handle); + _feof = feof((FILE *)_handle); + +#ifdef __PSP_DEBUG_SUSPEND__ + _errorSource = 3; +#endif + } + else { // everything ok + _feof = false; // Reset eof flag since we know it was ok + } + + _pos = ftell((FILE *)_handle); // update pos + + PowerMan.endCriticalSection(); + + return ret == 0; +} + +uint32 PSPIoStream::read(void *ptr, uint32 len) { + // Check if we can access the file + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugSuspend("Suspended in PSPIoStream::read()\n"); + + size_t ret = fread((byte *)ptr, 1, len, (FILE *)_handle); + + _pos += ret; // Update pos + + if (ret != len) { // Check for eof + _feof = feof((FILE *)_handle); + if (!_feof) { // It wasn't an eof. Must be an error + _ferror = true; + clearerr((FILE *)_handle); + _pos = ftell((FILE *)_handle); // Update our position + PSPDebugSuspend("In PSPIoStream::read(). encountered an error!\n"); + +#ifdef __PSP_DEBUG_SUSPEND__ + _errorSource = 4; +#endif + } + } + + PowerMan.endCriticalSection(); + + return ret; +} + +uint32 PSPIoStream::write(const void *ptr, uint32 len) { + // Check if we can access the file + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugSuspend("Suspended in PSPIoStream::read()\n"); + + size_t ret = fwrite(ptr, 1, len, (FILE *)_handle); + + _pos += ret; + + if (ret != len) { // Set error + _ferror = true; + clearerr((FILE *)_handle); + _pos = ftell((FILE *)_handle); // Update pos + PSPDebugTrace("In PSPIoStream::write(). encountered an error!\n"); + +#ifdef __PSP_DEBUG_SUSPEND__ + _errorSource = 5; +#endif + } + + PowerMan.endCriticalSection(); + + return ret; +} + +bool PSPIoStream::flush() { + // Enter critical section + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugSuspend("Suspended in PSPIoStream::read()\n"); + + int ret = fflush((FILE *)_handle); + + if (ret != 0) { + _ferror = true; + clearerr((FILE *)_handle); + PSPDebugSuspend("In PSPIoStream::flush(). encountered an error!\n"); + +#ifdef __PSP_DEBUG_SUSPEND__ + _errorSource = 6; +#endif + } + + PowerMan.endCriticalSection(); + + return ret == 0; +} + +// For the PSP, since we're building in suspend support, we moved opening +// the actual file to an open function since we need an actual PSPIoStream object to suspend. +// +PSPIoStream *PSPIoStream::makeFromPath(const Common::String &path, bool writeMode) { + PSPIoStream *stream = new PSPIoStream(path, writeMode); + + if (stream->open() > 0) { + return stream; + } else { + delete stream; + return 0; + } +} + +/* + * Function to suspend the IO stream (called by PowerManager) + */ +int PSPIoStream::suspend() { +#ifdef __PSP_DEBUG_SUSPEND__ + _suspendCount++; + + if (_handle > 0 && _pos < 0) { + _errorSuspend = SuspendError; + _errorPos = _pos; + _errorHandle = _handle; + } +#endif /* __PSP_DEBUG_SUSPEND__ */ + + if (_handle > 0) { + fclose((FILE *)_handle); // close our file descriptor + _handle = (void *)0xFFFFFFFF; // Set handle to non-null invalid value so makeFromPath doesn't return error + } + + return 0; +} + +/* + * Function to resume the IO stream (called by Power Manager) + */ +int PSPIoStream::resume() { + int ret = 0; +#ifdef __PSP_DEBUG_SUSPEND__ + _suspendCount--; +#endif + + // We reopen our file descriptor + _handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb"); + if (_handle <= 0) { + PSPDebugSuspend("PSPIoStream::resume(): Couldn't reopen file %s\n", _path.c_str()); + } + + // Resume our previous position + if (_handle > 0 && _pos > 0) { + ret = fseek((FILE *)_handle, _pos, SEEK_SET); + +#ifdef __PSP_DEBUG_SUSPEND__ + if (ret != 0) { // Check for problem + _errorSuspend = ResumeError; + _errorPos = _pos; + _errorHandle = _handle; + } +#endif + + } + + return ret; +} + + +#endif /* __PSP__ */ diff --git a/backends/fs/psp/psp-stream.h b/backends/fs/psp/psp-stream.h new file mode 100644 index 00000000000..2a72224930a --- /dev/null +++ b/backends/fs/psp/psp-stream.h @@ -0,0 +1,86 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * 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 PSPSTREAM_H_ +#define PSPSTREAM_H_ + +#include "backends/fs/stdiostream.h" +#include "backends/platform/psp/powerman.h" +#include "common/list.h" + +/* + * Class to handle special suspend/resume needs of PSP IO Streams + */ +class PSPIoStream : public StdioStream, public Suspendable { +protected: + Common::String _path; /* Need to maintain for reopening after suspend */ + bool _writeMode; /* "" */ + int _pos; /* "" */ + mutable int _ferror; /* Save file ferror */ + mutable bool _feof; /* and eof */ + + enum { + SuspendError = 2, + ResumeError = 3 + }; + + int _errorSuspend; + mutable int _errorSource; + +#ifdef __PSP_DEBUG_SUSPEND__ + int _errorPos; + void * _errorHandle; + int _suspendCount; +#endif /* __PSP_DEBUG_SUSPEND__ */ + +public: + /** + * Given a path, invoke fopen on that path and wrap the result in a + * PSPIoStream instance. + */ + static PSPIoStream *makeFromPath(const Common::String &path, bool writeMode); + + PSPIoStream(const Common::String &path, bool writeMode); + virtual ~PSPIoStream(); + + void * open(); // open the file pointed to by the file path + + bool err() const; + void clearErr(); + bool eos() const; + + virtual uint32 write(const void *dataPtr, uint32 dataSize); + virtual bool flush(); + + virtual int32 pos() const; + virtual int32 size() const; + virtual bool seek(int32 offs, int whence = SEEK_SET); + virtual uint32 read(void *dataPtr, uint32 dataSize); + + int suspend(); /* Suspendable interface (power manager) */ + int resume(); /* " " */ +}; + +#endif /* PSPSTREAM_H_ */ diff --git a/backends/fs/stdiostream.h b/backends/fs/stdiostream.h index 9e6d064e7ac..e17ca73ff1b 100644 --- a/backends/fs/stdiostream.h +++ b/backends/fs/stdiostream.h @@ -46,17 +46,17 @@ public: StdioStream(void *handle); virtual ~StdioStream(); - bool err() const; - void clearErr(); - bool eos() const; + virtual bool err() const; + virtual void clearErr(); + virtual bool eos() const; virtual uint32 write(const void *dataPtr, uint32 dataSize); virtual bool flush(); virtual int32 pos() const; virtual int32 size() const; - bool seek(int32 offs, int whence = SEEK_SET); - uint32 read(void *dataPtr, uint32 dataSize); + virtual bool seek(int32 offs, int whence = SEEK_SET); + virtual uint32 read(void *dataPtr, uint32 dataSize); }; #endif diff --git a/backends/keymapper/keymapper.cpp b/backends/keymapper/keymapper.cpp index a0313219d1f..240749a3797 100644 --- a/backends/keymapper/keymapper.cpp +++ b/backends/keymapper/keymapper.cpp @@ -168,6 +168,15 @@ void Keymapper::popKeymap() { _activeMaps.pop(); } +bool Keymapper::notifyEvent(const Common::Event &ev) { + if (ev.type == Common::EVENT_KEYDOWN) + return mapKeyDown(ev.kbd); + else if (ev.type == Common::EVENT_KEYUP) + return mapKeyUp(ev.kbd); + else + return false; +} + bool Keymapper::mapKeyDown(const KeyState& key) { return mapKey(key, true); } @@ -255,7 +264,7 @@ void Keymapper::executeAction(const Action *action, bool keyDown) { } evt.mouse = _eventMan->getMousePos(); - _eventMan->pushEvent(evt); + addEvent(evt); } } diff --git a/backends/keymapper/keymapper.h b/backends/keymapper/keymapper.h index faacc161d68..3f23c8d7ba0 100644 --- a/backends/keymapper/keymapper.h +++ b/backends/keymapper/keymapper.h @@ -39,7 +39,7 @@ namespace Common { -class Keymapper { +class Keymapper : public Common::EventMapper, private Common::ArtificialEventSource { public: struct MapRecord { @@ -134,6 +134,10 @@ public: */ void popKeymap(); + // Implementation of the EventMapper interface + bool notifyEvent(const Common::Event &ev); + bool pollEvent(Common::Event &ev) { return Common::ArtificialEventSource::pollEvent(ev); } + /** * @brief Map a key press event. * If the active keymap contains a Action mapped to the given key, then diff --git a/backends/platform/sdl/events.cpp b/backends/platform/sdl/events.cpp index c95090e55e3..9c5fa740e12 100644 --- a/backends/platform/sdl/events.cpp +++ b/backends/platform/sdl/events.cpp @@ -187,6 +187,8 @@ bool OSystem_SDL::pollEvent(Common::Event &event) { } */ while (SDL_PollEvent(&ev)) { + preprocessEvents(&ev); + switch (ev.type) { case SDL_KEYDOWN:{ b = event.kbd.flags = SDLModToOSystemKeyFlags(SDL_GetModState()); diff --git a/backends/platform/sdl/graphics.cpp b/backends/platform/sdl/graphics.cpp index e573dac5cf3..2d9a145d555 100644 --- a/backends/platform/sdl/graphics.cpp +++ b/backends/platform/sdl/graphics.cpp @@ -26,6 +26,13 @@ #include "backends/platform/sdl/sdl.h" #include "common/mutex.h" #include "common/util.h" +#ifdef USE_RGB_COLOR +#include "common/list.h" +#endif +#include "graphics/font.h" +#include "graphics/fontman.h" +#include "graphics/scaler.h" +#include "graphics/surface.h" @@ -354,6 +361,11 @@ void OSystem_SDL::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, i SDL_UnlockSurface(_overlayscreen); } + +#pragma mark - +#pragma mark --- Mouse --- +#pragma mark - + bool OSystem_SDL::showMouse(bool visible) { return false; } @@ -396,7 +408,17 @@ void OSystem_SDL::warpMouse(int x, int y) { }*/ } -void OSystem_SDL::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor, int cursorTargetScale) { +void OSystem_SDL::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format) { +#ifdef USE_RGB_COLOR + if (!format) + _cursorFormat = Graphics::PixelFormat::createFormatCLUT8(); + else if (format->bytesPerPixel <= _screenFormat.bytesPerPixel) + _cursorFormat = *format; + keycolor &= (1 << (_cursorFormat.bytesPerPixel << 3)) - 1; +#else + keycolor &= 0xFF; +#endif + if (w == 0 || h == 0) return; /* Residual doesn't support this @@ -430,9 +452,13 @@ void OSystem_SDL::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, } free(_mouseData); - +#ifdef USE_RGB_COLOR + _mouseData = (byte *)malloc(w * h * _cursorFormat.bytesPerPixel); + memcpy(_mouseData, buf, w * h * _cursorFormat.bytesPerPixel); +#else _mouseData = (byte *)malloc(w * h); memcpy(_mouseData, buf, w * h); +#endif blitCursor();*/ } diff --git a/backends/platform/sdl/main.cpp b/backends/platform/sdl/main.cpp index 82d7c706c60..d81f813964f 100644 --- a/backends/platform/sdl/main.cpp +++ b/backends/platform/sdl/main.cpp @@ -37,7 +37,7 @@ #include "SymbianOs.h" #endif -#if !defined(__MAEMO__) && !defined(_WIN32_WCE) && !defined(GP2XWIZ) +#if !defined(__MAEMO__) && !defined(_WIN32_WCE) && !defined(GP2XWIZ)&& !defined(LINUXMOTO) #if defined (WIN32) int __stdcall WinMain(HINSTANCE /*hInst*/, HINSTANCE /*hPrevInst*/, LPSTR /*lpCmdLine*/, int /*iShowCmd*/) { diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index 15f621402d2..575c5cb5e69 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -33,7 +33,7 @@ #include "common/archive.h" #include "common/config-manager.h" #include "common/debug.h" -#include "common/events.h" +#include "common/EventRecorder.h" #include "common/util.h" #ifdef UNIX @@ -215,7 +215,7 @@ OSystem_SDL::~OSystem_SDL() { uint32 OSystem_SDL::getMillis() { uint32 millis = SDL_GetTicks(); - getEventManager()->processMillis(millis); + g_eventRec.processMillis(millis); return millis; } @@ -349,13 +349,20 @@ Common::WriteStream *OSystem_SDL::createConfigWriteStream() { } void OSystem_SDL::setWindowCaption(const char *caption) { - Common::String cap(caption); + Common::String cap; + byte c; + + // The string caption is supposed to be in LATIN-1 encoding. + // SDL expects UTF-8. So we perform the conversion here. + while ((c = *(const byte *)caption++)) { + if (c < 0x80) + cap += c; + else { + cap += 0xC0 | (c >> 6); + cap += 0x80 | (c & 0x3F); + } + } - // Filter out any non-ASCII characters, replacing them by question marks. - // At some point, we may wish to allow LATIN 1 or UTF-8. - for (uint i = 0; i < cap.size(); ++i) - if ((byte)cap[i] > 0x7F) - cap.setChar('?', i); SDL_WM_SetCaption(cap.c_str(), cap.c_str()); } @@ -410,7 +417,7 @@ void OSystem_SDL::quit() { void OSystem_SDL::setupIcon() { int x, y, w, h, ncols, nbytes, i; unsigned int rgba[256]; - unsigned int *icon; + unsigned int *icon; sscanf(residual_icon[0], "%d %d %d %d", &w, &h, &ncols, &nbytes); if ((w > 512) || (h > 512) || (ncols > 255) || (nbytes > 1)) { @@ -585,7 +592,6 @@ void OSystem_SDL::mixCallback(void *sys, byte *samples, int len) { void OSystem_SDL::setupMixer() { SDL_AudioSpec desired; - SDL_AudioSpec obtained; // Determine the desired output sampling frequency. _samplesPerSec = 0; @@ -615,7 +621,7 @@ void OSystem_SDL::setupMixer() { _mixer = new Audio::MixerImpl(this); assert(_mixer); - if (SDL_OpenAudio(&desired, &obtained) != 0) { + if (SDL_OpenAudio(&desired, &_obtainedRate) != 0) { warning("Could not open audio device: %s", SDL_GetError()); _samplesPerSec = 0; _mixer->setReady(false); @@ -623,7 +629,7 @@ void OSystem_SDL::setupMixer() { // Note: This should be the obtained output rate, but it seems that at // least on some platforms SDL will lie and claim it did get the rate // even if it didn't. Probably only happens for "weird" rates, though. - _samplesPerSec = obtained.freq; + _samplesPerSec = _obtainedRate.freq; debug(1, "Output sample rate: %d Hz", _samplesPerSec); // Tell the mixer that we are ready and start the sound processing @@ -631,7 +637,7 @@ void OSystem_SDL::setupMixer() { _mixer->setReady(true); #ifdef MIXER_DOUBLE_BUFFERING - initThreadedMixer(_mixer, obtained.samples * 4); + initThreadedMixer(_mixer, _obtainedRate.samples * 4); #endif // start the sound system diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h index 8ddb55eaae0..db5d27f1e23 100644 --- a/backends/platform/sdl/sdl.h +++ b/backends/platform/sdl/sdl.h @@ -77,7 +77,7 @@ public: virtual void warpMouse(int x, int y); // overloaded by CE backend (FIXME) // Set the bitmap that's used when drawing the cursor. - virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor, int cursorTargetScale); // overloaded by CE backend (FIXME) + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format); // overloaded by CE backend (FIXME) // Get the number of milliseconds since the program was started. uint32 getMillis(); @@ -127,6 +127,7 @@ public: // Overlay virtual Graphics::PixelFormat getOverlayFormat() const { return _overlayFormat; } + virtual void showOverlay(); virtual void hideOverlay(); virtual void clearOverlay(); @@ -143,6 +144,7 @@ public: virtual bool hasFeature(Feature f); virtual void setFeatureState(Feature f, bool enable); virtual bool getFeatureState(Feature f); + virtual void preprocessEvents(SDL_Event *event) {}; virtual Common::SaveFileManager *getSavefileManager(); virtual FilesystemFactory *getFilesystemFactory(); @@ -153,6 +155,7 @@ public: protected: bool _inited; + SDL_AudioSpec _obtainedRate; #ifdef USE_OPENGL diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp index c92db46dcce..4644721a352 100644 --- a/backends/saves/default/default-saves.cpp +++ b/backends/saves/default/default-saves.cpp @@ -55,11 +55,14 @@ void DefaultSaveFileManager::checkPath(const Common::FSNode &dir) { } Common::StringList DefaultSaveFileManager::listSavefiles(const Common::String &pattern) { - Common::FSNode savePath(getSavePath()); - checkPath(savePath); + Common::String savePathName = getSavePath(); + checkPath(Common::FSNode(savePathName)); if (getError() != Common::kNoError) return Common::StringList(); + // recreate FSNode since checkPath may have changed/created the directory + Common::FSNode savePath(savePathName); + Common::FSDirectory dir(savePath); Common::ArchiveMemberList savefiles; Common::StringList results; @@ -76,11 +79,14 @@ Common::StringList DefaultSaveFileManager::listSavefiles(const Common::String &p Common::InSaveFile *DefaultSaveFileManager::openForLoading(const Common::String &filename) { // Ensure that the savepath is valid. If not, generate an appropriate error. - Common::FSNode savePath(getSavePath()); - checkPath(savePath); + Common::String savePathName = getSavePath(); + checkPath(Common::FSNode(savePathName)); if (getError() != Common::kNoError) return 0; + // recreate FSNode since checkPath may have changed/created the directory + Common::FSNode savePath(savePathName); + Common::FSNode file = savePath.getChild(filename); if (!file.exists()) return 0; @@ -93,11 +99,14 @@ Common::InSaveFile *DefaultSaveFileManager::openForLoading(const Common::String Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String &filename) { // Ensure that the savepath is valid. If not, generate an appropriate error. - Common::FSNode savePath(getSavePath()); - checkPath(savePath); + Common::String savePathName = getSavePath(); + checkPath(Common::FSNode(savePathName)); if (getError() != Common::kNoError) return 0; + // recreate FSNode since checkPath may have changed/created the directory + Common::FSNode savePath(savePathName); + Common::FSNode file = savePath.getChild(filename); // Open the file for saving @@ -107,13 +116,14 @@ Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String } bool DefaultSaveFileManager::removeSavefile(const Common::String &filename) { - clearError(); - - Common::FSNode savePath(getSavePath()); - checkPath(savePath); + Common::String savePathName = getSavePath(); + checkPath(Common::FSNode(savePathName)); if (getError() != Common::kNoError) return false; + // recreate FSNode since checkPath may have changed/created the directory + Common::FSNode savePath(savePathName); + Common::FSNode file = savePath.getChild(filename); // FIXME: remove does not exist on all systems. If your port fails to diff --git a/backends/saves/psp/psp-saves.cpp b/backends/saves/psp/psp-saves.cpp index dc45881c915..83baf035105 100644 --- a/backends/saves/psp/psp-saves.cpp +++ b/backends/saves/psp/psp-saves.cpp @@ -26,6 +26,7 @@ #ifdef __PSP__ #include "backends/saves/psp/psp-saves.h" +#include "backends/platform/psp/powerman.h" #include "common/config-manager.h" #include "common/savefile.h" @@ -49,6 +50,8 @@ void PSPSaveFileManager::checkPath(const Common::FSNode &dir) { const char *savePath = dir.getPath().c_str(); clearError(); + PowerMan.beginCriticalSection(); + //check if the save directory exists SceUID fd = sceIoDopen(savePath); if (fd < 0) { @@ -58,6 +61,8 @@ void PSPSaveFileManager::checkPath(const Common::FSNode &dir) { //it exists, so close it again. sceIoDclose(fd); } + + PowerMan.endCriticalSection(); } #endif diff --git a/backends/timer/default/default-timer.cpp b/backends/timer/default/default-timer.cpp index 8860e575ccc..220f6c68923 100644 --- a/backends/timer/default/default-timer.cpp +++ b/backends/timer/default/default-timer.cpp @@ -124,6 +124,12 @@ bool DefaultTimerManager::installTimerProc(TimerProc callback, int32 interval, v slot->nextFireTimeMicro = interval % 1000; slot->next = 0; + // FIXME: It seems we do allow the client to add one callback multiple times over here, + // but "removeTimerProc" will remove *all* added instances. We should either prevent + // multiple additions of a timer proc OR we should change removeTimerProc to only remove + // a specific timer proc entry. + // Probably we can safely just allow a single addition of a specific function once + // and just update our Timer documentation accordingly. insertPrioQueue(_head, slot); return true; diff --git a/backends/vkeybd/image-map.cpp b/backends/vkeybd/image-map.cpp index b0aa42ba22a..75f467d97ed 100644 --- a/backends/vkeybd/image-map.cpp +++ b/backends/vkeybd/image-map.cpp @@ -1,7 +1,7 @@ -/* ScummVM - Graphic Adventure Engine +/* Residual - A 3D game interpreter * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or diff --git a/backends/vkeybd/image-map.h b/backends/vkeybd/image-map.h index 269ad265cd9..3f33e4b1977 100644 --- a/backends/vkeybd/image-map.h +++ b/backends/vkeybd/image-map.h @@ -1,7 +1,7 @@ -/* ScummVM - Graphic Adventure Engine +/* Residual - A 3D game interpreter * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or diff --git a/backends/vkeybd/polygon.cpp b/backends/vkeybd/polygon.cpp index d12ee7f8750..7afe91cd4c4 100644 --- a/backends/vkeybd/polygon.cpp +++ b/backends/vkeybd/polygon.cpp @@ -1,7 +1,7 @@ -/* ScummVM - Graphic Adventure Engine +/* Residual - A 3D game interpreter * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or diff --git a/backends/vkeybd/polygon.h b/backends/vkeybd/polygon.h index a93e1ff8acd..da122d9c823 100644 --- a/backends/vkeybd/polygon.h +++ b/backends/vkeybd/polygon.h @@ -1,7 +1,7 @@ -/* ScummVM - Graphic Adventure Engine +/* Residual - A 3D game interpreter * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or diff --git a/backends/vkeybd/virtual-keyboard-gui.cpp b/backends/vkeybd/virtual-keyboard-gui.cpp index e1c076ad240..8d378374b3e 100644 --- a/backends/vkeybd/virtual-keyboard-gui.cpp +++ b/backends/vkeybd/virtual-keyboard-gui.cpp @@ -1,7 +1,7 @@ -/* ScummVM - Graphic Adventure Engine +/* Residual - A 3D game interpreter * -* ScummVM is the legal property of its developers, whose names -* are too numerous to list here. Please refer to the COPYRIGHT +* Residual is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or diff --git a/backends/vkeybd/virtual-keyboard-gui.h b/backends/vkeybd/virtual-keyboard-gui.h index bd6f72d64aa..46574be759a 100644 --- a/backends/vkeybd/virtual-keyboard-gui.h +++ b/backends/vkeybd/virtual-keyboard-gui.h @@ -1,7 +1,7 @@ -/* ScummVM - Graphic Adventure Engine +/* Residual - A 3D game interpreter * -* ScummVM is the legal property of its developers, whose names -* are too numerous to list here. Please refer to the COPYRIGHT +* Residual is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or diff --git a/backends/vkeybd/virtual-keyboard-parser.cpp b/backends/vkeybd/virtual-keyboard-parser.cpp index 5ae53f08ec8..cabf98818cb 100644 --- a/backends/vkeybd/virtual-keyboard-parser.cpp +++ b/backends/vkeybd/virtual-keyboard-parser.cpp @@ -1,7 +1,7 @@ -/* ScummVM - Graphic Adventure Engine +/* Residual - A 3D game interpreter * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or diff --git a/backends/vkeybd/virtual-keyboard-parser.h b/backends/vkeybd/virtual-keyboard-parser.h index eeea1995c70..acd2a01f4b8 100644 --- a/backends/vkeybd/virtual-keyboard-parser.h +++ b/backends/vkeybd/virtual-keyboard-parser.h @@ -1,7 +1,7 @@ -/* ScummVM - Graphic Adventure Engine +/* Residual - A 3D game interpreter * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or diff --git a/backends/vkeybd/virtual-keyboard.cpp b/backends/vkeybd/virtual-keyboard.cpp index a95b79d213a..095e8d650f6 100644 --- a/backends/vkeybd/virtual-keyboard.cpp +++ b/backends/vkeybd/virtual-keyboard.cpp @@ -1,7 +1,7 @@ -/* ScummVM - Graphic Adventure Engine +/* Residual - A 3D game interpreter * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or @@ -31,7 +31,6 @@ #include "backends/vkeybd/virtual-keyboard-parser.h" #include "backends/vkeybd/keycode-descriptions.h" #include "common/config-manager.h" -#include "common/fs.h" #include "common/unzip.h" #define KEY_START_CHAR ('[') @@ -77,51 +76,77 @@ void VirtualKeyboard::reset() { _kbdGUI->reset(); } -bool VirtualKeyboard::loadKeyboardPack(String packName) { +bool VirtualKeyboard::openPack(const String &packName, const FSNode &node) { + if (node.getChild(packName + ".xml").exists()) { + _fileArchive = new FSDirectory(node, 1); + + // uncompressed keyboard pack + if (!_parser->loadFile(node.getChild(packName + ".xml"))) { + delete _fileArchive; + _fileArchive = 0; + return false; + } + + return true; + } + +#ifdef USE_ZLIB + if (node.getChild(packName + ".zip").exists()) { + // compressed keyboard pack + _fileArchive = new ZipArchive(node.getChild(packName + ".zip")); + if (_fileArchive->hasFile(packName + ".xml")) { + if (!_parser->loadStream(_fileArchive->createReadStreamForMember(packName + ".xml"))) { + delete _fileArchive; + _fileArchive = 0; + return false; + } + } else { + warning("Could not find %s.xml file in %s.zip keyboard pack", packName.c_str(), packName.c_str()); + delete _fileArchive; + _fileArchive = 0; + return false; + } + + return true; + } +#endif + + return false; +} + +bool VirtualKeyboard::loadKeyboardPack(const String &packName) { _kbdGUI->initSize(_system->getOverlayWidth(), _system->getOverlayHeight()); delete _fileArchive; _fileArchive = 0; + _loaded = false; - FSNode vkDir; + bool opened = false; if (ConfMan.hasKey("vkeybdpath")) - vkDir = FSNode(ConfMan.get("vkeybdpath")); + opened = openPack(packName, FSNode(ConfMan.get("vkeybdpath"))); else if (ConfMan.hasKey("extrapath")) - vkDir = FSNode(ConfMan.get("extrapath")); - else // use current directory - vkDir = FSNode("."); + opened = openPack(packName, FSNode(ConfMan.get("extrapath"))); - if (vkDir.getChild(packName + ".xml").exists()) { - _fileArchive = new FSDirectory(vkDir, 1); + // fallback to the current dir + if (!opened) + opened = openPack(packName, FSNode(".")); - // uncompressed keyboard pack - if (!_parser->loadFile(vkDir.getChild(packName + ".xml"))) - return false; + if (opened) { + _parser->setParseMode(VirtualKeyboardParser::kParseFull); + _loaded = _parser->parse(); - } else if (vkDir.getChild(packName + ".zip").exists()) { - // compressed keyboard pack -#ifdef USE_ZLIB - _fileArchive = new ZipArchive(vkDir.getChild(packName + ".zip")); - if (_fileArchive->hasFile(packName + ".xml")) { - if (!_parser->loadStream(_fileArchive->createReadStreamForMember(packName + ".xml"))) - return false; + if (_loaded) { + printf("Keyboard pack '%s' loaded successfully!\n", packName.c_str()); } else { - warning("Could not find %s.xml file in %s.zip keyboard pack", packName.c_str(), packName.c_str()); - return false; + warning("Error parsing the keyboard pack '%s'", packName.c_str()); + + delete _fileArchive; + _fileArchive = 0; } -#else - return false; -#endif } else { warning("Keyboard pack not found"); - return false; } - _parser->setParseMode(VirtualKeyboardParser::kParseFull); - _loaded = _parser->parse(); - if (_loaded) - printf("Keyboard pack '%s' loaded successfully!\n", packName.c_str()); - return _loaded; } @@ -312,7 +337,7 @@ void VirtualKeyboard::KeyPressQueue::deleteKey() { List::iterator it = _keyPos; it--; _strPos -= it->strLen; - while((it->strLen)-- > 0) + while ((it->strLen)-- > 0) _keysStr.deleteChar(_strPos); _keys.erase(it); _strChanged = true; diff --git a/backends/vkeybd/virtual-keyboard.h b/backends/vkeybd/virtual-keyboard.h index e3a9cd36001..fea6f76cf40 100644 --- a/backends/vkeybd/virtual-keyboard.h +++ b/backends/vkeybd/virtual-keyboard.h @@ -1,7 +1,7 @@ -/* ScummVM - Graphic Adventure Engine +/* Residual - A 3D game interpreter * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or @@ -37,6 +37,7 @@ class OSystem; #include "common/keyboard.h" #include "common/list.h" #include "common/str.h" +#include "common/fs.h" #include "backends/vkeybd/image-map.h" #include "graphics/surface.h" @@ -190,7 +191,7 @@ public: * searches for a compressed keyboard pack by looking for packName.zip. * @param packName name of the keyboard pack */ - bool loadKeyboardPack(String packName); + bool loadKeyboardPack(const String &packName); /** * Shows the keyboard, starting an event loop that will intercept all @@ -232,6 +233,7 @@ protected: VirtualKeyboardParser *_parser; void reset(); + bool openPack(const String &packName, const FSNode &node); void deleteEvents(); bool checkModeResolutions(); void switchMode(Mode *newMode); diff --git a/base/commandLine.cpp b/base/commandLine.cpp index e993028cb09..3a4444b8157 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -34,6 +34,9 @@ #include "gui/ThemeEngine.h" +#define DETECTOR_TESTING_HACK +#define UPGRADE_ALL_TARGETS_HACK + namespace Base { #ifndef DISABLE_COMMAND_LINE @@ -235,6 +238,18 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha DO_COMMAND('z', "list-games") END_OPTION +#ifdef DETECTOR_TESTING_HACK + // HACK FIXME TODO: This command is intentionally *not* documented! + DO_LONG_COMMAND("test-detector") + END_OPTION +#endif + +#ifdef UPGRADE_ALL_TARGETS_HACK + // HACK FIXME TODO: This command is intentionally *not* documented! + DO_LONG_COMMAND("upgrade-targets") + END_OPTION +#endif + DO_OPTION('c', "config") END_OPTION @@ -253,10 +268,13 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha DO_OPTION_INT('m', "music-volume") END_OPTION - DO_OPTION('s', "sfx-volume") - END_OPTION - - DO_OPTION('r', "speech-volume") + DO_OPTION('p', "path") + Common::FSNode path(option); + if (!path.exists()) { + usage("Non-existent game path '%s'", option); + } else if (!path.isReadable()) { + usage("Non-readable game path '%s'", option); + } END_OPTION DO_OPTION('q', "language") @@ -264,6 +282,15 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha usage("Unrecognized language '%s'", option); END_OPTION + DO_OPTION_INT('s', "sfx-volume") + END_OPTION + + DO_OPTION_INT('r', "speech-volume") + END_OPTION + + DO_LONG_OPTION_INT("cdrom") + END_OPTION + DO_LONG_OPTION_OPT("joystick", "0") settings["joystick_num"] = option; settings.erase("joystick"); @@ -275,24 +302,15 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha DO_LONG_OPTION("soft-renderer") END_OPTION + DO_LONG_OPTION_BOOL("disable-sdl-parachute") + END_OPTION + DO_LONG_OPTION("engine-speed") END_OPTION DO_LONG_OPTION("gamma") END_OPTION - DO_OPTION('p', "path") - Common::FSNode path(option); - if (!path.exists()) { - usage("Non-existent game path '%s'", option); - } else if (!path.isReadable()) { - usage("Non-readable game path '%s'", option); - } - END_OPTION - - DO_LONG_OPTION_BOOL("disable-sdl-parachute") - END_OPTION - DO_LONG_OPTION("savepath") Common::FSNode path(option); if (!path.exists()) { @@ -326,8 +344,15 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha DO_LONG_COMMAND("list-themes") END_OPTION + DO_LONG_OPTION("target-md5") + END_OPTION + DO_LONG_OPTION("text-speed") END_OPTION +#ifdef ENABLE_SCUMM + DO_LONG_OPTION_INT("dimuse-tempo") + END_OPTION +#endif DO_LONG_OPTION("speech-mode") END_OPTION @@ -414,6 +439,192 @@ static void listThemes() { printf("%-14s %s\n", i->id.c_str(), i->name.c_str()); } + +#ifdef DETECTOR_TESTING_HACK +static void runDetectorTest() { + // HACK: The following code can be used to test the detection code of our + // engines. Basically, it loops over all targets, and calls the detector + // for the given path. It then prints out the result and also checks + // whether the result agrees with the settings of the target. + + const Common::ConfigManager::DomainMap &domains = ConfMan.getGameDomains(); + Common::ConfigManager::DomainMap::const_iterator iter = domains.begin(); + int success = 0, failure = 0; + for (iter = domains.begin(); iter != domains.end(); ++iter) { + Common::String name(iter->_key); + Common::String gameid(iter->_value.get("gameid")); + Common::String path(iter->_value.get("path")); + printf("Looking at target '%s', gameid '%s', path '%s' ...\n", + name.c_str(), gameid.c_str(), path.c_str()); + if (path.empty()) { + printf(" ... no path specified, skipping\n"); + continue; + } + if (gameid.empty()) { + gameid = name; + } + + Common::FSNode dir(path); + Common::FSList files; + if (!dir.getChildren(files, Common::FSNode::kListAll)) { + printf(" ... invalid path, skipping\n"); + continue; + } + + GameList candidates(EngineMan.detectGames(files)); + bool gameidDiffers = false; + GameList::iterator x; + for (x = candidates.begin(); x != candidates.end(); ++x) { + gameidDiffers |= (strcasecmp(gameid.c_str(), x->gameid().c_str()) != 0); + } + + if (candidates.empty()) { + printf(" FAILURE: No games detected\n"); + failure++; + } else if (candidates.size() > 1) { + if (gameidDiffers) { + printf(" WARNING: Multiple games detected, some/all with wrong gameid\n"); + } else { + printf(" WARNING: Multiple games detected, but all have matching gameid\n"); + } + failure++; + } else if (gameidDiffers) { + printf(" FAILURE: Wrong gameid detected\n"); + failure++; + } else { + printf(" SUCCESS: Game was detected correctly\n"); + success++; + } + + for (x = candidates.begin(); x != candidates.end(); ++x) { + printf(" gameid '%s', desc '%s', language '%s', platform '%s'\n", + x->gameid().c_str(), + x->description().c_str(), + Common::getLanguageCode(x->language()), + Common::getPlatformCode(x->platform())); + } + } + int total = domains.size(); + printf("Detector test run: %d fail, %d success, %d skipped, out of %d\n", + failure, success, total - failure - success, total); +} +#endif + +#ifdef UPGRADE_ALL_TARGETS_HACK +void upgradeTargets() { + // HACK: The following upgrades all your targets to the latest and + // greatest. Right now that means updating the guioptions and (optionally) + // also the game descriptions. + // Basically, it loops over all targets, and calls the detector for the + // given path. It then compares the result with the settings of the target. + // If the basics seem to match, it updates the guioptions. + + printf("Upgrading all your existing targets\n"); + + Common::ConfigManager::DomainMap &domains = ConfMan.getGameDomains(); + Common::ConfigManager::DomainMap::iterator iter = domains.begin(); + for (iter = domains.begin(); iter != domains.end(); ++iter) { + Common::ConfigManager::Domain &dom = iter->_value; + Common::String name(iter->_key); + Common::String gameid(dom.get("gameid")); + Common::String path(dom.get("path")); + printf("Looking at target '%s', gameid '%s' ...\n", + name.c_str(), gameid.c_str()); + if (path.empty()) { + printf(" ... no path specified, skipping\n"); + continue; + } + if (gameid.empty()) { + gameid = name; + } + gameid.toLowercase(); // TODO: Is this paranoia? Maybe we should just assume all lowercase, always? + + Common::FSNode dir(path); + Common::FSList files; + if (!dir.getChildren(files, Common::FSNode::kListAll)) { + printf(" ... invalid path, skipping\n"); + continue; + } + + Common::Language lang = Common::parseLanguage(dom.get("language")); + Common::Platform plat = Common::parsePlatform(dom.get("platform")); + Common::String desc(dom.get("description")); + + GameList candidates(EngineMan.detectGames(files)); + GameDescriptor *g = 0; + + // We proceed as follows: + // * If detection failed to produce candidates, skip. + // * If there is a unique detector match, trust it. + // * If there are multiple match, run over them comparing gameid, language and platform. + // If we end up with a unique match, use it. Otherwise, skip. + if (candidates.size() == 0) { + printf(" ... failed to detect game, skipping\n"); + continue; + } + if (candidates.size() > 1) { + // Scan over all candidates, check if there is a unique match for gameid, language and platform + GameList::iterator x; + int matchesFound = 0; + for (x = candidates.begin(); x != candidates.end(); ++x) { + if (x->gameid() == gameid && x->language() == lang && x->platform() == plat) { + matchesFound++; + g = &(*x); + } + } + if (matchesFound != 1) { + printf(" ... detected multiple games, could not establish unique match, skipping\n"); + continue; + } + } else { + // Unique match -> use it + g = &candidates[0]; + } + + // At this point, g points to a GameDescriptor which we can use to update + // the target referred to by dom. We update several things + + // Always set the gameid explicitly (in case of legacy targets) + dom["gameid"] = g->gameid(); + + // Always set the GUI options. The user should not modify them, and engines might + // gain more features over time, so we want to keep this list up-to-date. + if (g->contains("guioptions")) { + printf(" -> update guioptions to '%s'\n", (*g)["guioptions"].c_str()); + dom["guioptions"] = (*g)["guioptions"]; + } else if (dom.contains("guioptions")) { + dom.erase("guioptions"); + } + + // Update the language setting but only if none has been set yet. + if (lang == Common::UNK_LANG && g->language() != Common::UNK_LANG) { + printf(" -> set language to '%s'\n", Common::getLanguageCode(g->language())); + dom["language"] = (*g)["language"]; + } + + // Update the platform setting but only if none has been set yet. + if (plat == Common::kPlatformUnknown && g->platform() != Common::kPlatformUnknown) { + printf(" -> set platform to '%s'\n", Common::getPlatformCode(g->platform())); + dom["platform"] = (*g)["platform"]; + } + + // TODO: We could also update the description. But not everybody will want that. + // Esp. because for some games (e.g. the combined Zak/Loom FM-TOWNS demo etc.) + // ScummVM still generates an incorrect description string. So, the description + // should only be updated if the user explicitly requests this. +#if 0 + if (desc != g->description()) { + printf(" -> update desc from '%s' to\n '%s' ?\n", desc.c_str(), g->description().c_str()); + dom["description"] = (*g)["description"]; + } +#endif + } + + // Finally, save our changes to disk + ConfMan.flushToDisk(); +} +#endif + #else // DISABLE_COMMAND_LINE @@ -449,6 +660,18 @@ bool processSettings(Common::String &command, Common::StringMap &settings) { printf(HELP_STRING, s_appName); return false; } +#ifdef DETECTOR_TESTING_HACK + else if (command == "test-detector") { + runDetectorTest(); + return false; + } +#endif +#ifdef UPGRADE_ALL_TARGETS_HACK + else if (command == "upgrade-targets") { + upgradeTargets(); + return false; + } +#endif #endif // DISABLE_COMMAND_LINE diff --git a/base/main.cpp b/base/main.cpp index 1270f5a65a5..4b7ed7d4e2e 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -41,12 +41,16 @@ #include "common/config-manager.h" #include "common/debug.h" #include "common/events.h" +#include "common/EventRecorder.h" #include "common/file.h" #include "common/fs.h" #include "common/system.h" + #include "gui/GuiManager.h" #include "gui/message.h" +#include "sound/audiocd.h" + #include "backends/keymapper/keymapper.h" #if defined(_WIN32_WCE) @@ -168,9 +172,9 @@ static Common::Error runGame(const EnginePlugin *plugin, OSystem &system, const // Set the window caption to the game name Common::String caption(ConfMan.get("description")); - Common::String desc = EngineMan.findGame(ConfMan.get("gameid")).description(); - if (caption.empty() && !desc.empty()) - caption = desc; + if (caption.empty()) { + caption = EngineMan.findGame(ConfMan.get("gameid")).description(); + } if (caption.empty()) caption = ConfMan.getActiveDomainName(); // Use the domain (=target) name if (!caption.empty()) { @@ -341,6 +345,14 @@ extern "C" int residual_main(int argc, const char * const argv[]) { // take place after the backend is initiated and the screen has been setup system.getEventManager()->init(); + // Directly after initializing the event manager, we will initialize our + // event recorder. + // + // TODO: This is just to match the current behavior, when we further extend + // our event recorder, we might do this at another place. Or even change + // the whole API for that ;-). + g_eventRec.init(); + // Now as the event manager is created, setup the keymapper setupKeymapper(system); @@ -368,11 +380,15 @@ extern "C" int residual_main(int argc, const char * const argv[]) { } // Quit unless an error occurred, or Return to launcher was requested + #ifndef FORCE_RTL if (result == 0 && !g_system->getEventManager()->shouldRTL()) break; - + #endif // Reset RTL flag in case we want to load another engine g_system->getEventManager()->resetRTL(); + #ifdef FORCE_RTL + g_system->getEventManager()->resetQuit(); + #endif // Discard any command line options. It's unlikely that the user // wanted to apply them to *all* games ever launched. @@ -389,6 +405,15 @@ extern "C" int residual_main(int argc, const char * const argv[]) { warning("Could not find any engine capable of running the selected game"); } + // We will destroy the AudioCDManager singleton here to save some memory. + // This will not make the CD audio stop, one would have to enable this: + //AudioCD.stop(); + // but the engine is responsible for stopping CD playback anyway and + // this way we catch engines not doing it properly. For some more + // information about why AudioCDManager::destroy does not stop the CD + // playback read the FIXME in sound/audiocd.h + Audio::AudioCDManager::destroy(); + // reset the graphics to default setupGraphics(system); launcherDialog(); diff --git a/base/main.h b/base/main.h index ae99e3de9a0..633bde292b9 100644 --- a/base/main.h +++ b/base/main.h @@ -28,6 +28,9 @@ #include "common/sys.h" +// +// The residual main entry point, to be invoked by ports +// extern "C" int residual_main(int argc, const char *const argv[]); #endif diff --git a/base/version.cpp b/base/version.cpp index 49076b1e8cb..2166222b8b2 100644 --- a/base/version.cpp +++ b/base/version.cpp @@ -23,9 +23,39 @@ * */ +#include "common/sys.h" #include "base/internal_version.h" #include "base/version.h" +/* + * Version string and build date string. These can be used by anything that + * wants to display this information to the user (e.g. about dialog). + * + * Note: it would be very nice if we could instead of (or in addition to) the + * build date present a date which corresponds to the date our source files + * were last changed. To understand the difference, imagine that a user + * makes a checkout on January 1, then after a week compiles it + * (e.g. after doing a 'make clean'). The build date then will say January 8 + * even though the files were last changed on January 1. + * + * Another problem is that __DATE__/__TIME__ depend on the local time zone. + * + * It's clear that such a "last changed" date would be much more useful to us + * for feedback purposes. After all, when somebody files a bug report, we + * don't care about the build date, we want to know which date their checkout + * was made. + * + * So, how could we implement this? At least on unix systems, a special script + * could do it. Basically, that script could parse the output of "svn info" or + * "svnversion" to determine the revision of the checkout, and insert that + * information somehow into the build process (e.g. by generating a tiny + * header file, analog to internal_version.h, maybe called svn_rev.h or so.) + * + * Drawback: This only works on systems which can run suitable scripts as part + * of the build proces (so I guess Visual C++ would be out of the game here? + * I don't know VC enough to be sure). And of course it must be robust enough + * to properly work in exports (i.e. release tar balls etc.). + */ const char *gResidualVersion = RESIDUAL_VERSION; const char *gResidualBuildDate = __DATE__ " " __TIME__; const char *gResidualVersionDate = RESIDUAL_VERSION " (" __DATE__ " " __TIME__ ")"; @@ -52,6 +82,14 @@ const char *gResidualFeatures = "" "MP3 " #endif +#ifdef USE_ALSA + "ALSA " +#endif + +#ifdef USE_RGB_COLOR + "RGB " +#endif + #ifdef USE_ZLIB "zLib " #endif diff --git a/base/version.h b/base/version.h index 188e3b52438..a004fbb0a10 100644 --- a/base/version.h +++ b/base/version.h @@ -3,7 +3,7 @@ * Residual is the legal property of its developers, whose names * are too numerous to list here. Please refer to the AUTHORS * 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 diff --git a/common/EventDispatcher.cpp b/common/EventDispatcher.cpp new file mode 100644 index 00000000000..3b2ec252903 --- /dev/null +++ b/common/EventDispatcher.cpp @@ -0,0 +1,138 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * 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::iterator i = _sources.begin(); i != _sources.end(); ++i) { + if (i->autoFree) + delete i->source; + } + + for (Common::List::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::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::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::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::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::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 00000000000..c0fc35e6733 --- /dev/null +++ b/common/EventRecorder.cpp @@ -0,0 +1,367 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * 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; + + _recordMode = kPassthrough; +} + +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(_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 00000000000..843059605df --- /dev/null +++ b/common/EventRecorder.h @@ -0,0 +1,106 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * 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/sys.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 { + friend class Common::Singleton; + 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 _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/archive.cpp b/common/archive.cpp index c0e28c87949..a69d3ea54d1 100644 --- a/common/archive.cpp +++ b/common/archive.cpp @@ -56,7 +56,7 @@ int Archive::listMatchingMembers(ArchiveMemberList &list, const String &pattern) ArchiveMemberList::iterator it = allNames.begin(); for ( ; it != allNames.end(); ++it) { - if ((*it)->getName().matchString(lowercasePattern, true)) { + if ((*it)->getName().matchString(lowercasePattern, false, true)) { list.push_back(*it); matches++; } @@ -123,6 +123,55 @@ void SearchSet::addDirectory(const String &name, const FSNode &dir, int priority add(name, new FSDirectory(dir, depth, flat), priority); } +void SearchSet::addSubDirectoriesMatching(const FSNode &directory, String origPattern, bool ignoreCase, int priority) { + FSList subDirs; + if (!directory.getChildren(subDirs)) + return; + + String nextPattern, pattern; + String::const_iterator sep = Common::find(origPattern.begin(), origPattern.end(), '/'); + if (sep != origPattern.end()) { + pattern = String(origPattern.begin(), sep); + + ++sep; + if (sep != origPattern.end()) + nextPattern = String(sep, origPattern.end()); + } + else { + pattern = origPattern; + } + + // TODO: The code we have for displaying all matches, which vary only in case, might + // be a bit overhead, but as long as we want to display all useful information to the + // user we will need to keep track of all directory names added so far. We might + // want to reconsider this though. + typedef HashMap MatchList; + MatchList multipleMatches; + MatchList::iterator matchIter; + + for (FSList::const_iterator i = subDirs.begin(); i != subDirs.end(); ++i) { + String name = i->getName(); + + if (Common::matchString(name.c_str(), pattern.c_str(), ignoreCase)) { + matchIter = multipleMatches.find(name); + if (matchIter == multipleMatches.end()) { + multipleMatches[name] = true; + } else { + if (matchIter->_value) { + warning("Clash in case for match of pattern \"%s\" found in directory \"%s\": \"%s\"", pattern.c_str(), directory.getPath().c_str(), matchIter->_key.c_str()); + matchIter->_value = false; + } + + warning("Clash in case for match of pattern \"%s\" found in directory \"%s\": \"%s\"", pattern.c_str(), directory.getPath().c_str(), name.c_str()); + } + + if (nextPattern.empty()) + addDirectory(name, *i, priority); + else + addSubDirectoriesMatching(*i, nextPattern, ignoreCase, priority); + } + } +} void SearchSet::remove(const String &name) { ArchiveNodeList::iterator it = find(name); diff --git a/common/archive.h b/common/archive.h index e40faed16eb..17073e82130 100644 --- a/common/archive.h +++ b/common/archive.h @@ -168,6 +168,52 @@ public: */ void addDirectory(const String &name, const FSNode &directory, int priority = 0, int depth = 1, bool flat = false); + /** + * Create and add a sub directory by name (caseless). + * + * It is also possible to add sub directories of sub directories (of any depth) with this function. + * The path seperator for this case is SLASH for *all* systems. + * + * An example would be: + * + * "game/itedata" + * + * In this example the code would first try to search for all directories matching + * "game" (case insensitive) in the path "directory" first and search through all + * of the matches for "itedata" (case insensitive too). + * + * Note that it will add *all* matches found! + * + * Even though this method is currently implemented via addSubDirectoriesMatching it is not safe + * to assume that this method is using anything other than a simple case insensitive compare. + * Thus do not use any tokens like '*' or '?' in the "caselessName" parameter of this function! + */ + void addSubDirectoryMatching(const FSNode &directory, const String &caselessName, int priority = 0) { + addSubDirectoriesMatching(directory, caselessName, true, priority); + } + + /** + * Create and add sub directories by pattern. + * + * It is also possible to add sub directories of sub directories (of any depth) with this function. + * The path seperator for this case is SLASH for *all* systems. + * + * An example would be: + * + * "game/itedata" + * + * In this example the code would first try to search for all directories matching + * "game" in the path "directory" first and search through all of the matches for + * "itedata". If "ingoreCase" is set to true, the code would do a case insensitive + * match, otherwise it is doing a case sensitive match. + * + * This method works of course also with tokens. For a list of available tokens + * see the documentation for Common::matchString. + * + * @see Common::matchString + */ + void addSubDirectoriesMatching(const FSNode &directory, String origPattern, bool ignoreCase, int priority = 0); + /** * Remove an archive from the searchable set. */ diff --git a/common/array.h b/common/array.h index d90f809ecf3..fbdd6ea4da9 100644 --- a/common/array.h +++ b/common/array.h @@ -222,7 +222,7 @@ public: T *old_storage = _storage; _capacity = newCapacity; - _storage = new T[newCapacity](); + _storage = new T[newCapacity]; assert(_storage); if (old_storage) { @@ -273,7 +273,7 @@ protected: // If there is not enough space, allocate more and // copy old elements over. uint newCapacity = roundUpCapacity(_size + n); - newStorage = new T[newCapacity](); + newStorage = new T[newCapacity]; assert(newStorage); copy(_storage, _storage + idx, newStorage); pos = newStorage + idx; diff --git a/common/config-file.cpp b/common/config-file.cpp index 57f43e51d24..cb6109cdd70 100644 --- a/common/config-file.cpp +++ b/common/config-file.cpp @@ -26,10 +26,8 @@ #include "common/config-file.h" #include "common/file.h" #include "common/savefile.h" -#include "common/util.h" -#include "common/debug.h" #include "common/system.h" - +#include "common/util.h" #define MAXLINELEN 256 diff --git a/common/config-manager.h b/common/config-manager.h index 95c720aca02..d2bad54b9ef 100644 --- a/common/config-manager.h +++ b/common/config-manager.h @@ -132,10 +132,10 @@ public: // // Some additional convenience accessors. // - int getInt(const String &key, const String &domName = String::emptyString) const; - bool getBool(const String &key, const String &domName = String::emptyString) const; - void setInt(const String &key, int value, const String &domName = String::emptyString); - void setBool(const String &key, bool value, const String &domName = String::emptyString); + int getInt(const String &key, const String &domName = String()) const; + bool getBool(const String &key, const String &domName = String()) const; + void setInt(const String &key, int value, const String &domName = String()); + void setBool(const String &key, bool value, const String &domName = String()); void registerDefault(const String &key, const String &value); diff --git a/common/debug.cpp b/common/debug.cpp index 55519720b26..b067c4919a7 100644 --- a/common/debug.cpp +++ b/common/debug.cpp @@ -154,7 +154,14 @@ static void debugHelper(const char *s, va_list va, bool caret = true) { char buf[STRINGBUFLEN]; vsnprintf(in_buf, STRINGBUFLEN, s, va); - strncpy(buf, in_buf, STRINGBUFLEN); + // Next, give the active engine (if any) a chance to augment the message, + // but only if not used from debugN. + if (g_engine && caret) { + g_engine->errorString(in_buf, buf, STRINGBUFLEN); + } else { + strncpy(buf, in_buf, STRINGBUFLEN); + } + buf[STRINGBUFLEN-1] = '\0'; if (caret) { buf[STRINGBUFLEN-2] = '\0'; diff --git a/common/endian.h b/common/endian.h index be05dc2de86..9e5c8c2403b 100644 --- a/common/endian.h +++ b/common/endian.h @@ -28,28 +28,115 @@ #include "common/sys.h" -// -// Endian conversion functions, macros etc., follow from here! -// +/** + * \file endian.h + * Endian conversion and byteswap conversion functions or macros + * + * SWAP_BYTES_??(a) - inverse byte order + * SWAP_CONSTANT_??(a) - inverse byte order, implemented as macro. + * Use with compiletime-constants only, the result will be a compiletime-constant aswell. + * Unlike most other functions these can be used for eg. switch-case labels + * + * READ_UINT??(a) - read native value from pointer a + * READ_??_UINT??(a) - read LE/BE value from pointer a and convert it to native + * WRITE_??_UINT??(a, v) - write native value v to pointer a with LE/BE encoding + * TO_??_??(a) - convert native value v to LE/BE + * FROM_??_??(a) - convert LE/BE value v to native + * CONSTANT_??_??(a) - convert LE/BE value v to native, implemented as macro. + * Use with compiletime-constants only, the result will be a compiletime-constant aswell. + * Unlike most other functions these can be used for eg. switch-case labels + */ + +// Sanity check +#if !defined(SYSTEM_LITTLE_ENDIAN) && !defined(SYSTEM_BIG_ENDIAN) +# error No endianness defined +#endif + +#define SWAP_CONSTANT_32(a) \ + ((uint32)((((a) >> 24) & 0x00FF) | \ + (((a) >> 8) & 0xFF00) | \ + (((a) & 0xFF00) << 8) | \ + (((a) & 0x00FF) << 24) )) + +#define SWAP_CONSTANT_16(a) \ + ((uint16)((((a) >> 8) & 0x00FF) | \ + (((a) << 8) & 0xFF00) )) /** * Swap the bytes in a 32 bit word in order to convert LE encoded data to BE * and vice versa. */ -FORCEINLINE uint32 SWAP_BYTES_32(uint32 a) { - return ((a >> 24) & 0x000000FF) | - ((a >> 8) & 0x0000FF00) | - ((a << 8) & 0x00FF0000) | - ((a << 24) & 0xFF000000); -} + +// machine/compiler-specific variants come first, fallback last + +// Test for GCC and if the target has the MIPS rel.2 instructions (we know the psp does) +#if defined(__GNUC__) && (defined(__psp__) || defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)) + + FORCEINLINE uint32 SWAP_BYTES_32(const uint32 a) { + if (__builtin_constant_p(a)) { + return SWAP_CONSTANT_32(a); + } else { + uint32 result; +# if defined(__psp__) + // use special allegrex instruction + __asm__ ("wsbw %0,%1" : "=r" (result) : "r" (a)); +# else + __asm__ ("wsbh %0,%1\n" + "rotr %0,%0,16" : "=r" (result) : "r" (a)); +# endif + return result; + } + } + +// Test for GCC >= 4.3.0 as this version added the bswap builtin +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + + FORCEINLINE uint32 SWAP_BYTES_32(uint32 a) { + return __builtin_bswap32(a); + } + +// test for MSVC 7 or newer +#elif defined(_MSC_VER) && _MSC_VER >= 1300 + + FORCEINLINE uint32 SWAP_BYTES_32(uint32 a) { + return _byteswap_ulong(a); + } + +// generic fallback +#else + + inline uint32 SWAP_BYTES_32(uint32 a) { + const uint16 low = (uint16)a, high = (uint16)(a >> 16); + return ((uint32)(uint16)((low >> 8) | (low << 8)) << 16) + | (uint16)((high >> 8) | (high << 8)); + } +#endif /** * Swap the bytes in a 16 bit word in order to convert LE encoded data to BE * and vice versa. */ -FORCEINLINE uint16 SWAP_BYTES_16(uint16 a) { - return ((a >> 8) & 0x00FF) + ((a << 8) & 0xFF00); -} + +// compilerspecific variants come first, fallback last + +// Test for GCC and if the target has the MIPS rel.2 instructions (we know the psp does) +#if defined(__GNUC__) && (defined(__psp__) || defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)) + + FORCEINLINE uint16 SWAP_BYTES_16(const uint16 a) { + if (__builtin_constant_p(a)) { + return SWAP_CONSTANT_16(a); + } else { + uint16 result; + __asm__ ("wsbh %0,%1" : "=r" (result) : "r" (a)); + return result; + } + } +#else + + inline uint16 SWAP_BYTES_16(const uint16 a) { + return (a >> 8) | (a << 8); + } +#endif /** @@ -70,25 +157,119 @@ FORCEINLINE uint16 SWAP_BYTES_16(uint16 a) { * For the latter systems we provide the INVERSE_MKID override. */ #if defined(INVERSE_MKID) -#define MKID_BE(a) ((uint32) \ - (((a) >> 24) & 0x000000FF) | \ - (((a) >> 8) & 0x0000FF00) | \ - (((a) << 8) & 0x00FF0000) | \ - (((a) << 24) & 0xFF000000)) +#define MKID_BE(a) SWAP_CONSTANT_32(a) #else # define MKID_BE(a) ((uint32)(a)) #endif +// Functions for reading/writing native Integers, +// this transparently handles the need for alignment + +#if !defined(SYSTEM_NEED_ALIGNMENT) + + FORCEINLINE uint16 READ_UINT16(const void *ptr) { + return *(const uint16 *)(ptr); + } + + FORCEINLINE uint32 READ_UINT32(const void *ptr) { + return *(const uint32 *)(ptr); + } + + FORCEINLINE void WRITE_UINT16(void *ptr, uint16 value) { + *(uint16 *)(ptr) = value; + } + + FORCEINLINE void WRITE_UINT32(void *ptr, uint32 value) { + *(uint32 *)(ptr) = value; + } + +// test for GCC >= 4.0. these implementations will automatically use CPU-specific +// instructions for unaligned data when they are available (eg. MIPS) +#elif defined(__GNUC__) && (__GNUC__ >= 4) + + FORCEINLINE uint16 READ_UINT16(const void *ptr) { + struct Unaligned16 { uint16 val; } __attribute__ ((__packed__)); + return ((const Unaligned16 *)ptr)->val; + } + + FORCEINLINE uint32 READ_UINT32(const void *ptr) { + struct Unaligned32 { uint32 val; } __attribute__ ((__packed__)); + return ((const Unaligned32 *)ptr)->val; + } + + FORCEINLINE void WRITE_UINT16(void *ptr, uint16 value) { + struct Unaligned16 { uint16 val; } __attribute__ ((__packed__)); + ((Unaligned16 *)ptr)->val = value; + } + + FORCEINLINE void WRITE_UINT32(void *ptr, uint32 value) { + struct Unaligned32 { uint32 val; } __attribute__ ((__packed__)); + ((Unaligned32 *)ptr)->val = value; + } + +// use software fallback by loading each byte explicitely +#else + +# if defined(SYSTEM_LITTLE_ENDIAN) + + inline uint16 READ_UINT16(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[1] << 8) | b[0]; + } + inline uint32 READ_UINT32(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]); + } + inline void WRITE_UINT16(void *ptr, uint16 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 0); + b[1] = (uint8)(value >> 8); + } + inline void WRITE_UINT32(void *ptr, uint32 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 0); + b[1] = (uint8)(value >> 8); + b[2] = (uint8)(value >> 16); + b[3] = (uint8)(value >> 24); + } + +# elif defined(SYSTEM_BIG_ENDIAN) + + inline uint16 READ_UINT16(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[0] << 8) | b[1]; + } + inline uint32 READ_UINT32(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3]); + } + inline void WRITE_UINT16(void *ptr, uint16 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 8); + b[1] = (uint8)(value >> 0); + } + inline void WRITE_UINT32(void *ptr, uint32 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 24); + b[1] = (uint8)(value >> 16); + b[2] = (uint8)(value >> 8); + b[3] = (uint8)(value >> 0); + } + +# endif + +#endif +// Map Funtions for reading/writing BE/LE integers depending on native endianess #if defined(SYSTEM_LITTLE_ENDIAN) - #define READ_UINT16(a) READ_LE_UINT16(a) - #define READ_UINT32(a) READ_LE_UINT32(a) + #define READ_LE_UINT16(a) READ_UINT16(a) + #define READ_LE_UINT32(a) READ_UINT32(a) - #define WRITE_UINT16(a, v) WRITE_LE_UINT16(a, v) - #define WRITE_UINT32(a, v) WRITE_LE_UINT32(a, v) + #define WRITE_LE_UINT16(a, v) WRITE_UINT16(a, v) + #define WRITE_LE_UINT32(a, v) WRITE_UINT32(a, v) #define FROM_LE_32(a) ((uint32)(a)) #define FROM_LE_16(a) ((uint16)(a)) @@ -102,16 +283,61 @@ FORCEINLINE uint16 SWAP_BYTES_16(uint16 a) { #define TO_BE_32(a) SWAP_BYTES_32(a) #define TO_BE_16(a) SWAP_BYTES_16(a) + #define CONSTANT_LE_32(a) ((uint32)(a)) + #define CONSTANT_LE_16(a) ((uint16)(a)) + + #define CONSTANT_BE_32(a) SWAP_CONSTANT_32(a) + #define CONSTANT_BE_16(a) SWAP_CONSTANT_16(a) + +// if the unaligned load and the byteswap take alot instructions its better to directly read and invert +# if defined(SYSTEM_NEED_ALIGNMENT) && !defined(__mips__) + + inline uint16 READ_BE_UINT16(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[0] << 8) | b[1]; + } + inline uint32 READ_BE_UINT32(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3]); + } + inline void WRITE_BE_UINT16(void *ptr, uint16 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 8); + b[1] = (uint8)(value >> 0); + } + inline void WRITE_BE_UINT32(void *ptr, uint32 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 24); + b[1] = (uint8)(value >> 16); + b[2] = (uint8)(value >> 8); + b[3] = (uint8)(value >> 0); + } +# else + + inline uint16 READ_BE_UINT16(const void *ptr) { + return SWAP_BYTES_16(READ_UINT16(ptr)); + } + inline uint32 READ_BE_UINT32(const void *ptr) { + return SWAP_BYTES_32(READ_UINT32(ptr)); + } + inline void WRITE_BE_UINT16(void *ptr, uint16 value) { + WRITE_UINT16(ptr, SWAP_BYTES_16(value)); + } + inline void WRITE_BE_UINT32(void *ptr, uint32 value) { + WRITE_UINT32(ptr, SWAP_BYTES_32(value)); + } + +# endif // if defined(SYSTEM_NEED_ALIGNMENT) + #elif defined(SYSTEM_BIG_ENDIAN) - #define MKID(a) ((uint32)(a)) #define MKID_BE(a) ((uint32)(a)) - #define READ_UINT16(a) READ_BE_UINT16(a) - #define READ_UINT32(a) READ_BE_UINT32(a) + #define READ_BE_UINT16(a) READ_UINT16(a) + #define READ_BE_UINT32(a) READ_UINT32(a) - #define WRITE_UINT16(a, v) WRITE_BE_UINT16(a, v) - #define WRITE_UINT32(a, v) WRITE_BE_UINT32(a, v) + #define WRITE_BE_UINT16(a, v) WRITE_UINT16(a, v) + #define WRITE_BE_UINT32(a, v) WRITE_UINT32(a, v) #define FROM_LE_32(a) SWAP_BYTES_32(a) #define FROM_LE_16(a) SWAP_BYTES_16(a) @@ -125,95 +351,62 @@ FORCEINLINE uint16 SWAP_BYTES_16(uint16 a) { #define TO_BE_32(a) ((uint32)(a)) #define TO_BE_16(a) ((uint16)(a)) -#else + #define CONSTANT_LE_32(a) SWAP_CONSTANT_32(a) + #define CONSTANT_LE_16(a) SWAP_CONSTANT_16(a) - #error No endianness defined + #define CONSTANT_BE_32(a) ((uint32)(a)) + #define CONSTANT_BE_16(a) ((uint16)(a)) +// if the unaligned load and the byteswap take alot instructions its better to directly read and invert +# if defined(SYSTEM_NEED_ALIGNMENT) && !defined(__mips__) -#endif + inline uint16 READ_LE_UINT16(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[1] << 8) | b[0]; + } + inline uint32 READ_LE_UINT32(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]); + } + inline void WRITE_LE_UINT16(void *ptr, uint16 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 0); + b[1] = (uint8)(value >> 8); + } + inline void WRITE_LE_UINT32(void *ptr, uint32 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 0); + b[1] = (uint8)(value >> 8); + b[2] = (uint8)(value >> 16); + b[3] = (uint8)(value >> 24); + } +# else + inline uint16 READ_LE_UINT16(const void *ptr) { + return SWAP_BYTES_16(READ_UINT16(ptr)); + } + inline uint32 READ_LE_UINT32(const void *ptr) { + return SWAP_BYTES_32(READ_UINT32(ptr)); + } + inline void WRITE_LE_UINT16(void *ptr, uint16 value) { + WRITE_UINT16(ptr, SWAP_BYTES_16(value)); + } + inline void WRITE_LE_UINT32(void *ptr, uint32 value) { + WRITE_UINT32(ptr, SWAP_BYTES_32(value)); + } + +# endif // if defined(SYSTEM_NEED_ALIGNMENT) -#if defined(SYSTEM_NEED_ALIGNMENT) || !defined(SYSTEM_LITTLE_ENDIAN) - FORCEINLINE uint16 READ_LE_UINT16(const void *ptr) { - const byte *b = (const byte *)ptr; - return (b[1] << 8) + b[0]; - } - FORCEINLINE uint32 READ_LE_UINT32(const void *ptr) { - const byte *b = (const byte *)ptr; - return (b[3] << 24) + (b[2] << 16) + (b[1] << 8) + (b[0]); - } - FORCEINLINE void WRITE_LE_UINT16(void *ptr, uint16 value) { - byte *b = (byte *)ptr; - b[0] = (byte)(value >> 0); - b[1] = (byte)(value >> 8); - } - FORCEINLINE void WRITE_LE_UINT32(void *ptr, uint32 value) { - byte *b = (byte *)ptr; - b[0] = (byte)(value >> 0); - b[1] = (byte)(value >> 8); - b[2] = (byte)(value >> 16); - b[3] = (byte)(value >> 24); - } -#else - FORCEINLINE uint16 READ_LE_UINT16(const void *ptr) { - return *(const uint16 *)(ptr); - } - FORCEINLINE uint32 READ_LE_UINT32(const void *ptr) { - return *(const uint32 *)(ptr); - } - FORCEINLINE void WRITE_LE_UINT16(void *ptr, uint16 value) { - *(uint16 *)(ptr) = value; - } - FORCEINLINE void WRITE_LE_UINT32(void *ptr, uint32 value) { - *(uint32 *)(ptr) = value; - } -#endif +#endif // if defined(SYSTEM_LITTLE_ENDIAN) - -#if defined(SYSTEM_NEED_ALIGNMENT) || !defined(SYSTEM_BIG_ENDIAN) - FORCEINLINE uint16 READ_BE_UINT16(const void *ptr) { - const byte *b = (const byte *)ptr; - return (b[0] << 8) + b[1]; - } - FORCEINLINE uint32 READ_BE_UINT32(const void *ptr) { - const byte *b = (const byte*)ptr; - return (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + (b[3]); - } - FORCEINLINE void WRITE_BE_UINT16(void *ptr, uint16 value) { - byte *b = (byte *)ptr; - b[0] = (byte)(value >> 8); - b[1] = (byte)(value >> 0); - } - FORCEINLINE void WRITE_BE_UINT32(void *ptr, uint32 value) { - byte *b = (byte *)ptr; - b[0] = (byte)(value >> 24); - b[1] = (byte)(value >> 16); - b[2] = (byte)(value >> 8); - b[3] = (byte)(value >> 0); - } -#else - FORCEINLINE uint16 READ_BE_UINT16(const void *ptr) { - return *(const uint16 *)(ptr); - } - FORCEINLINE uint32 READ_BE_UINT32(const void *ptr) { - return *(const uint32 *)(ptr); - } - FORCEINLINE void WRITE_BE_UINT16(void *ptr, uint16 value) { - *(uint16 *)(ptr) = value; - } - FORCEINLINE void WRITE_BE_UINT32(void *ptr, uint32 value) { - *(uint32 *)(ptr) = value; - } -#endif - -FORCEINLINE uint32 READ_LE_UINT24(const void *ptr) { - const byte *b = (const byte *)ptr; - return (b[2] << 16) + (b[1] << 8) + (b[0]); +inline uint32 READ_LE_UINT24(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[2] << 16) | (b[1] << 8) | (b[0]); } -FORCEINLINE uint32 READ_BE_UINT24(const void *ptr) { - const byte *b = (const byte*)ptr; - return (b[0] << 16) + (b[1] << 8) + (b[2]); +inline uint32 READ_BE_UINT24(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[0] << 16) | (b[1] << 8) | (b[2]); } #if defined(SYSTEM_BIG_ENDIAN) diff --git a/common/error.h b/common/error.h index e58ae90db71..89fe097c3cf 100644 --- a/common/error.h +++ b/common/error.h @@ -48,6 +48,7 @@ enum Error { kInvalidPathError, //!< Engine initialization: Invalid game path was passed kNoGameDataFoundError, //!< Engine initialization: No game data was found in the specified location kUnsupportedGameidError, //!< Engine initialization: Gameid not supported by this (Meta)Engine + kUnsupportedColorMode, //!< Engine initialization: Engine does not support backend's color mode kReadPermissionDenied, //!< Unable to read data due to missing read permission diff --git a/common/events.h b/common/events.h index aa76c4116d8..2f33c3df2d1 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 _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 _sources; + + struct ObserverEntry : public Entry { + uint priority; + EventObserver *observer; + }; + + Common::List _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; @@ -195,7 +377,9 @@ public: * Used when we have returned to the launcher. */ virtual void resetRTL() = 0; - +#ifdef FORCE_RTL + virtual void resetQuit() = 0; +#endif // Optional: check whether a given key is currently pressed ???? //virtual bool isKeyPressed(int keycode) = 0; @@ -207,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 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 367285a9218..a3c14bdd3da 100644 --- a/common/file.cpp +++ b/common/file.cpp @@ -32,16 +32,6 @@ namespace Common { -void File::addDefaultDirectory(const String &directory) { - FSNode dir(directory); - addDefaultDirectory(dir); -} - -void File::addDefaultDirectory(const FSNode &dir) { - if (dir.exists() && dir.isDirectory()) - SearchMan.addDirectory(dir.getPath(), dir); -} - File::File() : _handle(0) { } @@ -120,15 +110,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 cbd27ea18a4..72e29653a44 100644 --- a/common/file.h +++ b/common/file.h @@ -48,10 +48,6 @@ protected: String _name; public: - - static void addDefaultDirectory(const String &directory); - static void addDefaultDirectory(const FSNode &directory); - File(); virtual ~File(); @@ -126,19 +122,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/fs.cpp b/common/fs.cpp index 6adbaeb0c8c..0ec7a02d92d 100644 --- a/common/fs.cpp +++ b/common/fs.cpp @@ -249,7 +249,7 @@ SeekableReadStream *FSDirectory::createReadStreamForMember(const String &name) c } FSDirectory *FSDirectory::getSubDirectory(const String &name, int depth, bool flat) { - return getSubDirectory(String::emptyString, name, depth, flat); + return getSubDirectory(String(), name, depth, flat); } FSDirectory *FSDirectory::getSubDirectory(const String &prefix, const String &name, int depth, bool flat) { @@ -320,7 +320,7 @@ int FSDirectory::listMatchingMembers(ArchiveMemberList &list, const String &patt int matches = 0; NodeCache::iterator it = _fileCache.begin(); for ( ; it != _fileCache.end(); ++it) { - if (it->_key.matchString(lowercasePattern, true)) { + if (it->_key.matchString(lowercasePattern, false, true)) { list.push_back(ArchiveMemberPtr(new FSNode(it->_value))); matches++; } diff --git a/common/hashmap.h b/common/hashmap.h index 1990be4cc1a..3f5920367a0 100644 --- a/common/hashmap.h +++ b/common/hashmap.h @@ -40,6 +40,12 @@ namespace Common { +// The sgi IRIX MIPSpro Compiler has difficulties with nested templates. +// This and the other __sgi conditionals below work around these problems. +#ifdef __sgi +template class IteratorImpl; +#endif + // Enable the following #define if you want to check how many collisions the // code produces (many collisions indicate either a bad hash function, or a // hash table that is too small). @@ -93,39 +99,41 @@ public: ObjectPool _nodePool; - Node *allocNode(const Key &key) { - return new (_nodePool) Node(key); - } - - void freeNode(Node *node) { - if (node && node != &_dummyNode) - _nodePool.deleteChunk(node); - } - - Node **_storage; // hashtable of size arrsize. - uint _mask; /**< Capacity of the HashMap minus one; must be a power of two of minus one */ + Node **_storage; ///< hashtable of size arrsize. + uint _mask; ///< Capacity of the HashMap minus one; must be a power of two of minus one uint _size; uint _deleted; ///< Number of deleted elements (_dummyNodes) HashFunc _hash; EqualFunc _equal; - // Default value, returned by the const getVal. + /** Default value, returned by the const getVal. */ const Val _defaultVal; - // Dummy node, used as marker for erased objects. - Node _dummyNode; + /** Dummy node, used as marker for erased objects. */ + #define HASHMAP_DUMMY_NODE ((Node *)1) #ifdef DEBUG_HASH_COLLISIONS mutable int _collisions, _lookups, _dummyHits; #endif + Node *allocNode(const Key &key) { + return new (_nodePool) Node(key); + } + + void freeNode(Node *node) { + if (node && node != HASHMAP_DUMMY_NODE) + _nodePool.deleteChunk(node); + } + void assign(const HM_t &map); int lookup(const Key &key) const; int lookupAndCreateIfMissing(const Key &key); void expandStorage(uint newCapacity); +#ifndef __sgi template friend class IteratorImpl; +#endif /** * Simple HashMap iterator implementation. @@ -133,7 +141,11 @@ public: template class IteratorImpl { friend class HashMap; +#ifdef __sgi + template friend class Common::IteratorImpl; +#else template friend class IteratorImpl; +#endif protected: typedef const HashMap hashmap_t; @@ -148,6 +160,7 @@ public: assert(_idx <= _hashmap->_mask); Node *node = _hashmap->_storage[_idx]; assert(node != 0); + assert(node != HASHMAP_DUMMY_NODE); return node; } @@ -166,7 +179,7 @@ public: assert(_hashmap); do { _idx++; - } while (_idx <= _hashmap->_mask && _hashmap->_storage[_idx] == 0); + } while (_idx <= _hashmap->_mask && (_hashmap->_storage[_idx] == 0 || _hashmap->_storage[_idx] == HASHMAP_DUMMY_NODE)); if (_idx > _hashmap->_mask) _idx = (uint)-1; @@ -207,6 +220,7 @@ public: Val &getVal(const Key &key); const Val &getVal(const Key &key) const; + const Val &getVal(const Key &key, const Val &defaultVal) const; void setVal(const Key &key, const Val &val); void clear(bool shrinkArray = 0); @@ -218,7 +232,7 @@ public: iterator begin() { // Find and return the first non-empty entry for (uint ctr = 0; ctr <= _mask; ++ctr) { - if (_storage[ctr]) + if (_storage[ctr] && _storage[ctr] != HASHMAP_DUMMY_NODE) return iterator(ctr, this); } return end(); @@ -230,7 +244,7 @@ public: const_iterator begin() const { // Find and return the first non-empty entry for (uint ctr = 0; ctr <= _mask; ++ctr) { - if (_storage[ctr]) + if (_storage[ctr] && _storage[ctr] != HASHMAP_DUMMY_NODE) return const_iterator(ctr, this); } return end(); @@ -274,7 +288,7 @@ HashMap::HashMap() #ifdef __PLAYSTATION2__ { #else - : _defaultVal(), _dummyNode() { + : _defaultVal() { #endif _mask = HASHMAP_MIN_CAPACITY - 1; _storage = new Node *[HASHMAP_MIN_CAPACITY]; @@ -298,7 +312,7 @@ HashMap::HashMap() */ template HashMap::HashMap(const HM_t &map) : - _defaultVal(), _dummyNode() { + _defaultVal() { #ifdef DEBUG_HASH_COLLISIONS _collisions = 0; _lookups = 0; @@ -340,8 +354,8 @@ void HashMap::assign(const HM_t &map) { _size = 0; _deleted = 0; for (uint ctr = 0; ctr <= _mask; ++ctr) { - if (map._storage[ctr] == &map._dummyNode) { - _storage[ctr] = &_dummyNode; + if (map._storage[ctr] == HASHMAP_DUMMY_NODE) { + _storage[ctr] = HASHMAP_DUMMY_NODE; _deleted++; } else if (map._storage[ctr] != NULL) { _storage[ctr] = allocNode(map._storage[ctr]->_key); @@ -383,7 +397,9 @@ template void HashMap::expandStorage(uint newCapacity) { assert(newCapacity > _mask+1); +#ifndef NDEBUG const uint old_size = _size; +#endif const uint old_mask = _mask; Node **old_storage = _storage; @@ -397,7 +413,7 @@ void HashMap::expandStorage(uint newCapacity) { // rehash all the old elements for (uint ctr = 0; ctr <= old_mask; ++ctr) { - if (old_storage[ctr] == NULL || old_storage[ctr] == &_dummyNode) + if (old_storage[ctr] == NULL || old_storage[ctr] == HASHMAP_DUMMY_NODE) continue; // Insert the element from the old table into the new table. @@ -406,7 +422,7 @@ void HashMap::expandStorage(uint newCapacity) { // don't have to call _equal(). const uint hash = _hash(old_storage[ctr]->_key); uint idx = hash & _mask; - for (uint perturb = hash; _storage[idx] != NULL && _storage[idx] != &_dummyNode; perturb >>= HASHMAP_PERTURB_SHIFT) { + for (uint perturb = hash; _storage[idx] != NULL && _storage[idx] != HASHMAP_DUMMY_NODE; perturb >>= HASHMAP_PERTURB_SHIFT) { idx = (5 * idx + perturb + 1) & _mask; } @@ -430,7 +446,7 @@ int HashMap::lookup(const Key &key) const { for (uint perturb = hash; ; perturb >>= HASHMAP_PERTURB_SHIFT) { if (_storage[ctr] == NULL) break; - if (_storage[ctr] == &_dummyNode) { + if (_storage[ctr] == HASHMAP_DUMMY_NODE) { #ifdef DEBUG_HASH_COLLISIONS _dummyHits++; #endif @@ -464,7 +480,7 @@ int HashMap::lookupAndCreateIfMissing(const Key & for (uint perturb = hash; ; perturb >>= HASHMAP_PERTURB_SHIFT) { if (_storage[ctr] == NULL) break; - if (_storage[ctr] == &_dummyNode) { + if (_storage[ctr] == HASHMAP_DUMMY_NODE) { #ifdef DEBUG_HASH_COLLISIONS _dummyHits++; #endif @@ -540,11 +556,16 @@ Val &HashMap::getVal(const Key &key) { template const Val &HashMap::getVal(const Key &key) const { + return getVal(key, _defaultVal); +} + +template +const Val &HashMap::getVal(const Key &key, const Val &defaultVal) const { uint ctr = lookup(key); if (_storage[ctr] != NULL) return _storage[ctr]->_value; else - return _defaultVal; + return defaultVal; } template @@ -563,12 +584,14 @@ void HashMap::erase(const Key &key) { // If we remove a key, we replace it with a dummy node. freeNode(_storage[ctr]); - _storage[ctr] = &_dummyNode; + _storage[ctr] = HASHMAP_DUMMY_NODE; _size--; _deleted++; return; } +#undef HASHMAP_DUMMY_NODE + } // End of namespace Common #endif diff --git a/common/libz.cpp b/common/libz.cpp index 3d4cb654f90..a74774f5a75 100644 --- a/common/libz.cpp +++ b/common/libz.cpp @@ -154,7 +154,7 @@ public: bool seek(int32 offset, int whence = SEEK_SET) { int32 newPos = 0; assert(whence != SEEK_END); // SEEK_END not supported - switch(whence) { + switch (whence) { case SEEK_SET: newPos = offset; break; diff --git a/common/memorypool.cpp b/common/memorypool.cpp index ef94e2236b0..9d8ffaf2451 100644 --- a/common/memorypool.cpp +++ b/common/memorypool.cpp @@ -46,6 +46,12 @@ MemoryPool::MemoryPool(size_t chunkSize) { } MemoryPool::~MemoryPool() { +#if 0 + freeUnusedPages(); + if (!_pages.empty()) + warning("Memory leak found in pool"); +#endif + for (size_t i = 0; i < _pages.size(); ++i) ::free(_pages[i].start); } @@ -62,7 +68,7 @@ void MemoryPool::allocPage() { _pages.push_back(page); - // Next time, we'll alocate a page twice as big as this one. + // Next time, we'll allocate a page twice as big as this one. _chunksPerPage *= 2; // Add the page to the pool of free chunk diff --git a/common/module.mk b/common/module.mk index 5045044a645..2c280d666d7 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 cba27f1669d..87c0ac481ad 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -28,6 +28,8 @@ #include "common/memorypool.h" +#include + #if !defined(__SYMBIAN32__) #include #endif @@ -350,12 +352,12 @@ bool String::contains(char x) const { return strchr(c_str(), x) != NULL; } -bool String::matchString(const char *pat, bool pathMode) const { - return Common::matchString(c_str(), pat, pathMode); +bool String::matchString(const char *pat, bool ignoreCase, bool pathMode) const { + return Common::matchString(c_str(), pat, ignoreCase, pathMode); } -bool String::matchString(const String &pat, bool pathMode) const { - return Common::matchString(c_str(), pat.c_str(), pathMode); +bool String::matchString(const String &pat, bool ignoreCase, bool pathMode) const { + return Common::matchString(c_str(), pat.c_str(), ignoreCase, pathMode); } void String::deleteLastChar() { @@ -435,6 +437,50 @@ 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 == -1) { + // MSVC doesn't return the size the full string would take up. + // Try increasing the size of the string until it fits. + + // We assume MSVC failed to output the correct, null-terminated string + // if the return value is either -1 or size. + int size = _builtinCapacity; + do { + size *= 2; + output.ensureCapacity(size-1, false); + assert(!output.isStorageIntern()); + size = output._extern._capacity; + + va_start(va, fmt); + len = vsnprintf(output._str, size, fmt, va); + va_end(va); + } while (len == -1 || len >= size); + } else 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 { @@ -635,7 +681,7 @@ Common::String normalizePath(const Common::String &path, const char sep) { return result; } -bool matchString(const char *str, const char *pat, bool pathMode) { +bool matchString(const char *str, const char *pat, bool ignoreCase, bool pathMode) { assert(str); assert(pat); @@ -652,7 +698,7 @@ bool matchString(const char *str, const char *pat, bool pathMode) { switch (*pat) { case '*': - // Record pattern / string possition for backtracking + // Record pattern / string position for backtracking p = ++pat; q = str; // If pattern ended with * -> match @@ -661,7 +707,8 @@ bool matchString(const char *str, const char *pat, bool pathMode) { break; default: - if (*pat != *str) { + if ((!ignoreCase && *pat != *str) || + (ignoreCase && tolower(*pat) != tolower(*str))) { if (p) { // No match, oops -> try to backtrack pat = p; diff --git a/common/str.h b/common/str.h index 16293b20d8d..f3dcb133c39 100644 --- a/common/str.h +++ b/common/str.h @@ -170,12 +170,13 @@ public: * * @param str Text to be matched against the given pattern. * @param pat Glob pattern. + * @param ignoreCase Whether to ignore the case when doing pattern match * @param pathMode Whether to use path mode, i.e., whether slashes must be matched explicitly. * * @return true if str matches the pattern, false otherwise. */ - bool matchString(const char *pat, bool pathMode = false) const; - bool matchString(const String &pat, bool pathMode = false) const; + bool matchString(const char *pat, bool ignoreCase = false, bool pathMode = false) const; + bool matchString(const String &pat, bool ignoreCase = false, bool pathMode = false) const; inline const char *c_str() const { return _str; } @@ -218,6 +219,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; @@ -311,11 +317,12 @@ Common::String normalizePath(const Common::String &path, const char sep); * * @param str Text to be matched against the given pattern. * @param pat Glob pattern. + * @param ignoreCase Whether to ignore the case when doing pattern match * @param pathMode Whether to use path mode, i.e., whether slashes must be matched explicitly. * * @return true if str matches the pattern, false otherwise. */ -bool matchString(const char *str, const char *pat, bool pathMode = false); +bool matchString(const char *str, const char *pat, bool ignoreCase = false, bool pathMode = false); typedef Array StringList; diff --git a/common/stream.cpp b/common/stream.cpp index b4d85c97bb9..27355da624c 100644 --- a/common/stream.cpp +++ b/common/stream.cpp @@ -202,7 +202,7 @@ bool SeekableSubReadStream::seek(int32 offset, int whence) { assert(_pos >= _begin); assert(_pos <= _end); - switch(whence) { + switch (whence) { case SEEK_END: offset = size() + offset; // fallthrough diff --git a/common/stream.h b/common/stream.h index fae84d93978..582681e1090 100644 --- a/common/stream.h +++ b/common/stream.h @@ -27,6 +27,7 @@ #define COMMON_STREAM_H #include "common/sys.h" +#include "common/endian.h" namespace Common { @@ -106,38 +107,38 @@ public: } void writeUint16LE(uint16 value) { - writeByte((byte)(value & 0xff)); - writeByte((byte)(value >> 8)); + value = TO_LE_16(value); + write(&value, 2); } void writeUint32LE(uint32 value) { - writeUint16LE((uint16)(value & 0xffff)); - writeUint16LE((uint16)(value >> 16)); + value = TO_LE_32(value); + write(&value, 4); } void writeUint16BE(uint16 value) { - writeByte((byte)(value >> 8)); - writeByte((byte)(value & 0xff)); + value = TO_BE_16(value); + write(&value, 2); } void writeUint32BE(uint32 value) { - writeUint16BE((uint16)(value >> 16)); - writeUint16BE((uint16)(value & 0xffff)); + value = TO_BE_32(value); + write(&value, 4); } - void writeSint16LE(int16 value) { + FORCEINLINE void writeSint16LE(int16 value) { writeUint16LE((uint16)value); } - void writeSint32LE(int32 value) { + FORCEINLINE void writeSint32LE(int32 value) { writeUint32LE((uint32)value); } - void writeSint16BE(int16 value) { + FORCEINLINE void writeSint16BE(int16 value) { writeUint16BE((uint16)value); } - void writeSint32BE(int32 value) { + FORCEINLINE void writeSint32BE(int32 value) { writeUint32BE((uint32)value); } @@ -179,7 +180,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. @@ -188,7 +189,7 @@ public: * calling err() and eos() ). */ byte readByte() { - byte b = 0; + byte b = 0; // FIXME: remove initialisation read(&b, 1); return b; } @@ -199,10 +200,8 @@ public: * if a read error occurred (for which client code can check by * calling err() and eos() ). */ - int8 readSByte() { - int8 b = 0; - read(&b, 1); - return b; + FORCEINLINE int8 readSByte() { + return (int8)readByte(); } /** @@ -213,9 +212,9 @@ public: * calling err() and eos() ). */ uint16 readUint16LE() { - uint16 a = readByte(); - uint16 b = readByte(); - return a | (b << 8); + uint16 val; + read(&val, 2); + return FROM_LE_16(val); } /** @@ -226,9 +225,9 @@ public: * calling err() and eos() ). */ uint32 readUint32LE() { - uint32 a = readUint16LE(); - uint32 b = readUint16LE(); - return (b << 16) | a; + uint32 val; + read(&val, 4); + return FROM_LE_32(val); } /** @@ -239,9 +238,9 @@ public: * calling err() and eos() ). */ uint16 readUint16BE() { - uint16 b = readByte(); - uint16 a = readByte(); - return a | (b << 8); + uint16 val; + read(&val, 2); + return FROM_BE_16(val); } /** @@ -252,9 +251,9 @@ public: * calling err() and eos() ). */ uint32 readUint32BE() { - uint32 b = readUint16BE(); - uint32 a = readUint16BE(); - return (b << 16) | a; + uint32 val; + read(&val, 4); + return FROM_BE_32(val); } /** @@ -264,7 +263,7 @@ public: * if a read error occurred (for which client code can check by * calling err() and eos() ). */ - int16 readSint16LE() { + FORCEINLINE int16 readSint16LE() { return (int16)readUint16LE(); } @@ -275,7 +274,7 @@ public: * if a read error occurred (for which client code can check by * calling err() and eos() ). */ - int32 readSint32LE() { + FORCEINLINE int32 readSint32LE() { return (int32)readUint32LE(); } @@ -286,7 +285,7 @@ public: * if a read error occurred (for which client code can check by * calling err() and eos() ). */ - int16 readSint16BE() { + FORCEINLINE int16 readSint16BE() { return (int16)readUint16BE(); } @@ -297,7 +296,7 @@ public: * if a read error occurred (for which client code can check by * calling err() and eos() ). */ - int32 readSint32BE() { + FORCEINLINE int32 readSint32BE() { return (int32)readUint32BE(); } @@ -400,6 +399,7 @@ public: /** * SubReadStream provides access to a ReadStream restricted to the range * [currentPosition, currentPosition+end). + * * Manipulating the parent stream directly /will/ mess up a substream. * Likewise, manipulating two substreams of a parent stream will cause them to * step on each others toes. @@ -434,6 +434,9 @@ public: * SeekableSubReadStream provides access to a SeekableReadStream restricted to * the range [begin, end). * The same caveats apply to SeekableSubReadStream as do to SeekableReadStream. + * + * Manipulating the parent stream directly /will/ mess up a substream. + * @see SubReadStream */ class SeekableSubReadStream : public SubReadStream, public SeekableReadStream { protected: @@ -451,28 +454,36 @@ public: /** * This is a wrapper around SeekableSubReadStream, but it adds non-endian * read methods whose endianness is set on the stream creation. + * + * Manipulating the parent stream directly /will/ mess up a substream. + * @see SubReadStream */ class SeekableSubReadStreamEndian : public SeekableSubReadStream { -public: - bool _bigEndian; +private: + const bool _bigEndian; +public: SeekableSubReadStreamEndian(SeekableReadStream *parentStream, uint32 begin, uint32 end, bool bigEndian = false, bool disposeParentStream = false) : SeekableSubReadStream(parentStream, begin, end, disposeParentStream), _bigEndian(bigEndian) { } - inline uint16 readUint16() { - return (_bigEndian) ? readUint16BE() : readUint16LE(); + uint16 readUint16() { + uint16 val; + read(&val, 2); + return (_bigEndian) ? TO_BE_16(val) : TO_LE_16(val); } - inline uint32 readUint32() { - return (_bigEndian) ? readUint32BE() : readUint32LE(); + uint32 readUint32() { + uint32 val; + read(&val, 4); + return (_bigEndian) ? TO_BE_32(val) : TO_LE_32(val); } - inline int16 readSint16() { + FORCEINLINE int16 readSint16() { return (int16)readUint16(); } - inline int32 readSint32() { + FORCEINLINE int32 readSint32() { return (int32)readUint32(); } }; @@ -575,23 +586,28 @@ public: */ class MemoryReadStreamEndian : public Common::MemoryReadStream { private: + const bool _bigEndian; + public: - bool _bigEndian; MemoryReadStreamEndian(const byte *buf, uint32 len, bool bigEndian = false) : MemoryReadStream(buf, len), _bigEndian(bigEndian) {} - inline uint16 readUint16() { - return (_bigEndian) ? readUint16BE() : readUint16LE(); + uint16 readUint16() { + uint16 val; + read(&val, 2); + return (_bigEndian) ? TO_BE_16(val) : TO_LE_16(val); } - inline uint32 readUint32() { - return (_bigEndian) ? readUint32BE() : readUint32LE(); + uint32 readUint32() { + uint32 val; + read(&val, 4); + return (_bigEndian) ? TO_BE_32(val) : TO_LE_32(val); } - inline int16 readSint16() { + FORCEINLINE int16 readSint16() { return (int16)readUint16(); } - inline int32 readSint32() { + FORCEINLINE int32 readSint32() { return (int32)readUint32(); } }; @@ -642,13 +658,13 @@ private: byte *old_data = _data; _capacity = new_len + 32; - _data = new byte[_capacity]; + _data = (byte *)malloc(_capacity); _ptr = _data + _pos; if (old_data) { // Copy old data memcpy(_data, old_data, _size); - delete[] old_data; + free(old_data); } _size = new_len; @@ -658,7 +674,7 @@ public: ~MemoryWriteStreamDynamic() { if (_disposeMemory) - delete[] _data; + free(_data); } uint32 write(const void *dataPtr, uint32 dataSize) { diff --git a/common/system.h b/common/system.h index 479a1b10d74..21865e9a02a 100644 --- a/common/system.h +++ b/common/system.h @@ -125,7 +125,6 @@ public: * like PocketPC, Palms, Symbian phones like the P800, Zaurus, etc. */ kFeatureVirtualKeyboard, - kFeatureIconifyWindow, kFeatureOpenGL, /** @@ -174,7 +173,6 @@ public: * rather complicated for backend authors to fully understand and * implement the semantics of the OSystem interface. */ - //@{ /** @@ -323,8 +321,10 @@ public: * @param hotspotY vertical offset from the top side to the hotspot * @param keycolor transparency color index * @param cursorTargetScale scale factor which cursor is designed for + * @param format pointer to the pixel format which cursor graphic uses */ - virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int cursorTargetScale = 1) = 0; + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int cursorTargetScale = 1, const Graphics::PixelFormat *format = NULL) = 0; + //@} @@ -490,13 +490,11 @@ public: /** * Set a window caption or any other comparable status display to the - * given value. The caption must be a pure ASCII string. Passing a - * non-ASCII string may lead to unexpected behavior, even crashes. + * given value. The caption must be a pure ISO LATIN 1 string. Passing a + * string with a different encoding may lead to unexpected behavior, + * even crashes. * - * In a future revision of this API, this may be changed to allowing - * UTF-8 or UTF-16 encoded data, or maybe ISO LATIN 1. - * - * @param caption the window caption to use, as an ASCII string + * @param caption the window caption to use, as an ISO LATIN 1 string */ virtual void setWindowCaption(const char *caption) {} diff --git a/common/util.cpp b/common/util.cpp index 3b8aeb5995c..01ce4317466 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -24,6 +24,7 @@ #include "common/util.h" #include "common/system.h" +#include "common/config-manager.h" #include "gui/debugger.h" #include "engines/engine.h" @@ -211,6 +212,7 @@ const LanguageDescription g_languages[] = { {"ru", "Russian", RU_RUS}, {"es", "Spanish", ES_ESP}, {"se", "Swedish", SE_SWE}, + {"hu", "Hungarian", HU_HUN}, {0, 0, UNK_LANG} }; @@ -419,6 +421,14 @@ String getGameGUIOptionsDescription(uint32 options) { return res; } +void updateGameGUIOptions(const uint32 options) { + if ((options && !ConfMan.hasKey("guioptions")) || + (ConfMan.hasKey("guioptions") && options != parseGameGUIOptions(ConfMan.get("guioptions")))) { + ConfMan.set("guioptions", getGameGUIOptionsDescription(options)); + ConfMan.flushToDisk(); + } +} + } // End of namespace Common diff --git a/common/util.h b/common/util.h index ffe8238b502..593f31f15bb 100644 --- a/common/util.h +++ b/common/util.h @@ -26,7 +26,6 @@ #define COMMON_UTIL_H #include "common/sys.h" -#include "common/debug.h" #include "common/str.h" #if defined(WIN32) @@ -183,6 +182,7 @@ enum Language { RU_RUS, ES_ESP, SE_SWE, + HU_HUN, UNK_LANG = -1 // Use default language (i.e. none specified) }; @@ -294,6 +294,13 @@ bool checkGameGUIOption(GameGUIOption option, const String &str); uint32 parseGameGUIOptions(const String &str); String getGameGUIOptionsDescription(uint32 options); +/** + * Updates the GUI options of the current config manager + * domain, when they differ to the ones passed as + * parameter. + */ +void updateGameGUIOptions(const uint32 options); + } // End of namespace Common diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp index 8582e82f2c0..47a72cb780a 100644 --- a/common/xmlparser.cpp +++ b/common/xmlparser.cpp @@ -276,126 +276,126 @@ bool XMLParser::parse() { continue; switch (_state) { - case kParserNeedHeader: - case kParserNeedKey: - if (_char != '<') { - parserError("Parser expecting key start."); - break; - } - - if ((_char = _stream->readByte()) == 0) { - parserError("Unexpected end of file."); - break; - } - - if (_state == kParserNeedHeader) { - if (_char != '?') { - parserError("Expecting XML header."); - break; - } - - _char = _stream->readByte(); - activeHeader = true; - } else if (_char == '/') { - _char = _stream->readByte(); - activeClosure = true; - } else if (_char == '?') { - parserError("Unexpected header. There may only be one XML header per file."); - break; - } - - _state = kParserNeedKeyName; + case kParserNeedHeader: + case kParserNeedKey: + if (_char != '<') { + parserError("Parser expecting key start."); break; + } - case kParserNeedKeyName: - if (!parseToken()) { - parserError("Invalid key name."); - break; - } - - if (activeClosure) { - if (_activeKey.empty() || _token != _activeKey.top()->name) { - parserError("Unexpected closure."); - break; - } - } else { - ParserNode *node = allocNode(); //new ParserNode; - node->name = _token; - node->ignore = false; - node->header = activeHeader; - node->depth = _activeKey.size(); - node->layout = 0; - _activeKey.push(node); - } - - _state = kParserNeedPropertyName; + if ((_char = _stream->readByte()) == 0) { + parserError("Unexpected end of file."); break; + } - case kParserNeedPropertyName: - if (activeClosure) { - if (!closeKey()) { - parserError("Missing data when closing key '%s'.", _activeKey.top()->name.c_str()); - break; - } - - activeClosure = false; - - if (_char != '>') - parserError("Invalid syntax in key closure."); - else - _state = kParserNeedKey; - - _char = _stream->readByte(); + if (_state == kParserNeedHeader) { + if (_char != '?') { + parserError("Expecting XML header."); break; } - selfClosure = false; + _char = _stream->readByte(); + activeHeader = true; + } else if (_char == '/') { + _char = _stream->readByte(); + activeClosure = true; + } else if (_char == '?') { + parserError("Unexpected header. There may only be one XML header per file."); + break; + } - if (_char == '/' || (_char == '?' && activeHeader)) { - selfClosure = true; - _char = _stream->readByte(); + _state = kParserNeedKeyName; + break; + + case kParserNeedKeyName: + if (!parseToken()) { + parserError("Invalid key name."); + break; + } + + if (activeClosure) { + if (_activeKey.empty() || _token != _activeKey.top()->name) { + parserError("Unexpected closure."); + break; } + } else { + ParserNode *node = allocNode(); //new ParserNode; + node->name = _token; + node->ignore = false; + node->header = activeHeader; + node->depth = _activeKey.size(); + node->layout = 0; + _activeKey.push(node); + } - if (_char == '>') { - if (activeHeader && !selfClosure) { - parserError("XML Header must be self-closed."); - } else if (parseActiveKey(selfClosure)) { - _char = _stream->readByte(); - _state = kParserNeedKey; - } + _state = kParserNeedPropertyName; + break; - activeHeader = false; + case kParserNeedPropertyName: + if (activeClosure) { + if (!closeKey()) { + parserError("Missing data when closing key '%s'.", _activeKey.top()->name.c_str()); break; } - if (selfClosure) - parserError("Expecting key closure after '/' symbol."); - else if (!parseToken()) - parserError("Error when parsing key value."); + activeClosure = false; + + if (_char != '>') + parserError("Invalid syntax in key closure."); else - _state = kParserNeedPropertyOperator; - - break; - - case kParserNeedPropertyOperator: - if (_char != '=') - parserError("Syntax error after key name."); - else - _state = kParserNeedPropertyValue; + _state = kParserNeedKey; _char = _stream->readByte(); break; + } - case kParserNeedPropertyValue: - if (!parseKeyValue(_token)) - parserError("Invalid key value."); - else - _state = kParserNeedPropertyName; + selfClosure = false; + if (_char == '/' || (_char == '?' && activeHeader)) { + selfClosure = true; + _char = _stream->readByte(); + } + + if (_char == '>') { + if (activeHeader && !selfClosure) { + parserError("XML Header must be self-closed."); + } else if (parseActiveKey(selfClosure)) { + _char = _stream->readByte(); + _state = kParserNeedKey; + } + + activeHeader = false; break; + } - default: - break; + if (selfClosure) + parserError("Expecting key closure after '/' symbol."); + else if (!parseToken()) + parserError("Error when parsing key value."); + else + _state = kParserNeedPropertyOperator; + + break; + + case kParserNeedPropertyOperator: + if (_char != '=') + parserError("Syntax error after key name."); + else + _state = kParserNeedPropertyValue; + + _char = _stream->readByte(); + break; + + case kParserNeedPropertyValue: + if (!parseKeyValue(_token)) + parserError("Invalid key value."); + else + _state = kParserNeedPropertyName; + + break; + + default: + break; } } diff --git a/config.guess b/config.guess index da833146088..e792aac6080 100755 --- a/config.guess +++ b/config.guess @@ -1,10 +1,10 @@ #! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. -timestamp='2009-04-27' +timestamp='2009-09-18' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -27,16 +27,16 @@ timestamp='2009-04-27' # the same distribution terms that you use for the rest of that program. -# Originally written by Per Bothner . -# Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. +# Originally written by Per Bothner. Please send patches (context +# diff format) to and include a ChangeLog +# entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # -# The plan is that this can be called by configure scripts if you -# don't specify an explicit build system type. +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD me=`echo "$0" | sed -e 's,.*/,,'` @@ -170,7 +170,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep __ELF__ >/dev/null + | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? @@ -656,7 +656,7 @@ EOF # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | - grep __LP64__ >/dev/null + grep -q __LP64__ then HP_ARCH="hppa2.0w" else @@ -822,6 +822,9 @@ EOF [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we @@ -851,6 +854,20 @@ EOF i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ @@ -873,6 +890,9 @@ EOF frv:Linux:*:*) echo frv-unknown-linux-gnu exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-gnu + exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; @@ -882,40 +902,17 @@ EOF m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; - mips:Linux:*:*) + mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU - #undef mips - #undef mipsel + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mipsel + CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips - #else - CPU= - #endif - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^CPU/{ - s: ::g - p - }'`" - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } - ;; - mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef mips64 - #undef mips64el - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mips64el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips64 + CPU=${UNAME_MACHINE} #else CPU= #endif @@ -931,29 +928,12 @@ EOF or32:Linux:*:*) echo or32-unknown-linux-gnu exit ;; - ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu - exit ;; - ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu - exit ;; - alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} - exit ;; padre:Linux:*:*) echo sparc-unknown-linux-gnu exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in @@ -962,8 +942,11 @@ EOF *) echo hppa-unknown-linux-gnu ;; esac exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux @@ -986,66 +969,6 @@ EOF xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; - i*86:Linux:*:*) - # The BFD linker knows what the default object file format is, so - # first see if it will tell us. cd to the root directory to prevent - # problems with other programs or directories called `ld' in the path. - # Set LC_ALL=C to ensure ld outputs messages in English. - ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ - | sed -ne '/supported targets:/!d - s/[ ][ ]*/ /g - s/.*supported targets: *// - s/ .*// - p'` - case "$ld_supported_targets" in - elf32-i386) - TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" - ;; - a.out-i386-linux) - echo "${UNAME_MACHINE}-pc-linux-gnuaout" - exit ;; - "") - # Either a pre-BFD a.out linker (linux-gnuoldld) or - # one that does not give us useful --help. - echo "${UNAME_MACHINE}-pc-linux-gnuoldld" - exit ;; - esac - # Determine whether the default compiler is a.out or elf - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - #ifdef __ELF__ - # ifdef __GLIBC__ - # if __GLIBC__ >= 2 - LIBC=gnu - # else - LIBC=gnulibc1 - # endif - # else - LIBC=gnulibc1 - # endif - #else - #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) - LIBC=gnu - #else - LIBC=gnuaout - #endif - #endif - #ifdef __dietlibc__ - LIBC=dietlibc - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^LIBC/{ - s: ::g - p - }'`" - test x"${LIBC}" != x && { - echo "${UNAME_MACHINE}-pc-linux-${LIBC}" - exit - } - test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } - ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both @@ -1074,7 +997,7 @@ EOF i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) @@ -1182,7 +1105,7 @@ EOF rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) @@ -1275,6 +1198,16 @@ EOF *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in + i386) + eval $set_cc_for_build + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + UNAME_PROCESSOR="x86_64" + fi + fi ;; unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} diff --git a/config.sub b/config.sub index a39437d0158..8ca084bf334 100755 --- a/config.sub +++ b/config.sub @@ -1,10 +1,10 @@ #! /bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. -timestamp='2009-04-17' +timestamp='2009-08-19' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -32,13 +32,16 @@ timestamp='2009-04-17' # Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. +# diff and a properly formatted GNU ChangeLog entry. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. @@ -149,10 +152,13 @@ case $os in -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray) + -apple | -axis | -knuth | -cray | -microblaze) os= basic_machine=$1 ;; + -bluegene*) + os=-cnk + ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 @@ -337,7 +343,7 @@ case $basic_machine in | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ @@ -467,6 +473,10 @@ case $basic_machine in basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; c90) basic_machine=c90-cray os=-unicos @@ -719,6 +729,9 @@ case $basic_machine in basic_machine=ns32k-utek os=-sysv ;; + microblaze) + basic_machine=microblaze-xilinx + ;; mingw32) basic_machine=i386-pc os=-mingw32 @@ -1260,7 +1273,7 @@ case $os in # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ | -kopensolaris* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ @@ -1613,7 +1626,7 @@ case $basic_machine in -sunos*) vendor=sun ;; - -aix*) + -cnk*|-aix*) vendor=ibm ;; -beos*) diff --git a/configure b/configure index 9e21560bee2..8ab79c272a4 100755 --- a/configure +++ b/configure @@ -29,6 +29,8 @@ SAVED_LDFLAGS=$LDFLAGS SAVED_CXX=$CXX SAVED_CXXFLAGS=$CXXFLAGS SAVED_CPPFLAGS=$CPPFLAGS +SAVED_ASFLAGS=$ASFLAGS +SAVED_WINDRESFLAGS=$WINDRESFLAGS # Use environment vars if set CXXFLAGS="$CXXFLAGS $CPPFLAGS" @@ -54,15 +56,22 @@ if test "$ac_emxsupport" != "no" -a "$ac_emxsupport" != "NO"; then unset ac_TEMP_PATH fi +set_var() { + eval ${1}='${2}' +} + +get_var() { + eval echo \$${1} +} # Add an engine: id name build subengines add_engine() { _engines="${_engines} ${1}" - eval _engine_${1}_name='${2}' - eval _engine_${1}_build='${3}' - eval _engine_${1}_subengines='${4}' + set_var _engine_${1}_name "${2}" + set_var _engine_${1}_build "${3}" + set_var _engine_${1}_subengines "${4}" for sub in ${4}; do - eval _engine_${sub}_sub=yes + set_var _engine_${sub}_sub "yes" done } @@ -92,6 +101,7 @@ _plugins_default=static _ranlib=ranlib _strip=strip _ar="ar cru" +_as="as" _windres=windres _win32path="C:/residual" _aos4path="Games:Residual" @@ -107,6 +117,7 @@ _host_os="" _host_alias="" _srcdir=`dirname $0` +_port_mk="ports.mk" # Determine a tmp file name, using mktemp(1) when available. if type mktemp > /dev/null 2>&1 ; then @@ -225,7 +236,7 @@ find_sdlconfig() { # get_system_exe_extension() { case $1 in - mingw* | *os2-emx) + mingw* | *os2-emx | wince) _exeext=".exe" ;; arm-riscos) @@ -237,7 +248,7 @@ get_system_exe_extension() { gp2x-linux) _exeext=".gp2x" ;; - dreamcast | wii | gamecube | psp) + dreamcast | wii | gamecube | nds | psp) _exeext=".elf" ;; *) @@ -252,7 +263,7 @@ get_system_exe_extension() { # Show the configure help line for an option option_help() { - tmpopt=`echo $1 | sed 's/_/-/g'` + tmpopt=`echo $1 | sed 's/_/-/g'` option=`echo "--${tmpopt} " | sed "s/\(.\{23\}\).*/\1/"` echo " ${option} ${2}" } @@ -271,22 +282,22 @@ Try \`$0 --help' for more information." >&2 # Get the name of the engine get_engine_name() { - eval echo \$_engine_$1_name + get_var _engine_$1_name } # Will this engine be built? get_engine_build() { - eval echo \$_engine_$1_build + get_var _engine_$1_build } # Get the subengines get_engine_subengines() { - eval echo \$_engine_$1_subengines + get_var _engine_$1_subengines } # Ask if this is a subengine get_engine_sub() { - sub=`eval echo \\$_engine_$1_sub` + sub=`get_var _engine_$1_sub` if test -z "$sub" ; then sub=no fi @@ -296,14 +307,14 @@ get_engine_sub() { # Enable *all* engines engine_enable_all() { for engine in $_engines; do - eval _engine_${engine}_build=yes + set_var _engine_${engine}_build "yes" done } # Disable *all* engines engine_disable_all() { for engine in $_engines; do - eval _engine_${engine}_build=no + set_var _engine_${engine}_build "no" done } @@ -327,7 +338,7 @@ engine_enable() { if test "$opt" = "static" -o "$opt" = "dynamic" -o "$opt" = "yes" ; then if test "`get_engine_name ${engine}`" != "" ; then - eval _engine_${engine}_build=$opt + set_var _engine_${engine}_build "$opt" else option_error fi @@ -346,7 +357,7 @@ engine_disable() { engine=`echo $1 | sed 's/-/_/g'` if test "`get_engine_name ${engine}`" != "" ; then - eval _engine_${engine}_build=no + set_var _engine_${engine}_build "no" else option_error fi @@ -493,7 +504,7 @@ Usage: $0 [OPTIONS]... Configuration: -h, --help display this help and exit - --backend=BACKEND backend to build (sdl, morphos, dc, gp2x, gp2xwiz, iphone, wii, psp, null) [sdl] + --backend=BACKEND backend to build (sdl, dc, gp2x, gp2xwiz, iphone, morphos, nds, psp, wii, wince, linuxmoto, null) [sdl] Installation directories: --prefix=DIR use this prefix for installing Residual [/usr/local] @@ -508,20 +519,23 @@ Special configuration feature: dreamcast for Sega Dreamcast wii for Nintendo Wii gamecube for Nintendo Gamecube + nds for Nintendo DS iphone for Apple iPhone + wince for Windows CE psp for PlayStation Portable -Optional Features: - --disable-debug disable building with debugging symbols - --enable-Werror treat warnings as errors - --enable-profiling enable building with gprof profile information - --enable-release set flags to build release binary - --enable-verbose-build enable regular echoing of commands during build process +Game engines: --enable-all-engines enable all engines --disable-all-engines disable all engines $engines_help +Optional Features: + --disable-debug disable building with debugging symbols + --enable-Werror treat warnings as errors --enable-plugins enable the support for dynamic plugins --default-dynamic make plugins dynamic by default + --enable-profiling enable building with gprof profile information + --enable-release set flags to build release binary + --enable-verbose-build enable regular echoing of commands during build process Optional Libraries: --with-ogg-prefix=DIR Prefix where libogg is installed (optional) @@ -542,12 +556,14 @@ Optional Libraries: --with-sdl-prefix=DIR Prefix where the sdl-config script is installed (optional) Some influential environment variables: - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - CXX C++ compiler command - CXXFLAGS C++ compiler flags - CPPFLAGS C++ preprocessor flags, e.g. -I if you have - headers in a nonstandard directory + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CPPFLAGS C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + ASFLAGS assembler flags + WINDRESFLAGS Windows resource compiler flags EOF exit 0 @@ -566,8 +582,9 @@ for ac_option in $@; do --disable-flac) _flac=no ;; --enable-mad) _mad=yes ;; --disable-mad) _mad=no ;; - --enable-plugins) _dynamic_modules=yes ;; + --enable-verbose-build) _verbose_build=yes ;; + --enable-plugins) _dynamic_modules=yes ;; --default-dynamic) _plugins_default=dynamic ;; --enable-vkeybd) _vkeybd=yes ;; --disable-vkeybd) _vkeybd=no ;; @@ -676,6 +693,16 @@ linupy) _host_os=linux _host_cpu=arm ;; +motoezx) + _host_os=linux + _host_cpu=arm + _host_alias=arm-linux-gnu + ;; +motomagx) + _host_os=linux + _host_cpu=arm + _host_alias=arm-linux-gnueabi + ;; arm-riscos) _host_os=riscos _host_cpu=arm @@ -703,6 +730,11 @@ iphone) _host_cpu=arm _host_alias=arm-apple-darwin9 ;; +wince) + _host_os=wince + _host_cpu=arm + _host_alias=arm-wince-mingw32ce + ;; neuros) _host_os=linux _host_cpu=arm @@ -719,17 +751,22 @@ wii) _host_cpu=ppc _host_alias=powerpc-gekko ;; +gamecube) + _host_os=gamecube + _host_cpu=ppc + _host_alias=powerpc-gekko + ;; +nds) + _host_os=nds + _host_cpu=arm + _host_alias=arm-eabi + ;; psp) _host_os=psp _host_cpu=mipsallegrexel _host_alias=psp LDFLAGS="$LDFLAGS -L$PSPDEV/psp/sdk/lib -specs=$_srcdir/backends/platform/psp/psp.spec" ;; -gamecube) - _host_os=gamecube - _host_cpu=ppc - _host_alias=powerpc-gekko - ;; *) if test -n "$_host"; then guessed_host=`$_srcdir/config.sub $_host` @@ -742,6 +779,13 @@ esac if test -z "$_host_alias"; then _host_alias="$_host_cpu-$_host_os" +else + # if _host_alias was set, default to the standard GNU tools + _ranlib=$_host_alias-ranlib + _strip=$_host_alias-strip + _ar="$_host_alias-ar cru" + _as="$_host_alias-as" + _windres=$_host_alias-windres fi # @@ -766,7 +810,7 @@ esac # Platform specific sanity checks # case $_host_os in -wii | gamecube) +wii | gamecube | nds) if test -z "$DEVKITPRO"; then echo "Please set DEVKITPRO in your environment. export DEVKITPRO=" exit 1 @@ -836,37 +880,79 @@ fi # echocheck "compiler version" +have_gcc=no cxx_version=`( $CXX -dumpversion ) 2>&1` if test "$?" -gt 0; then - cxx_version="not found" + # TODO: Big scary warning about unsupported Compilers + cxx_version=`( $CXX -version ) 2>&1` + if test "$?" -eq 0; then + cxx_version="`echo "${cxx_version}" | sed -ne 's/^.*[^0-9]\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*$/\1/gp'`" + if test -z "${cxx_version}"; then + cxx_version="not found" + cxx_verc_fail=yes + fi + echo non-gcc compiler version ${cxx_version} + else + cxx_version="not found" + cxx_verc_fail=yes + echo found non-gcc compiler version ${cxx_version} + fi +else + add_line_to_config_mk 'HAVE_GCC = 1' + have_gcc=yes fi -case $cxx_version in - 2.95.[2-9]|2.95.[2-9][-.]*|3.[0-9]|3.[0-9].[0-9]|3.[0-9].[0-9][-.]*|4.[0-9]|4.[0-9].[0-9]|4.[0-9].[0-9][-.]*) - _cxx_major=`echo $cxx_version | cut -d '.' -f 1` - _cxx_minor=`echo $cxx_version | cut -d '.' -f 2` - cxx_version="$cxx_version, ok" - cxx_verc_fail=no - ;; - # whacky beos version strings - 2.9-beos-991026*|2.9-beos-000224*) - _cxx_major=2 - _cxx_minor=95 - cxx_version="$cxx_version, ok" - cxx_verc_fail=no - ;; - 3_4) - _cxx_major=3 - _cxx_minor=4 - ;; - 'not found') - cxx_verc_fail=yes - ;; - *) - cxx_version="$cxx_version, bad" - cxx_verc_fail=yes - ;; -esac +if test "$have_gcc" = yes; then + case $cxx_version in + 2.95.[2-9]|2.95.[2-9][-.]*|3.[0-9]|3.[0-9].[0-9]|3.[0-9].[0-9][-.]*|4.[0-9]|4.[0-9].[0-9]|4.[0-9].[0-9][-.]*) + _cxx_major=`echo $cxx_version | cut -d '.' -f 1` + _cxx_minor=`echo $cxx_version | cut -d '.' -f 2` + cxx_version="$cxx_version, ok" + cxx_verc_fail=no + ;; + # whacky beos version strings + 2.9-beos-991026*|2.9-beos-000224*) + _cxx_major=2 + _cxx_minor=95 + cxx_version="$cxx_version, ok" + cxx_verc_fail=no + ;; + 3_4) + _cxx_major=3 + _cxx_minor=4 + ;; + 'not found') + cxx_verc_fail=yes + ;; + *) + cxx_version="$cxx_version, bad" + cxx_verc_fail=yes + ;; + esac +else + case $_host_os in + irix*) + case $cxx_version in + 7.4.4*) + # We just assume this is SGI MipsPRO + _cxx_major=7 + _cxx_minor=4 + add_line_to_config_mk 'CXX_UPDATE_DEP_FLAG = -MDupdate "$(*D)/$(DEPDIR)/$(*F).d"' + add_line_to_config_mk '-include Makedepend' + ;; + *) + cxx_version="$cxx_version, bad" + cxx_verc_fail=yes + ;; + esac + ;; + *) + cxx_version="$cxx_version, bad" + cxx_verc_fail=yes + ;; + esac + +fi echo "$cxx_version" @@ -1040,7 +1126,7 @@ case $_host_os in ;; darwin*) DEFINES="$DEFINES -DUNIX -DMACOSX -DUSE_OPENGL" - LIBS="$LIBS -framework QuickTime -framework AudioUnit -framework AudioToolbox -framework Carbon -Wl,-framework,OpenGL \ + LIBS="$LIBS -framework QuickTime -framework AudioUnit -framework AudioToolbox -framework Carbon -framework OpenGL \ -dylib_file /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib:/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib" ;; mingw*) @@ -1073,17 +1159,24 @@ case $_host_os in wii) CXXFLAGS="$CXXFLAGS -Os -mrvl -mcpu=750 -meabi -mhard-float" CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fmodulo-sched" - CXXFLAGS="$CXXFLAGS -I$DEVKITPRO/libogc/include" - LDFLAGS="$LDFLAGS -mrvl -mcpu=750 -L$DEVKITPRO/libogc/lib/wii" + CXXFLAGS="$CXXFLAGS -I$DEVKITPRO/libogc/include -I$DEVKITPRO/wii/include" + LDFLAGS="$LDFLAGS -mrvl -mcpu=750 -L$DEVKITPRO/libogc/lib/wii -L$DEVKITPRO/wii/lib" ;; gamecube) CXXFLAGS="$CXXFLAGS -Os -mogc -mcpu=750 -meabi -mhard-float" CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fmodulo-sched" - CXXFLAGS="$CXXFLAGS -I$DEVKITPRO/libogc/include" - LDFLAGS="$LDFLAGS -mogc -mcpu=750 -L$DEVKITPRO/libogc/lib/cube" + CXXFLAGS="$CXXFLAGS -I$DEVKITPRO/libogc/include -I$DEVKITPRO/cube/include" + LDFLAGS="$LDFLAGS -mogc -mcpu=750 -L$DEVKITPRO/libogc/lib/cube -L$DEVKITPRO/cube/lib" + ;; + nds) + # TODO nds ;; psp) - CXXFLAGS="$CXXFLAGS -O2 -G0 -I$PSPDEV/psp/sdk/include -D_PSP_FW_VERSION=150" + CXXFLAGS="$CXXFLAGS -O3 -G0 -I$PSPDEV/psp/sdk/include -D_PSP_FW_VERSION=150" + ;; + wince) + CXXFLAGS="$CXXFLAGS -O3 -march=armv4 -mtune=xscale -D_WIN32_WCE=300 -D__ARM__ -D_ARM_ -DUNICODE -DFPM_DEFAULT -DNONSTANDARD_PORT" + CXXFLAGS="$CXXFLAGS -DWIN32 -Dcdecl= -D__cdecl__=" ;; # given this is a shell script assume some type of unix *) @@ -1098,16 +1191,6 @@ if test -n "$_host"; then linupy|arm-riscos) echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" DEFINES="$DEFINES -DUNIX" - _endian=little - _need_memalign=yes - add_line_to_config_h "#define LINUPY" - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' - ;; - arm-linux|arm*-linux-gnueabi|arm-*-linux|*-angstrom-linux) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" - DEFINES="$DEFINES -DUNIX -DUSE_ARM_SOUND_ASM" #not true for all ARM systems, but the interesting ones are all LE. Most (if not all) BE arm devices don't have a screen _endian=little _need_memalign=yes @@ -1115,6 +1198,44 @@ if test -n "$_host"; then type_2_byte='short' type_4_byte='int' add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' + add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + ;; + motoezx) + echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" + DEFINES="$DEFINES -DUNIX -DMOTOEZX" + #not true for all ARM systems, but the interesting ones are all LE. Most (if not all) BE arm devices don't have a screen + ASFLAGS="$ASFLAGS -mfpu=vfp" + _endian=little + _need_memalign=yes + type_1_byte='char' + type_2_byte='short' + type_4_byte='int' + add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' + add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + add_line_to_config_mk 'USE_ARM_GFX_ASM = 1' + add_line_to_config_mk 'USE_ARM_COSTUME_ASM = 1' + add_line_to_config_mk 'USE_ARM_SCALER_ASM = 1' + _backend="linuxmoto" + _build_hq_scalers="no" + _mt32emu="no" + _port_mk="backends/platform/linuxmoto/linuxmoto.mk" + ;; + motomagx) + echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" + DEFINES="$DEFINES -DUNIX -DMOTOMAGX" + #not true for all ARM systems, but the interesting ones are all LE. Most (if not all) BE arm devices don't have a screen + ASFLAGS="$ASFLAGS -mfpu=vfp" + _endian=little + _need_memalign=yes + type_1_byte='char' + type_2_byte='short' + type_4_byte='int' + add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' + add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + _backend="linuxmoto" + _build_hq_scalers="no" + _mt32emu="no" + _port_mk="backends/platform/linuxmoto/linuxmoto.mk" ;; bfin*) _need_memalign=yes @@ -1125,7 +1246,7 @@ if test -n "$_host"; then ;; gp2xwiz) echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" - DEFINES="$DEFINES -DUNIX -DGP2XWIZ -DNDEBUG -DUSE_ARM_SMUSH_ASM" + DEFINES="$DEFINES -DUNIX -DGP2XWIZ -DNDEBUG" CXXFLAGS="$CXXFLAGS -mcpu=arm926ej-s -mtune=arm926ej-s" LDFLAGS="$LDFLAGS" _endian=little @@ -1133,8 +1254,6 @@ if test -n "$_host"; then type_1_byte='char' type_2_byte='short' type_4_byte='int' - _ar="$_host_alias-ar cru" - _ranlib=$_host_alias-ranlib add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' _backend="gp2xwiz" @@ -1143,27 +1262,26 @@ if test -n "$_host"; then ;; gp2x) echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" - DEFINES="$DEFINES -DUNIX -DGP2X -DNDEBUG -DUSE_ARM_SMUSH_ASM" + DEFINES="$DEFINES -DUNIX -DGP2X -DNDEBUG" CXXFLAGS="$CXXFLAGS -march=armv4t" + ASFLAGS="$ASFLAGS -mfloat-abi=soft" LDFLAGS="$LDFLAGS -static" _endian=little _need_memalign=yes type_1_byte='char' type_2_byte='short' type_4_byte='int' - _ar="$_host_alias-ar cru" - _ranlib=$_host_alias-ranlib add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' + add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' _backend="gp2x" _build_hq_scalers="no" _mt32emu="no" ;; neuros) echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" - DEFINES="$DEFINES -DUNIX" + DEFINES="$DEFINES -DUNIX -DNEUROS" _endian=little _need_memalign=yes - add_line_to_config_h "#define NEUROS" type_1_byte='char' type_2_byte='short' type_4_byte='int' @@ -1198,34 +1316,46 @@ if test -n "$_host"; then _windres=$_host-windres _ar="$_host-ar cru" _ranlib=$_host-ranlib - _strip=$_host-strip ;; iphone) echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" - DEFINES="$DEFINES -DIPHONE -DUNIX -DUSE_ARM_SOUND_ASM" + DEFINES="$DEFINES -DIPHONE -DUNIX" _endian=little _need_memalign=yes type_1_byte='char' type_2_byte='short' type_4_byte='int' add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' + add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' _backend="iphone" - _ar="$_host_alias-ar cru" - _ranlib=$_host_alias-ranlib - _strip=$_host_alias-strip + ;; + wince) + echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" + LDFLAGS="$LDFLAGS -Wl,-Map,scummvm.exe.map -Wl,--stack,65536" + _endian=little + _need_memalign=yes + type_1_byte='char' + type_2_byte='short' + type_4_byte='int' + add_line_to_config_mk 'USE_TREMOLO = 1' + add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' + add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + _backend="wince" + _mt32emu="no" + _port_mk="backends/platform/wince/wince.mk" ;; dreamcast) echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" DEFINES="$DEFINES -DDISABLE_DEFAULT_SAVEFILEMANAGER -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE" - CXXFLAGS="$CXXFLAGS -O3 -Wno-multichar -funroll-loops -fschedule-insns2 -fomit-frame-pointer -fdelete-null-pointer-checks -fno-exceptions" + CXXFLAGS="$CXXFLAGS -O3 -funroll-loops -fschedule-insns2 -fomit-frame-pointer -fdelete-null-pointer-checks" _endian=little _need_memalign=yes type_1_byte='char' type_2_byte='short' type_4_byte='int' _backend="dc" - _ar="$_host_alias-ar cru" - _ranlib=$_host_alias-ranlib + _mad="yes" + _zlib="yes" add_line_to_config_mk 'include $(srcdir)/backends/platform/dc/dreamcast.mk' ;; wii) @@ -1235,16 +1365,14 @@ if test -n "$_host"; then type_1_byte='char' type_2_byte='short' type_4_byte='int' - _ar="$_host_alias-ar cru" - _ranlib=$_host_alias-ranlib - _strip=$_host_alias-strip _backend="wii" + _port_mk="backends/platform/wii/wii.mk" add_line_to_config_mk 'GAMECUBE = 0' - add_line_to_config_mk 'include $(srcdir)/backends/platform/wii/wii.mk' add_line_to_config_h "#define DEBUG_WII_USBGECKO" add_line_to_config_h "/* #define DEBUG_WII_MEMSTATS */" add_line_to_config_h "/* #define DEBUG_WII_GDB */" add_line_to_config_h "#define USE_WII_DI" + add_line_to_config_h "#define USE_WII_SMB" add_line_to_config_h "#define USE_WII_KBD" ;; gamecube) @@ -1254,17 +1382,36 @@ if test -n "$_host"; then type_1_byte='char' type_2_byte='short' type_4_byte='int' - _ar="$_host_alias-ar cru" - _ranlib=$_host_alias-ranlib - _strip=$_host_alias-strip _backend="wii" + _port_mk="backends/platform/wii/wii.mk" add_line_to_config_mk 'GAMECUBE = 1' - add_line_to_config_mk 'include $(srcdir)/backends/platform/wii/wii.mk' add_line_to_config_h '#define GAMECUBE' add_line_to_config_h "/* #define DEBUG_WII_USBGECKO */" add_line_to_config_h "/* #define DEBUG_WII_MEMSTATS */" add_line_to_config_h "/* #define DEBUG_WII_GDB */" ;; + nds) + echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" + # TODO: complete this + # TODO: Maybe rename nds -> ds (would be more consistent with other backends) + DEFINES="$DEFINES -D__DS__ -DNDS -DARM9 -DARM -DNONSTANDARD_PORT" + DEFINES="$DEFINES -DDISABLE_FANCY_THEMES -DVECTOR_RENDERER_FORMAT=1555" + DEFINES="$DEFINES -DDISABLE_DEFAULT_SAVEFILEMANAGER" + DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" + DEFINES="$DEFINES -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE" + _endian=little + _need_memalign=yes + type_1_byte='char' + type_2_byte='short' + type_4_byte='int' + _backend="nds" + _build_hq_scalers="no" + _mt32emu="no" + add_line_to_config_mk 'include $(srcdir)/backends/platform/ds/ds.mk' + # TODO: Enable more ARM optimizations -- requires testing! + add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' + add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + ;; psp) echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" _endian=little @@ -1272,10 +1419,8 @@ if test -n "$_host"; then type_1_byte='char' type_2_byte='short' type_4_byte='int' - _ar="$_host_alias-ar cru" - _ranlib=$_host_alias-ranlib _backend="psp" - add_line_to_config_mk 'include $(srcdir)/backends/platform/psp/psp.mk' + _port_mk="backends/platform/psp/psp.mk" ;; *) echo "Continuing with auto-detected values ... if you have problems, please add your target to configure." @@ -1667,6 +1812,12 @@ case $_backend in LIBS="$LIBS `$_sdlconfig --prefix="$_sdlpath" --libs`" DEFINES="$DEFINES -DSDL_BACKEND" ;; + linuxmoto) + find_sdlconfig + INCLUDES="$INCLUDES `$_sdlconfig --prefix="$_sdlpath" --cflags`" + LIBS="$LIBS `$_sdlconfig --prefix="$_sdlpath" --libs`" + DEFINES="$DEFINES -DSDL_BACKEND -DLINUXMOTO" + ;; gp2x) find_sdlconfig INCLUDES="$INCLUDES `$_sdlconfig --prefix="$_sdlpath" --cflags`" @@ -1685,6 +1836,10 @@ case $_backend in OBJCFLAGS="$OBJCFLAGS --std=c99" LIBS="$LIBS -lobjc -framework UIKit -framework CoreGraphics -framework OpenGLES -framework QuartzCore -framework GraphicsServices -framework CoreFoundation -framework Foundation -framework AudioToolbox -framework CoreAudio" ;; + wince) + INCLUDES="$INCLUDES "'-I$(srcdir) -I$(srcdir)/backends/platform/wince -I$(srcdir)/engines -I$(srcdir)/backends/platform/wince/missing/gcc -I$(srcdir)/backends/platform/wince/CEgui -I$(srcdir)/backends/platform/wince/CEkeys' + LIBS="$LIBS -static -lSDL" + ;; dc) INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/dc -isystem $(ronindir)/include' LDFLAGS="$LDFLAGS -Wl,-Ttext,0x8c010000 -nostartfiles "'$(ronindir)/lib/crt0.o -L$(ronindir)/lib' @@ -1694,15 +1849,18 @@ case $_backend in DEFINES="$DEFINES -D__WII__ -DGEKKO" case $_host_os in gamecube) - LIBS="$LIBS -lfat -logc -ldb" + LIBS="$LIBS -lgxflux -lfat -logc -ldb" ;; *) - LIBS="$LIBS -ldi -lfat -lwiiuse -lbte -logc -lwiikeyboard -ldb" + LIBS="$LIBS -lgxflux -ldi -ltinysmb -lfat -lwiiuse -lbte -logc -lwiikeyboard -ldb" ;; esac ;; + nds) + # TODO nds + ;; psp) - DEFINES="$DEFINES -D__PSP__ -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE" + DEFINES="$DEFINES -D__PSP__ -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DDISABLE_DOSBOX_OPL" INCLUDES="$INCLUDES -I$PSPDEV/psp/include/SDL" LIBS="$LIBS -lSDL" ;; @@ -1716,24 +1874,27 @@ MODULES="$MODULES backends/platform/$_backend" # # Do CXXFLAGS now we know the compiler version # -if test "$_cxx_major" -ge "3" ; then - case $_host_os in - # newlib-based system include files suppress non-C89 function - # declarations under __STRICT_ANSI__ - mingw* | dreamcast | wii | gamecube | psp | amigaos*) - CXXFLAGS="$CXXFLAGS -W -Wno-unused-parameter" - ;; - *) - CXXFLAGS="$CXXFLAGS -ansi -W -Wno-unused-parameter" - ;; - esac - add_line_to_config_mk 'HAVE_GCC3 = 1' -fi; +if test "$have_gcc" = yes ; then + if test "$_cxx_major" -ge "3" ; then + case $_host_os in + # newlib-based system include files suppress non-C89 function + # declarations under __STRICT_ANSI__ + mingw* | dreamcast | wii | gamecube | psp | wince | amigaos*) + CXXFLAGS="$CXXFLAGS -W -Wno-unused-parameter" + ;; + *) + CXXFLAGS="$CXXFLAGS -ansi -W -Wno-unused-parameter" + ;; + esac + add_line_to_config_mk 'HAVE_GCC3 = 1' + add_line_to_config_mk 'CXX_UPDATE_DEP_FLAG = -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP' + fi; -if test "$_cxx_major" -ge "4" && test "$_cxx_minor" -ge "3" ; then - CXXFLAGS="$CXXFLAGS -Wno-empty-body" -else - CXXFLAGS="$CXXFLAGS -Wconversion" + if test "$_cxx_major" -ge "4" && test "$_cxx_minor" -ge "3" ; then + CXXFLAGS="$CXXFLAGS -Wno-empty-body" + else + CXXFLAGS="$CXXFLAGS -Wconversion" + fi; fi; # Some platforms use certain GNU extensions in header files @@ -1761,12 +1922,12 @@ for engine in $_engines; do # If dynamic plugins aren't supported, mark # all the engines as static if test $_dynamic_modules = no ; then - eval _engine_${engine}_build=static + set_var _engine_${engine}_build "static" else # If it wasn't explicitly marked as static or # dynamic, use the configured default if test `get_engine_build $engine` = yes ; then - eval _engine_${engine}_build=${_plugins_default} + set_var _engine_${engine}_build "${_plugins_default}" fi fi @@ -1774,7 +1935,7 @@ for engine in $_engines; do if test `get_engine_build $engine` = dynamic ; then isbuilt=DYNAMIC_PLUGIN else - eval _engine_${engine}_build=static + set_var _engine_${engine}_build "static" isbuilt=STATIC_PLUGIN fi fi @@ -1871,7 +2032,10 @@ LIBS += $LIBS RANLIB := $_ranlib STRIP := $_strip AR := $_ar +AS := $_as +ASFLAGS := $ASFLAGS WINDRES := $_windres +WINDRESFLAGS := $WINDRESFLAGS WIN32PATH=$_win32path AOS4PATH=$_aos4path STATICLIBPATH=$_staticlibpath @@ -1896,11 +2060,15 @@ LDFLAGS += $LDFLAGS $_mak_plugins -SAVED_CONFIGFLAGS := $SAVED_CONFIGFLAGS -SAVED_LDFLAGS := $SAVED_LDFLAGS -SAVED_CXX := $SAVED_CXX -SAVED_CXXFLAGS := $SAVED_CXXFLAGS -SAVED_CPPFLAGS := $SAVED_CPPFLAGS +port_mk = $_port_mk + +SAVED_CONFIGFLAGS := $SAVED_CONFIGFLAGS +SAVED_LDFLAGS := $SAVED_LDFLAGS +SAVED_CXX := $SAVED_CXX +SAVED_CXXFLAGS := $SAVED_CXXFLAGS +SAVED_CPPFLAGS := $SAVED_CPPFLAGS +SAVED_ASFLAGS := $SAVED_ASFLAGS +SAVED_WINDRESFLAGS := $SAVED_WINDRESFLAGS EOF # diff --git a/dists/debian/control b/dists/debian/control index d867e427ad7..c66e6b14ebc 100644 --- a/dists/debian/control +++ b/dists/debian/control @@ -2,7 +2,7 @@ Source: residual Section: games Priority: optional Maintainer: Pino Toscano -Build-Depends: debhelper (>= 5.0.0), libgl1-mesa-dev, libsdl1.2-dev, libz-dev +Build-Depends: debhelper (>= 5.0.0), libgl1-mesa-dev, libsdl1.2-dev, libz-dev, libmad0-dev, libvorbis-dev (>= 1.0.0-2), libflac-dev (>= 1.1.1-2), libz-dev, libfluidsynth-dev Standards-Version: 3.8.0 Package: residual diff --git a/dists/debian/rules b/dists/debian/rules index d484707374e..54ff81c9146 100755 --- a/dists/debian/rules +++ b/dists/debian/rules @@ -4,7 +4,7 @@ build: residual residual: dh_testdir - ./configure --enable-release --prefix=/usr --bindir=/usr/games --datadir=/usr/share/games --mandir=/usr/share/man --disable-vorbis --disable-mad --disable-tremor --disable-flac + ./configure --enable-release --prefix=/usr --bindir=/usr/games --datadir=/usr/share/games --mandir=/usr/share/man $(MAKE) clean: diff --git a/dists/msvc8_to_msvc9.bat b/dists/msvc8_to_msvc9.bat index b617b45ebcc..1faacbd1a62 100644 --- a/dists/msvc8_to_msvc9.bat +++ b/dists/msvc8_to_msvc9.bat @@ -11,15 +11,12 @@ if not exist rpl.exe goto no_rpl echo Creating MSVC9 project files from the MSVC8 ones copy /y msvc8\*.vcproj msvc9\ copy /y msvc8\*.sln msvc9\ +copy /y msvc8\*.vsprops msvc9\ rpl -e -q "Version=\"8.00\"" "Version=\"9.00\"" msvc9\*.vcproj rpl -e -q "Version=\"8,00\"" "Version=\"9,00\"" msvc9\*.vcproj rpl -e -q "Keyword=\"Win32Proj\"" "Keyword=\"Win32Proj\"\n\tTargetFrameworkVersion=\"131072\"" msvc9\*.vcproj -rpl -e -q "EntryPointSymbol=\"WinMainCRTStartup\"" "EntryPointSymbol=\"WinMainCRTStartup\"\n\t\t\t\tRandomizedBaseAddress=\"1\"\n\t\t\t\tDataExecutionPrevention=\"0\"" msvc9\residual.vcproj -rpl -e -q "Format Version 9.00" "Format Version 10.00" msvc9\residual.sln -rpl -e -q "Format Version 9,00" "Format Version 10,00" msvc9\residual.sln -rpl -e -q "# Visual C++ Express 2005" "# Visual C++ Express 2008" msvc9\residual.sln -rpl -e -q "# Visual Studio 2005" "# Visual Studio 2008" msvc9\residual -.sln +rpl -e -q "Format Version 9.00" "Format Version 10.00" msvc9\scummvm.sln +rpl -e -q "Format Version 9,00" "Format Version 10,00" msvc9\scummvm.sln goto the_end :no_rpl diff --git a/dists/msvc9/residual.vcproj b/dists/msvc9/residual.vcproj index 2c78231e8b5..2f0b96c894d 100644 --- a/dists/msvc9/residual.vcproj +++ b/dists/msvc9/residual.vcproj @@ -240,6 +240,18 @@ RelativePath="..\..\common\error.h" > + + + + + + @@ -460,6 +472,14 @@ + + + + @@ -468,6 +488,14 @@ RelativePath="..\..\sound\audiostream.h" > + + + + @@ -480,6 +508,18 @@ RelativePath="..\..\sound\mixer_intern.h" > + + + + + + @@ -488,6 +528,14 @@ RelativePath="..\..\sound\rate.h" > + + + + guioptions | params.guioptions); debug(2, "Running %s", toGameDescriptor(*agdDesc, params.list).description().c_str()); - if (!createInstance(syst, engine, agdDesc)) { + if (!createInstance(syst, engine, agdDesc)) return Common::kNoGameDataFoundError; - } - return Common::kNoError; + else + return Common::kNoError; } struct SizeMD5 { diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp index 0a19d6b0659..b660172f6e5 100644 --- a/engines/dialogs.cpp +++ b/engines/dialogs.cpp @@ -29,6 +29,8 @@ #include "common/system.h" #include "common/events.h" +#include "graphics/scaler.h" + #include "gui/about.h" #include "gui/GuiManager.h" #include "gui/launcher.h" diff --git a/engines/engine.cpp b/engines/engine.cpp index 213ae550e8d..b19dab184ac 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -221,7 +221,10 @@ void Engine::syncSoundSettings() { } void Engine::flipMute() { - bool mute = false; + // Mute will be set to true by default here. This has two reasons: + // - if the game already has an "mute" config entry, it will be overwritten anyway. + // - if it does not have a "mute" config entry, the sound is unmuted currently and should be muted now. + bool mute = true; if (ConfMan.hasKey("mute")) { mute = !ConfMan.getBool("mute"); diff --git a/engines/engine.h b/engines/engine.h index 983556a25a8..11d66a81cf8 100644 --- a/engines/engine.h +++ b/engines/engine.h @@ -29,6 +29,7 @@ #include "common/error.h" #include "common/fs.h" #include "common/str.h" +#include "graphics/pixelformat.h" class OSystem; diff --git a/engines/grim/grim.cpp b/engines/grim/grim.cpp index 2103eda354b..0d9062dc162 100644 --- a/engines/grim/grim.cpp +++ b/engines/grim/grim.cpp @@ -24,6 +24,7 @@ */ #include "common/events.h" +#include "common/file.h" #include "common/config-manager.h" #include "engines/engine.h" @@ -352,9 +353,6 @@ Common::Error GrimEngine::run() { error("gfx backend doesn't support hardware rendering"); #endif - if (g_grim->getGameFlags() & GF_DEMO) - Common::File::addDefaultDirectory(_gameDataDir.getChild("Movies")); - g_driver->setupScreen(640, 480, fullscreen); Bitmap *splash_bm = NULL; diff --git a/graphics/VectorRenderer.cpp b/graphics/VectorRenderer.cpp index 97048802dc1..845b8466e6f 100644 --- a/graphics/VectorRenderer.cpp +++ b/graphics/VectorRenderer.cpp @@ -83,26 +83,26 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect & if (!step.autoWidth) { in_w = step.w == -1 ? area.height() : step.w; - switch(step.xAlign) { - case Graphics::DrawStep::kVectorAlignManual: - if (step.x >= 0) in_x = area.left + step.x; - else in_x = area.left + area.width() + step.x; // value relative to the opposite corner. - break; + switch (step.xAlign) { + case Graphics::DrawStep::kVectorAlignManual: + if (step.x >= 0) in_x = area.left + step.x; + else in_x = area.left + area.width() + step.x; // value relative to the opposite corner. + break; - case Graphics::DrawStep::kVectorAlignCenter: - in_x = area.left + (area.width() / 2) - (in_w / 2); - break; + case Graphics::DrawStep::kVectorAlignCenter: + in_x = area.left + (area.width() / 2) - (in_w / 2); + break; - case Graphics::DrawStep::kVectorAlignLeft: - in_x = area.left; - break; + case Graphics::DrawStep::kVectorAlignLeft: + in_x = area.left; + break; - case Graphics::DrawStep::kVectorAlignRight: - in_x = area.left + area.width() - in_w; - break; + case Graphics::DrawStep::kVectorAlignRight: + in_x = area.left + area.width() - in_w; + break; - default: - error("Vertical alignment in horizontal data."); + default: + error("Vertical alignment in horizontal data."); } } else { in_x = area.left; @@ -112,26 +112,26 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect & if (!step.autoHeight) { in_h = step.h == -1 ? area.width() : step.h; - switch(step.yAlign) { - case Graphics::DrawStep::kVectorAlignManual: - if (step.y >= 0) in_y = area.top + step.y; - else in_y = area.top + area.height() + step.y; // relative - break; + switch (step.yAlign) { + case Graphics::DrawStep::kVectorAlignManual: + if (step.y >= 0) in_y = area.top + step.y; + else in_y = area.top + area.height() + step.y; // relative + break; - case Graphics::DrawStep::kVectorAlignCenter: - in_y = area.top + (area.height() / 2) - (in_h / 2); - break; + case Graphics::DrawStep::kVectorAlignCenter: + in_y = area.top + (area.height() / 2) - (in_h / 2); + break; - case Graphics::DrawStep::kVectorAlignTop: - in_y = area.top; - break; + case Graphics::DrawStep::kVectorAlignTop: + in_y = area.top; + break; - case Graphics::DrawStep::kVectorAlignBottom: - in_y = area.top + area.height() - in_h; - break; + case Graphics::DrawStep::kVectorAlignBottom: + in_y = area.top + area.height() - in_h; + break; - default: - error("Horizontal alignment in vertical data."); + default: + error("Horizontal alignment in vertical data."); } } else { in_y = area.top; diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index da17e222e03..45a43b7dbcb 100644 --- a/graphics/VectorRenderer.h +++ b/graphics/VectorRenderer.h @@ -36,6 +36,10 @@ namespace Graphics { class VectorRenderer; + +typedef void (VectorRenderer::*DrawingFunctionCallback)(const Common::Rect &, const Graphics::DrawStep &); + + struct DrawStep { struct Color { uint8 r, g, b; @@ -58,7 +62,10 @@ struct DrawStep { kVectorAlignBottom, kVectorAlignTop, kVectorAlignCenter - } xAlign, yAlign; + }; + + VectorAlignment xAlign; + VectorAlignment yAlign; uint8 shadow, stroke, factor, radius, bevel; /**< Misc options... */ @@ -67,7 +74,7 @@ struct DrawStep { uint32 scale; /**< scale of all the coordinates in FIXED POINT with 16 bits mantissa */ - void (VectorRenderer::*drawingCall)(const Common::Rect &, const DrawStep &); /** Pointer to drawing function */ + DrawingFunctionCallback drawingCall; /**< Pointer to drawing function */ Graphics::Surface *blitSrc; }; diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp index 1251e24ebbc..f3bb1d62e72 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -365,22 +365,24 @@ applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) { uint8 r, g, b; uint lum; - const uint32 shiftMask = (uint32)~( - (1 << _format.rShift) | (1 << _format.gShift) | (1 << _format.bShift) | (_format.aLoss == 8 ? 0 : (1 << _format.aShift))) >> 1; + // Mask to clear the last bit of every color component and all unused bits + const uint32 colorMask = ~((1 << _format.rShift) | (1 << _format.gShift) | (1 << _format.bShift) // R/G/B components + | (_format.aLoss == 8 ? 0 : (1 << _format.aShift)) // Alpha component + | ~(_alphaMask | _redMask | _greenMask | _blueMask)); // All unused bits if (shadingStyle == GUI::ThemeEngine::kShadingDim) { int n = (pixels + 7) >> 3; switch (pixels % 8) { case 0: do { - *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 7: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 6: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 5: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 4: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 3: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 2: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 1: *ptr = (*ptr >> 1) & shiftMask; ++ptr; + *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 7: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 6: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 5: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 4: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 3: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 2: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 1: *ptr = (*ptr & colorMask) >> 1; ++ptr; } while (--n > 0); } @@ -420,10 +422,10 @@ calcGradient(uint32 pos, uint32 max) { PixelType output = 0; pos = (MIN(pos * Base::_gradientFactor, max) << 12) / max; - output |= ((_gradientStart&_redMask) + ((Base::_gradientBytes[0] * pos) >> 12)) & _redMask; - output |= ((_gradientStart&_greenMask) + ((Base::_gradientBytes[1] * pos) >> 12)) & _greenMask; - output |= ((_gradientStart&_blueMask) + ((Base::_gradientBytes[2] * pos) >> 12)) & _blueMask; - output |= ~(_redMask | _greenMask | _blueMask); + output |= ((_gradientStart & _redMask) + ((Base::_gradientBytes[0] * pos) >> 12)) & _redMask; + output |= ((_gradientStart & _greenMask) + ((Base::_gradientBytes[1] * pos) >> 12)) & _greenMask; + output |= ((_gradientStart & _blueMask) + ((Base::_gradientBytes[2] * pos) >> 12)) & _blueMask; + output |= _alphaMask; return output; } @@ -599,12 +601,12 @@ drawRoundedSquare(int x, int y, int r, int w, int h) { w <= 0 || h <= 0 || x < 0 || y < 0 || r <= 0) return; - if ((r << 1) > w || (r << 1) > h) - r = MIN(w >> 1, h >> 1); + if ((r * 2) > w || (r * 2) > h) + r = MIN(w /2, h / 2); if (Base::_fillMode != kFillDisabled && Base::_shadowOffset - && x + w + Base::_shadowOffset < Base::_activeSurface->w - && y + h + Base::_shadowOffset < Base::_activeSurface->h) { + && x + w + Base::_shadowOffset + 1 < Base::_activeSurface->w + && y + h + Base::_shadowOffset + 1 < Base::_activeSurface->h) { drawRoundedSquareShadow(x, y, r, w, h, Base::_shadowOffset); } @@ -697,7 +699,7 @@ drawTriangle(int x, int y, int w, int h, TriangleOrientation orient) { int newW = w / 2; if (newW % 2) newW++; - switch(orient) { + switch (orient) { case kTriangleUp: case kTriangleDown: drawTriangleFast(x + (newW / 2), y + (h / 2) - (newW / 2), newW, (orient == kTriangleDown), color, Base::_fillMode); @@ -918,10 +920,12 @@ drawBevelSquareAlg(int x, int y, int w, int h, int bevel, PixelType top_color, P } int i, j; + x = MAX(x - bevel, 0); y = MAX(y - bevel, 0); - h += bevel << 1; - w += bevel << 1; + + w = MIN(w + (bevel * 2), (int)_activeSurface->w); + h = MIN(h + (bevel * 2), (int)_activeSurface->h); PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x, y); @@ -1023,7 +1027,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color int dysub = ddy - (dx * 2); int error_term = ddy - dx; - switch(fill_m) { + switch (fill_m) { case kFillDisabled: while (dx--) { __TRIANGLE_MAINX(); @@ -1055,7 +1059,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color int dxsub = ddx - (dy * 2); int error_term = ddx - dy; - switch(fill_m) { + switch (fill_m) { case kFillDisabled: while (dy--) { __TRIANGLE_MAINY(); diff --git a/graphics/colormasks.h b/graphics/colormasks.h index aad52237315..a6754445719 100644 --- a/graphics/colormasks.h +++ b/graphics/colormasks.h @@ -237,6 +237,33 @@ struct ColorMasks<8888> { }; }; +#ifdef __WII__ +/* Gamecube/Wii specific ColorMask ARGB3444 */ +template<> +struct ColorMasks<3444> { + enum { + kBytesPerPixel = 2, + + kAlphaBits = 3, + kRedBits = 4, + kGreenBits = 4, + kBlueBits = 4, + + kBlueShift = 0, + kGreenShift = kBlueBits, + kRedShift = kGreenBits+kBlueBits, + kAlphaShift = kGreenBits+kBlueBits+kRedBits, + + kAlphaMask = ((1 << kAlphaBits) - 1) << kAlphaShift, + kRedMask = ((1 << kRedBits) - 1) << kRedShift, + kGreenMask = ((1 << kGreenBits) - 1) << kGreenShift, + kBlueMask = ((1 << kBlueBits) - 1) << kBlueShift, + + kRedBlueMask = kRedMask | kBlueMask + }; +}; +#endif + template uint32 RGBToColor(uint8 r, uint8 g, uint8 b) { return T::kAlphaMask | diff --git a/graphics/cursorman.cpp b/graphics/cursorman.cpp index 9a034c2d6f4..a128a1c9996 100644 --- a/graphics/cursorman.cpp +++ b/graphics/cursorman.cpp @@ -57,14 +57,14 @@ bool CursorManager::showMouse(bool visible) { return g_system->showMouse(visible); } -void CursorManager::pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor, int targetScale) { - Cursor *cur = new Cursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale); +void CursorManager::pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale, const Graphics::PixelFormat *format) { + Cursor *cur = new Cursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale, format); cur->_visible = isVisible(); _cursorStack.push(cur); if (buf) { - g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale); + g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale, format); } } @@ -77,7 +77,7 @@ void CursorManager::popCursor() { if (!_cursorStack.empty()) { cur = _cursorStack.top(); - g_system->setMouseCursor(cur->_data, cur->_width, cur->_height, cur->_hotspotX, cur->_hotspotY, cur->_keycolor, cur->_targetScale); + g_system->setMouseCursor(cur->_data, cur->_width, cur->_height, cur->_hotspotX, cur->_hotspotY, cur->_keycolor, cur->_targetScale, &cur->_format); } g_system->showMouse(isVisible()); @@ -93,15 +93,24 @@ void CursorManager::popAllCursors() { g_system->showMouse(isVisible()); } +void CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale, const Graphics::PixelFormat *format) { -void CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor, int targetScale) { if (_cursorStack.empty()) { - pushCursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale); + pushCursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale, format); return; } Cursor *cur = _cursorStack.top(); + +#ifdef USE_RGB_COLOR + uint size; + if (!format) + size = w * h; + else + size = w * h * format->bytesPerPixel; +#else uint size = w * h; +#endif if (cur->_size < size) { delete[] cur->_data; @@ -118,8 +127,14 @@ void CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX, cur->_hotspotY = hotspotY; cur->_keycolor = keycolor; cur->_targetScale = targetScale; +#ifdef USE_RGB_COLOR + if (format) + cur->_format = *format; + else + cur->_format = Graphics::PixelFormat::createFormatCLUT8(); +#endif - g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale); + g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale, format); } bool CursorManager::supportsCursorPalettes() { diff --git a/graphics/cursorman.h b/graphics/cursorman.h index cf29c9b5f91..b835e895009 100644 --- a/graphics/cursorman.h +++ b/graphics/cursorman.h @@ -28,6 +28,10 @@ #include "common/sys.h" #include "common/stack.h" #include "common/singleton.h" +#include "graphics/pixelformat.h" +#ifdef USE_RGB_COLOR +#include "common/system.h" +#endif namespace Graphics { @@ -36,7 +40,21 @@ public: /** Query whether the mouse cursor is visible. */ bool isVisible(); - /** Show or hide the mouse cursor. */ + /** + * Show or hide the mouse cursor. + * + * This function does not call OSystem::updateScreen, when visible is true. + * This fact might result in a non visible mouse cursor if the caller does + * not call OSystem::updateScreen itself after a showMouse(true) call. + * + * TODO: We might want to reconsider this behavior, it might be confusing + * for the user to call OSystem::updateScreen separately, on the other + * hand OSystem::updateScreen might as well display unwanted changes on + * the screen. Another alternative would be to let the backend worry + * about this on OSystem::showMouse call. + * + * @see OSystem::showMouse. + */ bool showMouse(bool visible); /** @@ -45,18 +63,19 @@ public: * safely freed afterwards. * * @param buf the new cursor data - * @param w the width - * @param h the height + * @param w the width + * @param h the height * @param hotspotX the hotspot X coordinate * @param hotspotY the hotspot Y coordinate * @param keycolor the index for the transparent color * @param targetScale the scale for which the cursor is designed - * + * @param format a pointer to the pixel format which the cursor graphic uses, + * CLUT8 will be used if this is NULL or not specified. * @note It is ok for the buffer to be a NULL pointer. It is sometimes * useful to push a "dummy" cursor and modify it later. The * cursor will be added to the stack, but not to the backend. */ - void pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int targetScale = 1); + void pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, const Graphics::PixelFormat *format = NULL); /** * Pop a cursor from the stack, and restore the previous one to the @@ -76,8 +95,10 @@ public: * @param hotspotY the hotspot Y coordinate * @param keycolor the index for the transparent color * @param targetScale the scale for which the cursor is designed + * @param format a pointer to the pixel format which the cursor graphic uses, + * CLUT8 will be used if this is NULL or not specified. */ - void replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int targetScale = 1); + void replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, const Graphics::PixelFormat *format = NULL); /** * Pop all of the cursors and cursor palettes from their respective stacks. @@ -152,13 +173,24 @@ private: uint _height; int _hotspotX; int _hotspotY; - byte _keycolor; + uint32 _keycolor; + Graphics::PixelFormat _format; byte _targetScale; uint _size; - - Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int targetScale = 1) { + Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, const Graphics::PixelFormat *format = NULL) { +#ifdef USE_RGB_COLOR + if (!format) + _format = Graphics::PixelFormat::createFormatCLUT8(); + else + _format = *format; + _size = w * h * _format.bytesPerPixel; + _keycolor = keycolor & ((1 << (_format.bytesPerPixel << 3)) - 1); +#else + _format = Graphics::PixelFormat::createFormatCLUT8(); _size = w * h; + _keycolor = keycolor & 0xFF; +#endif _data = new byte[_size]; if (data && _data) memcpy(_data, data, _size); @@ -166,7 +198,6 @@ private: _height = h; _hotspotX = hotspotX; _hotspotY = hotspotY; - _keycolor = keycolor; _targetScale = targetScale; } @@ -202,7 +233,6 @@ private: delete[] _data; } }; - Common::Stack _cursorStack; Common::Stack _cursorPaletteStack; }; diff --git a/graphics/font.cpp b/graphics/font.cpp index 5c7ec454ed5..adfe9e91e71 100644 --- a/graphics/font.cpp +++ b/graphics/font.cpp @@ -572,8 +572,10 @@ bitmap_t bdf_hexval(unsigned char *buf) { NewFont *NewFont::loadFont(Common::SeekableReadStream &stream) { NewFontData *data = bdf_read_font(stream); - if (!data) + if (!data || stream.err()) { + free_font(data); return 0; + } FontDesc desc; desc.name = data->name; @@ -673,7 +675,7 @@ NewFont *NewFont::loadFromCache(Common::SeekableReadStream &stream) { data->defaultchar = stream.readUint16BE(); data->bits_size = stream.readUint32BE(); - data->bits = (bitmap_t*)malloc(sizeof(bitmap_t)*data->bits_size); + data->bits = (bitmap_t *)malloc(sizeof(bitmap_t) * data->bits_size); if (!data->bits) { free(data); return 0; @@ -685,7 +687,7 @@ NewFont *NewFont::loadFromCache(Common::SeekableReadStream &stream) { bool hasOffsetTable = (stream.readByte() != 0); if (hasOffsetTable) { - data->offset = (unsigned long*)malloc(sizeof(unsigned long)*data->size); + data->offset = (unsigned long *)malloc(sizeof(unsigned long) * data->size); if (!data->offset) { free(data->bits); free(data); @@ -699,7 +701,7 @@ NewFont *NewFont::loadFromCache(Common::SeekableReadStream &stream) { bool hasWidthTable = (stream.readByte() != 0); if (hasWidthTable) { - data->width = (unsigned char*)malloc(sizeof(unsigned char)*data->size); + data->width = (unsigned char *)malloc(sizeof(unsigned char) * data->size); if (!data->width) { free(data->bits); free(data->offset); @@ -714,7 +716,7 @@ NewFont *NewFont::loadFromCache(Common::SeekableReadStream &stream) { bool hasBBXTable = (stream.readByte() != 0); if (hasBBXTable) { - data->bbx = (BBX *)malloc(sizeof(BBX)*data->size); + data->bbx = (BBX *)malloc(sizeof(BBX) * data->size); if (!data->bbx) { free(data->bits); free(data->offset); @@ -731,6 +733,14 @@ NewFont *NewFont::loadFromCache(Common::SeekableReadStream &stream) { } } + if (stream.err() || stream.eos()) { + free(data->bits); + free(data->offset); + free(data->width); + free(data); + return 0; + } + FontDesc desc; desc.name = data->name; desc.maxwidth = data->maxwidth; @@ -750,7 +760,7 @@ NewFont *NewFont::loadFromCache(Common::SeekableReadStream &stream) { desc.bits_size = data->bits_size; font = new NewFont(desc, data); - if (!font || stream.ioFailed()) { + if (!font) { free(data->bits); free(data->offset); free(data->width); diff --git a/graphics/pixelformat.h b/graphics/pixelformat.h index d1209a684d3..9b6727a8dfd 100644 --- a/graphics/pixelformat.h +++ b/graphics/pixelformat.h @@ -27,6 +27,7 @@ #define GRAPHICS_PIXELFORMAT_H #include "common/sys.h" +#include "common/list.h" namespace Graphics { @@ -50,6 +51,24 @@ struct PixelFormat { byte rLoss, gLoss, bLoss, aLoss; /**< Precision loss of each color component. */ byte rShift, gShift, bShift, aShift; /**< Binary left shift of each color component in the pixel value. */ + inline PixelFormat() { + bytesPerPixel = + rLoss = gLoss = bLoss = aLoss = + rShift = gShift = bShift = aShift = 0; + } + + inline PixelFormat(byte BytesPerPixel, + byte RBits, byte GBits, byte BBits, byte ABits, + byte RShift, byte GShift, byte BShift, byte AShift) { + bytesPerPixel = BytesPerPixel; + rLoss = 8 - RBits, gLoss = 8 - GBits, bLoss = 8 - BBits, aLoss = 8 - ABits; + rShift = RShift, gShift = GShift, bShift = BShift, aShift = AShift; + } + + static inline PixelFormat createFormatCLUT8() { + return PixelFormat(1, 0, 0, 0, 0, 0, 0, 0, 0); + } + inline bool operator==(const PixelFormat &fmt) const { // TODO: If aLoss==8, then the value of aShift is irrelevant, and should be ignored. return 0 == memcmp(this, &fmt, sizeof(PixelFormat)); @@ -129,6 +148,26 @@ struct PixelFormat { } }; +/** + * Determines the first matching format between two lists. + * + * @param backend The higher priority list, meant to be a list of formats supported by the backend + * @param frontend The lower priority list, meant to be a list of formats supported by the engine + * @return The first item on the backend list that also occurs on the frontend list + * or PixelFormat::createFormatCLUT8() if no matching formats were found. + */ +inline PixelFormat findCompatibleFormat(Common::List backend, Common::List frontend) { +#ifdef USE_RGB_COLOR + for (Common::List::iterator i = backend.begin(); i != backend.end(); ++i) { + for (Common::List::iterator j = frontend.begin(); j != frontend.end(); ++j) { + if (*i == *j) + return *i; + } + } +#endif + return PixelFormat::createFormatCLUT8(); +} + } // end of namespace Graphics #endif diff --git a/gui/EditTextWidget.cpp b/gui/EditTextWidget.cpp index 7a57e5da20d..f86b69f9105 100644 --- a/gui/EditTextWidget.cpp +++ b/gui/EditTextWidget.cpp @@ -84,7 +84,7 @@ void EditTextWidget::drawWidget() { // Draw the text adjustOffset(); - g_gui.theme()->drawText(Common::Rect(_x+2+ _leftPadding,_y+2, _x+_leftPadding+getEditRect().width()+2, _y+_h-2), _editString, _state, Graphics::kTextAlignLeft, false, -_editScrollOffset, false, _font); + g_gui.theme()->drawText(Common::Rect(_x+2+ _leftPadding,_y+2, _x+_leftPadding+getEditRect().width()+2, _y+_h-2), _editString, _state, Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, -_editScrollOffset, false, _font); } Common::Rect EditTextWidget::getEditRect() const { diff --git a/gui/GuiManager.cpp b/gui/GuiManager.cpp index 7c029e16b66..7fc017c585c 100644 --- a/gui/GuiManager.cpp +++ b/gui/GuiManager.cpp @@ -72,7 +72,6 @@ GuiManager::GuiManager() : _redrawStatus(kRedrawDisabled), error("Failed to load any GUI theme, aborting"); } } - _themeChange = false; } GuiManager::~GuiManager() { @@ -143,12 +142,20 @@ bool GuiManager::loadNewTheme(Common::String id, ThemeEngine::GraphicsMode gfx) // Enable the new theme // _theme = newTheme; - _themeChange = true; + _useStdCursor = !_theme->ownCursor(); + + // If _stateIsSaved is set, we know that a Theme is already initialized, + // thus we initialize the new theme properly + if (_stateIsSaved) { + _theme->enable(); + + if (_useStdCursor) + setupCursor(); + } // refresh all dialogs - for (int i = 0; i < _dialogStack.size(); ++i) { + for (int i = 0; i < _dialogStack.size(); ++i) _dialogStack[i]->reflowLayout(); - } // We need to redraw immediately. Otherwise // some other event may cause a widget to be @@ -157,10 +164,6 @@ bool GuiManager::loadNewTheme(Common::String id, ThemeEngine::GraphicsMode gfx) redraw(); _system->updateScreen(); - Common::Event event; - event.type = Common::EVENT_SCREEN_CHANGED; - _system->getEventManager()->pushEvent(event); - return true; } @@ -233,7 +236,6 @@ void GuiManager::runLoop() { // _theme->refresh(); - _themeChange = false; _redrawStatus = kRedrawFull; redraw(); } @@ -285,21 +287,6 @@ void GuiManager::runLoop() { Common::Point mouse(event.mouse.x - activeDialog->_x, event.mouse.y - activeDialog->_y); - // HACK to change the cursor to the new themes one - if (_themeChange) { - _theme->enable(); - - _useStdCursor = !_theme->ownCursor(); - if (_useStdCursor) - setupCursor(); - -// _theme->refresh(); - - _themeChange = false; - _redrawStatus = kRedrawFull; - redraw(); - } - if (lastRedraw + waitTime < _system->getMillis()) { _theme->updateScreen(); _system->updateScreen(); diff --git a/gui/GuiManager.h b/gui/GuiManager.h index 494f6ab8cf8..22323136fab 100644 --- a/gui/GuiManager.h +++ b/gui/GuiManager.h @@ -120,9 +120,7 @@ protected: // mouse cursor state int _cursorAnimateCounter; int _cursorAnimateTimer; - byte _cursor[2048]; - - bool _themeChange; + byte _cursor[2048]; void initKeymap(); diff --git a/gui/ListWidget.cpp b/gui/ListWidget.cpp index cce78a43119..c5c15226426 100644 --- a/gui/ListWidget.cpp +++ b/gui/ListWidget.cpp @@ -24,6 +24,8 @@ #include "common/system.h" #include "common/events.h" +#include "common/frac.h" + #include "gui/ListWidget.h" #include "gui/ScrollBarWidget.h" #include "gui/dialog.h" @@ -62,6 +64,7 @@ ListWidget::ListWidget(GuiObject *boss, const String &name, uint32 cmd) _editable = true; _quickSelect = true; + _editColor = ThemeEngine::kFontColorNormal; } ListWidget::ListWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd) @@ -105,6 +108,22 @@ Widget *ListWidget::findWidget(int x, int y) { } void ListWidget::setSelected(int item) { + // HACK/FIXME: If our _listIndex has a non zero size, + // we will need to look up, whether the user selected + // item is present in that list + if (_listIndex.size()) { + int filteredItem = -1; + + for (uint i = 0; i < _listIndex.size(); ++i) { + if (_listIndex[i] == item) { + filteredItem = i; + break; + } + } + + item = filteredItem; + } + assert(item >= -1 && item < (int)_list.size()); // We only have to do something if the widget is enabled and the selection actually changes @@ -123,7 +142,17 @@ void ListWidget::setSelected(int item) { } } -void ListWidget::setList(const StringList &list) { +ThemeEngine::FontColor ListWidget::getSelectionColor() const { + if (_listColors.empty()) + return ThemeEngine::kFontColorNormal; + + if (_filter.empty()) + return _listColors[_selectedItem]; + else + return _listColors[_listIndex[_selectedItem]]; +} + +void ListWidget::setList(const StringList &list, const ColorList *colors) { if (_editMode && _caretVisible) drawCaret(true); @@ -131,6 +160,13 @@ void ListWidget::setList(const StringList &list) { _dataList = list; _list = list; _filter.clear(); + _listIndex.clear(); + _listColors.clear(); + + if (colors) { + _listColors = *colors; + assert(_listColors.size() == _dataList.size()); + } int size = list.size(); if (_currentPos >= size) @@ -143,7 +179,19 @@ void ListWidget::setList(const StringList &list) { scrollBarRecalc(); } -void ListWidget::append(const String &s) { +void ListWidget::append(const String &s, ThemeEngine::FontColor color) { + if (_dataList.size() == _listColors.size()) { + // If the color list has the size of the data list, we append the color. + _listColors.push_back(color); + } else if (!_listColors.size() && color != ThemeEngine::kFontColorNormal) { + // If it's the first entry to use a non default color, we will fill + // up all other entries of the color list with the default color and + // add the requested color for the new entry. + for (uint i = 0; i < _dataList.size(); ++i) + _listColors.push_back(ThemeEngine::kFontColorNormal); + _listColors.push_back(color); + } + _dataList.push_back(s); _list.push_back(s); @@ -356,8 +404,13 @@ bool ListWidget::handleKeyUp(Common::KeyState state) { return true; } +void ListWidget::receivedFocusWidget() { + // Redraw the widget so the selection color will change + draw(); +} + void ListWidget::lostFocusWidget() { - // If we loose focus, we simply forget the user changes + // If we lose focus, we simply forget the user changes _editMode = false; g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); drawCaret(true); @@ -387,15 +440,14 @@ void ListWidget::drawWidget() { for (i = 0, pos = _currentPos; i < _entriesPerPage && pos < len; i++, pos++) { const int y = _y + _topPadding + kLineHeight * i; const int fontHeight = kLineHeight; - bool inverted = false; + ThemeEngine::TextInversionState inverted = ThemeEngine::kTextInversionNone; // Draw the selected item inverted, on a highlighted background. if (_selectedItem == pos) { if (_hasFocus) - inverted = true; + inverted = ThemeEngine::kTextInversionFocus; else - g_gui.theme()->drawWidgetBackground(Common::Rect(_x, y - 1, _x + _w - 1, y + fontHeight - 1), - 0, ThemeEngine::kWidgetBackgroundBorderSmall); + inverted = ThemeEngine::kTextInversion; } Common::Rect r(getEditRect()); @@ -413,17 +465,27 @@ void ListWidget::drawWidget() { int width; + ThemeEngine::FontColor color = ThemeEngine::kFontColorNormal; + + if (!_listColors.empty()) { + if (_filter.empty() || _selectedItem == -1) + color = _listColors[pos]; + else + color = _listColors[_listIndex[pos]]; + } + if (_selectedItem == pos && _editMode) { buffer = _editString; + color = _editColor; adjustOffset(); width = _w - r.left - _hlRightPadding - _leftPadding - scrollbarW; - g_gui.theme()->drawText(Common::Rect(_x + r.left, y, _x + r.left + width, y + fontHeight - 2), - buffer, _state, Graphics::kTextAlignLeft, inverted, pad, true); + g_gui.theme()->drawText(Common::Rect(_x + r.left, y, _x + r.left + width, y + fontHeight - 2), buffer, _state, + Graphics::kTextAlignLeft, inverted, pad, true, ThemeEngine::kFontStyleBold, color); } else { buffer = _list[pos]; width = _w - r.left - scrollbarW; - g_gui.theme()->drawText(Common::Rect(_x + r.left, y, _x + r.left + width, y + fontHeight - 2), - buffer, _state, Graphics::kTextAlignLeft, inverted, pad, true); + g_gui.theme()->drawText(Common::Rect(_x + r.left, y, _x + r.left + width, y + fontHeight - 2), buffer, _state, + Graphics::kTextAlignLeft, inverted, pad, true, ThemeEngine::kFontStyleBold, color); } _textWidth[i] = width; @@ -481,6 +543,14 @@ void ListWidget::startEditMode() { if (_editable && !_editMode && _selectedItem >= 0) { _editMode = true; setEditString(_list[_selectedItem]); + if (_listColors.empty()) { + _editColor = ThemeEngine::kFontColorNormal; + } else { + if (_filter.empty()) + _editColor = _listColors[_selectedItem]; + else + _editColor = _listColors[_listIndex[_selectedItem]]; + } draw(); g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true); } @@ -522,13 +592,15 @@ void ListWidget::reflowLayout() { // of the list. // We do a rough rounding on the decimal places of Entries Per Page, // to add another entry even if it goes a tad over the padding. - _entriesPerPage = ((_h - _topPadding - _bottomPadding) << 16) / kLineHeight; + frac_t entriesPerPage = intToFrac(_h - _topPadding - _bottomPadding) / kLineHeight; - if ((uint)(_entriesPerPage & 0xFFFF) >= 0xF000) - _entriesPerPage += (1 << 16); + // Our threshold before we add another entry is 0.9375 (0xF000 with FRAC_BITS being 16). + const frac_t threshold = intToFrac(15) / 16; - _entriesPerPage >>= 16; + if ((frac_t)(entriesPerPage & FRAC_LO_MASK) >= threshold) + entriesPerPage += FRAC_ONE; + _entriesPerPage = fracToInt(entriesPerPage); assert(_entriesPerPage > 0); delete[] _textWidth; @@ -560,9 +632,12 @@ void ListWidget::setFilter(const String &filter, bool redraw) { if (_filter.empty()) { // No filter -> display everything _list = _dataList; + _listIndex.clear(); } else { - // Restrict the list to everything which contains _filter as a substring, - // ignoring case. + // Restrict the list to everything which contains all words in _filter + // as substrings, ignoring case. + + Common::StringTokenizer tok(_filter); String tmp; int n = 0; @@ -572,7 +647,16 @@ void ListWidget::setFilter(const String &filter, bool redraw) { for (StringList::iterator i = _dataList.begin(); i != _dataList.end(); ++i, ++n) { tmp = *i; tmp.toLowercase(); - if (tmp.contains(_filter)) { + bool matches = true; + tok.reset(); + while (!tok.empty()) { + if (!tmp.contains(tok.nextToken())) { + matches = false; + break; + } + } + + if (matches) { _list.push_back(*i); _listIndex.push_back(n); } diff --git a/gui/ListWidget.h b/gui/ListWidget.h index f4d576c40e0..603efafe047 100644 --- a/gui/ListWidget.h +++ b/gui/ListWidget.h @@ -28,6 +28,8 @@ #include "gui/editable.h" #include "common/str.h" +#include "gui/ThemeEngine.h" + namespace GUI { class ScrollBarWidget; @@ -51,9 +53,11 @@ class ListWidget : public EditableWidget { public: typedef Common::String String; typedef Common::StringList StringList; + typedef Common::Array ColorList; protected: StringList _list; StringList _dataList; + ColorList _listColors; Common::Array _listIndex; bool _editable; bool _editMode; @@ -80,6 +84,8 @@ protected: uint32 _cmd; + ThemeEngine::FontColor _editColor; + public: ListWidget(GuiObject *boss, const String &name, uint32 cmd = 0); ListWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd = 0); @@ -87,20 +93,33 @@ public: virtual Widget *findWidget(int x, int y); - void setList(const StringList &list); - void append(const String &s); + void setList(const StringList &list, const ColorList *colors = 0); const StringList &getList() const { return _dataList; } - int getSelected() const { return (_filter.empty() || _selectedItem == -1) ? _selectedItem : _listIndex[_selectedItem]; } + + void append(const String &s, ThemeEngine::FontColor color = ThemeEngine::kFontColorNormal); + void setSelected(int item); + int getSelected() const { return (_filter.empty() || _selectedItem == -1) ? _selectedItem : _listIndex[_selectedItem]; } + const String &getSelectedString() const { return _list[_selectedItem]; } + ThemeEngine::FontColor getSelectionColor() const; + void setNumberingMode(NumberingMode numberingMode) { _numberingMode = numberingMode; } - bool isEditable() const { return _editable; } - void setEditable(bool editable) { _editable = editable; } + void scrollTo(int item); void scrollToEnd(); + void enableQuickSelect(bool enable) { _quickSelect = enable; } String getQuickSelectString() const { return _quickSelectStr; } + bool isEditable() const { return _editable; } + void setEditable(bool editable) { _editable = editable; } + void setEditColor(ThemeEngine::FontColor color) { _editColor = color; } + + // Made startEditMode/endEditMode for SaveLoadChooser + void startEditMode(); + void endEditMode(); + void setFilter(const String &filter, bool redraw = true); virtual void handleTickle(); @@ -115,10 +134,6 @@ public: virtual bool wantsFocus() { return true; } - // Made startEditMode for SCUMM's SaveLoadChooser - void startEditMode(); - void endEditMode(); - protected: void drawWidget(); @@ -130,6 +145,7 @@ protected: Common::Rect getEditRect() const; + void receivedFocusWidget(); void lostFocusWidget(); void scrollToCurrent(); diff --git a/gui/PopUpWidget.cpp b/gui/PopUpWidget.cpp index ebe4ee5e48a..675331e2afe 100644 --- a/gui/PopUpWidget.cpp +++ b/gui/PopUpWidget.cpp @@ -345,7 +345,7 @@ void PopUpDialog::drawMenuEntry(int entry, bool hilite) { g_gui.theme()->drawLineSeparator(Common::Rect(x, y, x+w, y+kLineHeight)); } else { g_gui.theme()->drawText(Common::Rect(x+1, y+2, x+w, y+2+kLineHeight), name, hilite ? ThemeEngine::kStateHighlight : ThemeEngine::kStateEnabled, - Graphics::kTextAlignLeft, false, _leftPadding); + Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, _leftPadding); } } diff --git a/gui/ScrollBarWidget.cpp b/gui/ScrollBarWidget.cpp index 327d8f4fb32..741db9d43db 100644 --- a/gui/ScrollBarWidget.cpp +++ b/gui/ScrollBarWidget.cpp @@ -63,9 +63,9 @@ void ScrollBarWidget::handleMouseDown(int x, int y, int button, int clickCount) _currentPos++; _draggingPart = kDownArrowPart; } else if (y < _sliderPos) { - _currentPos -= _entriesPerPage; + _currentPos -= _entriesPerPage - 1; } else if (y >= _sliderPos + _sliderHeight) { - _currentPos += _entriesPerPage; + _currentPos += _entriesPerPage - 1; } else { _draggingPart = kSliderPart; _sliderDeltaMouseDownPos = y - _sliderPos; diff --git a/gui/TabWidget.cpp b/gui/TabWidget.cpp index 9a0005de173..ae1c74816ca 100644 --- a/gui/TabWidget.cpp +++ b/gui/TabWidget.cpp @@ -91,7 +91,37 @@ int TabWidget::addTab(const String &title) { int numTabs = _tabs.size(); + // HACK: Nintendo DS uses a custom config dialog. This dialog does not work with + // our default "Globals.TabWidget.Tab.Width" setting. + // + // TODO: Add proper handling in the theme layout for such cases. + // + // There are different solutions to this problem: + // - offer a "Tab.Width" setting per tab widget and thus let the Ninteno DS + // backend set a default value for its special dialog. + // + // - change our themes to use auto width calculaction by default + // + // - change "Globals.TabWidget.Tab.Width" to be the minimal tab width setting and + // rename it accordingly. + // Actually this solution is pretty similar to our HACK for the Nintendo DS + // backend. This hack enables auto width calculation by default with the + // "Globals.TabWidget.Tab.Width" value as minimal width for the tab buttons. + // + // - we might also consider letting every tab button having its own width. + // + // - other solutions you can think of, which are hopefully less evil ;-). + // + // Of course also the Ninteno DS' dialog should be in our layouting engine, instead + // of being hard coded like it is right now. + // + // There are checks for __DS__ all over this source file to take care of the + // aforemnetioned problem. +#ifdef __DS__ + if (true) { +#else if (g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Width") == 0) { +#endif if (_tabWidth == 0) _tabWidth = 40; // Determine the new tab width @@ -217,6 +247,10 @@ void TabWidget::reflowLayout() { if (_tabWidth == 0) { _tabWidth = 40; +#ifdef __DS__ + } + if (true) { +#endif int maxWidth = _w / _tabs.size(); for (uint i = 0; i < _tabs.size(); ++i) { diff --git a/gui/TabWidget.h b/gui/TabWidget.h index 78878aeecc3..8606441782a 100644 --- a/gui/TabWidget.h +++ b/gui/TabWidget.h @@ -76,6 +76,10 @@ public: */ void removeTab(int tabID); + int getActiveTab() { + return _activeTab; + } + /** * Set the active tab by specifying a valid tab ID. * setActiveTab changes the value of _firstWidget. This means new diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index 533fbb44a04..2d0ce0acf1f 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -56,10 +56,10 @@ const char * const ThemeEngine::kImageSearch = "search.bmp"; struct TextDrawData { const Graphics::Font *_fontPtr; +}; - struct { - uint8 r, g, b; - } _color; +struct TextColorData { + int r, g, b; }; struct WidgetDrawData { @@ -67,6 +67,7 @@ struct WidgetDrawData { Common::List _steps; TextData _textDataId; + TextColor _textColorId; Graphics::TextAlign _textAlignH; GUI::ThemeEngine::TextAlignVertical _textAlignV; @@ -116,16 +117,17 @@ protected: class ThemeItemTextData : public ThemeItem { public: - ThemeItemTextData(ThemeEngine *engine, const TextDrawData *data, const Common::Rect &area, const Common::String &text, + ThemeItemTextData(ThemeEngine *engine, const TextDrawData *data, const TextColorData *color, const Common::Rect &area, const Common::String &text, Graphics::TextAlign alignH, GUI::ThemeEngine::TextAlignVertical alignV, bool ellipsis, bool restoreBg, int deltaX) : - ThemeItem(engine, area), _data(data), _text(text), _alignH(alignH), _alignV(alignV), + ThemeItem(engine, area), _data(data), _color(color), _text(text), _alignH(alignH), _alignV(alignV), _ellipsis(ellipsis), _restoreBg(restoreBg), _deltax(deltaX) {} void drawSelf(bool draw, bool restore); protected: const TextDrawData *_data; + const TextColorData *_color; Common::String _text; Graphics::TextAlign _alignH; GUI::ThemeEngine::TextAlignVertical _alignV; @@ -167,6 +169,7 @@ static const DrawDataInfo kDrawDataDefaults[] = { {kDDPlainColorBackground, "plain_bg", true, kDDNone}, {kDDDefaultBackground, "default_bg", true, kDDNone}, {kDDTextSelectionBackground, "text_selection", false, kDDNone}, + {kDDTextSelectionFocusBackground, "text_selection_focus", false, kDDNone}, {kDDWidgetBackgroundDefault, "widget_default", true, kDDNone}, {kDDWidgetBackgroundSmall, "widget_small", true, kDDNone}, @@ -179,7 +182,7 @@ static const DrawDataInfo kDrawDataDefaults[] = { {kDDSliderFull, "slider_full", false, kDDNone}, {kDDSliderHover, "slider_hover", false, kDDNone}, - {kDDSliderDisabled, "slider_disabled", true, kDDNone}, + {kDDSliderDisabled, "slider_disabled", false, kDDNone}, {kDDCheckboxDefault, "checkbox_default", true, kDDNone}, {kDDCheckboxDisabled, "checkbox_disabled", true, kDDNone}, @@ -231,7 +234,7 @@ void ThemeItemTextData::drawSelf(bool draw, bool restore) { _engine->restoreBackground(_area); if (draw) { - _engine->renderer()->setFgColor(_data->_color.r, _data->_color.g, _data->_color.b); + _engine->renderer()->setFgColor(_color->r, _color->g, _color->b); _engine->renderer()->drawString(_data->_fontPtr, _text, _area, _alignH, _alignV, _deltax, _ellipsis); } @@ -276,6 +279,10 @@ ThemeEngine::ThemeEngine(Common::String id, GraphicsMode mode) : _texts[i] = 0; } + for (int i = 0; i < kTextColorMAX; ++i) { + _textColors[i] = 0; + } + // We currently allow two different ways of theme selection in our config file: // 1) Via full path // 2) Via a basename, which will need to be translated into a full path @@ -531,20 +538,21 @@ void ThemeEngine::addDrawStep(const Common::String &drawDataId, const Graphics:: _widgets[id]->_steps.push_back(step); } -bool ThemeEngine::addTextData(const Common::String &drawDataId, TextData textId, Graphics::TextAlign alignH, TextAlignVertical alignV) { +bool ThemeEngine::addTextData(const Common::String &drawDataId, TextData textId, TextColor colorId, Graphics::TextAlign alignH, TextAlignVertical alignV) { DrawData id = parseDrawDataId(drawDataId); - if (id == -1 || textId == -1 || !_widgets[id]) + if (id == -1 || textId == -1 || colorId == kTextColorMAX || !_widgets[id]) return false; _widgets[id]->_textDataId = textId; + _widgets[id]->_textColorId = colorId; _widgets[id]->_textAlignH = alignH; _widgets[id]->_textAlignV = alignV; return true; } -bool ThemeEngine::addFont(TextData textId, const Common::String &file, int r, int g, int b) { +bool ThemeEngine::addFont(TextData textId, const Common::String &file) { if (textId == -1) return false; @@ -568,13 +576,26 @@ bool ThemeEngine::addFont(TextData textId, const Common::String &file, int r, in } } - _texts[textId]->_color.r = r; - _texts[textId]->_color.g = g; - _texts[textId]->_color.b = b; return true; } +bool ThemeEngine::addTextColor(TextColor colorId, int r, int g, int b) { + if (colorId >= kTextColorMAX) + return false; + + if (_textColors[colorId] != 0) + delete _textColors[colorId]; + + _textColors[colorId] = new TextColorData; + + _textColors[colorId]->r = r; + _textColors[colorId]->g = g; + _textColors[colorId]->b = b; + + return true; +} + bool ThemeEngine::addBitmap(const Common::String &filename) { // Nothing has to be done if the bitmap already has been loaded. Graphics::Surface *surf = _bitmaps[filename]; @@ -655,6 +676,11 @@ void ThemeEngine::unloadTheme() { _texts[i] = 0; } + for (int i = 0; i < kTextColorMAX; ++i) { + delete _textColors[i]; + _textColors[i] = 0; + } + _themeEval->reset(); _themeOk = false; } @@ -770,7 +796,7 @@ void ThemeEngine::queueDD(DrawData type, const Common::Rect &r, uint32 dynamic, } } -void ThemeEngine::queueDDText(TextData type, const Common::Rect &r, const Common::String &text, bool restoreBg, +void ThemeEngine::queueDDText(TextData type, TextColor color, const Common::Rect &r, const Common::String &text, bool restoreBg, bool ellipsis, Graphics::TextAlign alignH, TextAlignVertical alignV, int deltax) { if (_texts[type] == 0) @@ -779,7 +805,7 @@ void ThemeEngine::queueDDText(TextData type, const Common::Rect &r, const Common Common::Rect area = r; area.clip(_screen.w, _screen.h); - ThemeItemTextData *q = new ThemeItemTextData(this, _texts[type], area, text, alignH, alignV, ellipsis, restoreBg, deltax); + ThemeItemTextData *q = new ThemeItemTextData(this, _texts[type], _textColors[color], area, text, alignH, alignV, ellipsis, restoreBg, deltax); if (_buffering) { _screenQueue.push_back(q); @@ -823,7 +849,7 @@ void ThemeEngine::drawButton(const Common::Rect &r, const Common::String &str, W dd = kDDButtonDisabled; queueDD(dd, r, 0, hints & WIDGET_CLEARBG); - queueDDText(getTextData(dd), r, str, false, false, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV); + queueDDText(getTextData(dd), getTextColor(dd), r, str, false, false, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV); } void ThemeEngine::drawLineSeparator(const Common::Rect &r, WidgetStateInfo state) { @@ -846,7 +872,6 @@ void ThemeEngine::drawCheckbox(const Common::Rect &r, const Common::String &str, if (state == kStateDisabled) dd = kDDCheckboxDisabled; - TextData td = (state == kStateHighlight) ? kTextDataHover : getTextData(dd); const int checkBoxSize = MIN((int)r.height(), getFontHeight()); r2.bottom = r2.top + checkBoxSize; @@ -857,7 +882,7 @@ void ThemeEngine::drawCheckbox(const Common::Rect &r, const Common::String &str, r2.left = r2.right + checkBoxSize; r2.right = r.right; - queueDDText(td, r2, str, false, false, _widgets[kDDCheckboxDefault]->_textAlignH, _widgets[dd]->_textAlignV); + queueDDText(getTextData(dd), getTextColor(dd), r2, str, false, false, _widgets[kDDCheckboxDefault]->_textAlignH, _widgets[dd]->_textAlignV); } void ThemeEngine::drawSlider(const Common::Rect &r, int width, WidgetStateInfo state) { @@ -911,21 +936,21 @@ void ThemeEngine::drawDialogBackground(const Common::Rect &r, DialogBackground b return; switch (bgtype) { - case kDialogBackgroundMain: - queueDD(kDDMainDialogBackground, r); - break; + case kDialogBackgroundMain: + queueDD(kDDMainDialogBackground, r); + break; - case kDialogBackgroundSpecial: - queueDD(kDDSpecialColorBackground, r); - break; + case kDialogBackgroundSpecial: + queueDD(kDDSpecialColorBackground, r); + break; - case kDialogBackgroundPlain: - queueDD(kDDPlainColorBackground, r); - break; + case kDialogBackgroundPlain: + queueDD(kDDPlainColorBackground, r); + break; - case kDialogBackgroundDefault: - queueDD(kDDDefaultBackground, r); - break; + case kDialogBackgroundDefault: + queueDD(kDDDefaultBackground, r); + break; } } @@ -957,7 +982,7 @@ void ThemeEngine::drawPopUpWidget(const Common::Rect &r, const Common::String &s if (!sel.empty()) { Common::Rect text(r.left + 3, r.top + 1, r.right - 10, r.bottom); - queueDDText(getTextData(dd), text, sel, true, false, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV, deltax); + queueDDText(getTextData(dd), getTextColor(dd), text, sel, true, false, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV, deltax); } } @@ -995,74 +1020,114 @@ void ThemeEngine::drawTab(const Common::Rect &r, int tabHeight, int tabWidth, co if (!ready()) return; - const int tabOffset = 2; - tabWidth -= tabOffset; - queueDD(kDDTabBackground, Common::Rect(r.left, r.top, r.right, r.top + tabHeight)); for (int i = 0; i < (int)tabs.size(); ++i) { if (i == active) continue; - Common::Rect tabRect(r.left + i * (tabWidth + tabOffset), r.top, r.left + i * (tabWidth + tabOffset) + tabWidth, r.top + tabHeight); + Common::Rect tabRect(r.left + i * tabWidth, r.top, r.left + (i + 1) * tabWidth, r.top + tabHeight); queueDD(kDDTabInactive, tabRect); - queueDDText(getTextData(kDDTabInactive), tabRect, tabs[i], false, false, _widgets[kDDTabInactive]->_textAlignH, _widgets[kDDTabInactive]->_textAlignV); + queueDDText(getTextData(kDDTabInactive), getTextColor(kDDTabInactive), tabRect, tabs[i], false, false, _widgets[kDDTabInactive]->_textAlignH, _widgets[kDDTabInactive]->_textAlignV); } if (active >= 0) { - Common::Rect tabRect(r.left + active * (tabWidth + tabOffset), r.top, r.left + active * (tabWidth + tabOffset) + tabWidth, r.top + tabHeight); - const uint16 tabLeft = active * (tabWidth + tabOffset); + Common::Rect tabRect(r.left + active * tabWidth, r.top, r.left + (active + 1) * tabWidth, r.top + tabHeight); + const uint16 tabLeft = active * tabWidth; const uint16 tabRight = MAX(r.right - tabRect.right, 0); queueDD(kDDTabActive, tabRect, (tabLeft << 16) | (tabRight & 0xFFFF)); - queueDDText(getTextData(kDDTabActive), tabRect, tabs[active], false, false, _widgets[kDDTabActive]->_textAlignH, _widgets[kDDTabActive]->_textAlignV); + queueDDText(getTextData(kDDTabActive), getTextColor(kDDTabActive), tabRect, tabs[active], false, false, _widgets[kDDTabActive]->_textAlignH, _widgets[kDDTabActive]->_textAlignV); } } -void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, WidgetStateInfo state, Graphics::TextAlign align, bool inverted, int deltax, bool useEllipsis, FontStyle font) { +void ThemeEngine::drawText(const Common::Rect &r, const Common::String &str, WidgetStateInfo state, Graphics::TextAlign align, TextInversionState inverted, int deltax, bool useEllipsis, FontStyle font, FontColor color) { if (!ready()) return; - if (inverted) { - queueDD(kDDTextSelectionBackground, r); - queueDDText(kTextDataInverted, r, str, false, useEllipsis, align, kTextAlignVCenter, deltax); + TextColor colorId = kTextColorMAX; + + switch (color) { + case kFontColorNormal: + if (inverted) { + colorId = kTextColorNormalInverted; + } else { + switch (state) { + case kStateDisabled: + colorId = kTextColorNormalDisabled; + break; + + case kStateHighlight: + colorId = kTextColorNormalHover; + break; + + case kStateEnabled: + colorId = kTextColorNormal; + break; + } + } + break; + + case kFontColorAlternate: + if (inverted) { + colorId = kTextColorAlternativeInverted; + } else { + switch (state) { + case kStateDisabled: + colorId = kTextColorAlternativeDisabled; + break; + + case kStateHighlight: + colorId = kTextColorAlternativeHover; + break; + + case kStateEnabled: + colorId = kTextColorAlternative; + break; + } + } + break; + + default: return; } - switch (font) { - case kFontStyleNormal: - queueDDText(kTextDataNormalFont, r, str, true, useEllipsis, align, kTextAlignVCenter, deltax); - return; + TextData textId = kTextDataNone; + if (font == kFontStyleNormal) + textId = kTextDataNormalFont; + else + textId = kTextDataDefault; - default: - break; + bool restore = true; + + switch (inverted) { + case kTextInversion: + queueDD(kDDTextSelectionBackground, r); + restore = false; + break; + + case kTextInversionFocus: + queueDD(kDDTextSelectionFocusBackground, r); + restore = false; + break; + + default: + break; } - switch (state) { - case kStateDisabled: - queueDDText(kTextDataDisabled, r, str, true, useEllipsis, align, kTextAlignVCenter, deltax); - return; - - case kStateHighlight: - queueDDText(kTextDataHover, r, str, true, useEllipsis, align, kTextAlignVCenter, deltax); - return; - - case kStateEnabled: - queueDDText(kTextDataDefault, r, str, true, useEllipsis, align, kTextAlignVCenter, deltax); - return; - } + queueDDText(textId, colorId, r, str, restore, useEllipsis, align, kTextAlignVCenter, deltax); } -void ThemeEngine::drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, WidgetStateInfo state) { +void ThemeEngine::drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, WidgetStateInfo state, FontColor color) { if (!ready()) return; Common::Rect charArea = r; charArea.clip(_screen.w, _screen.h); - uint32 color = _overlayFormat.RGBToColor(_texts[kTextDataDefault]->_color.r, _texts[kTextDataDefault]->_color.g, _texts[kTextDataDefault]->_color.b); + uint32 rgbColor = _overlayFormat.RGBToColor(_textColors[color]->r, _textColors[color]->g, _textColors[color]->b); restoreBackground(charArea); - font->drawChar(&_screen, ch, charArea.left, charArea.top, color); + font->drawChar(&_screen, ch, charArea.left, charArea.top, rgbColor); addDirtyRect(charArea); } @@ -1161,6 +1226,78 @@ void ThemeEngine::openDialog(bool doBuffer, ShadingStyle style) { } bool ThemeEngine::createCursor(const Common::String &filename, int hotspotX, int hotspotY, int scale) { + return true; // Residual doesn's support cursor palette mode + + // Try to locate the specified file among all loaded bitmaps + const Graphics::Surface *cursor = _bitmaps[filename]; + if (!cursor) + return false; + +#ifdef USE_RGB_COLOR + _cursorFormat.bytesPerPixel = 1; + _cursorFormat.rLoss = _cursorFormat.gLoss = _cursorFormat.bLoss = _cursorFormat.aLoss = 8; + _cursorFormat.rShift = _cursorFormat.gShift = _cursorFormat.bShift = _cursorFormat.aShift = 0; +#endif + + // Set up the cursor parameters + _cursorHotspotX = hotspotX; + _cursorHotspotY = hotspotY; + _cursorTargetScale = scale; + + _cursorWidth = cursor->w; + _cursorHeight = cursor->h; + + // Allocate a new buffer for the cursor + delete[] _cursor; + _cursor = new byte[_cursorWidth * _cursorHeight]; + assert(_cursor); + memset(_cursor, 0xFF, sizeof(byte) * _cursorWidth * _cursorHeight); + + // the transparent color is 0xFF00FF + const int colTransparent = _overlayFormat.RGBToColor(0xFF, 0, 0xFF); + + // Now, scan the bitmap. We have to convert it from 16 bit color mode + // to 8 bit mode, and have to create a suitable palette on the fly. + uint colorsFound = 0; + Common::HashMap colorToIndex; + const OverlayColor *src = (const OverlayColor*)cursor->pixels; + for (uint y = 0; y < _cursorHeight; ++y) { + for (uint x = 0; x < _cursorWidth; ++x) { + byte r, g, b; + + // Skip transparency + if (src[x] == colTransparent) + continue; + + _overlayFormat.colorToRGB(src[x], r, g, b); + const int col = (r << 16) | (g << 8) | b; + + // If there is no entry yet for this color in the palette: Add one + if (!colorToIndex.contains(col)) { + const int index = colorsFound++; + colorToIndex[col] = index; + + _cursorPal[index * 4 + 0] = r; + _cursorPal[index * 4 + 1] = g; + _cursorPal[index * 4 + 2] = b; + _cursorPal[index * 4 + 3] = 0xFF; + + if (colorsFound > MAX_CURS_COLORS) { + warning("Cursor contains too many colors (%d, but only %d are allowed)", colorsFound, MAX_CURS_COLORS); + return false; + } + } + + // Copy pixel from the 16 bit source surface to the 8bit target surface + const int index = colorToIndex[col]; + _cursor[y * _cursorWidth + x] = index; + } + src += _cursorWidth; + } + + _useCursor = true; + _cursorPalSize = colorsFound; + return true; } @@ -1189,6 +1326,9 @@ TextData ThemeEngine::getTextData(DrawData ddId) const { return _widgets[ddId] ? (TextData)_widgets[ddId]->_textDataId : kTextDataNone; } +TextColor ThemeEngine::getTextColor(DrawData ddId) const { + return _widgets[ddId] ? _widgets[ddId]->_textColorId : kTextColorMAX; +} DrawData ThemeEngine::parseDrawDataId(const Common::String &name) const { for (int i = 0; i < kDrawDataMAX; ++i) @@ -1287,7 +1427,7 @@ bool ThemeEngine::themeConfigParseHeader(Common::String header, Common::String & Common::StringTokenizer tok(header, ":"); - if (tok.nextToken() != SCUMMVM_THEME_VERSION_STR) + if (tok.nextToken() != RESIDUAL_THEME_VERSION_STR) return false; themeName = tok.nextToken(); @@ -1383,7 +1523,7 @@ void ThemeEngine::listUsableThemes(Common::List &list) { output.clear(); } -void ThemeEngine::listUsableThemes(Common::FSNode node, Common::List &list, int depth) { +void ThemeEngine::listUsableThemes(const Common::FSNode &node, Common::List &list, int depth) { if (!node.exists() || !node.isReadable() || !node.isDirectory()) return; diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index 5df8448273d..76d03daf530 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -32,7 +32,7 @@ #include "graphics/surface.h" #include "graphics/fontman.h" -#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.5" +#define RESIDUAL_THEME_VERSION_STR "RESIDUAL_STX0.8" namespace Graphics { struct DrawStep; @@ -45,6 +45,7 @@ struct WidgetDrawData; struct DrawDataInfo; struct TextDataInfo; struct TextDrawData; +struct TextColorData; class Dialog; class GuiObject; class ThemeEval; @@ -62,6 +63,7 @@ enum DrawData { kDDPlainColorBackground, kDDDefaultBackground, kDDTextSelectionBackground, + kDDTextSelectionFocusBackground, kDDWidgetBackgroundDefault, kDDWidgetBackgroundSmall, @@ -104,15 +106,26 @@ enum DrawData { enum TextData { kTextDataNone = -1, kTextDataDefault = 0, - kTextDataHover, - kTextDataDisabled, - kTextDataInverted, kTextDataButton, - kTextDataButtonHover, kTextDataNormalFont, kTextDataMAX }; +enum TextColor { + kTextColorNormal = 0, + kTextColorNormalInverted, + kTextColorNormalHover, + kTextColorNormalDisabled, + kTextColorAlternative, + kTextColorAlternativeInverted, + kTextColorAlternativeHover, + kTextColorAlternativeDisabled, + kTextColorButton, + kTextColorButtonHover, + kTextColorButtonDisabled, + kTextColorMAX +}; + class ThemeEngine { protected: typedef Common::HashMap ImagesMap; @@ -156,6 +169,13 @@ public: typedef State WidgetStateInfo; + //! Text inversion state of the text to be draw + enum TextInversionState { + kTextInversionNone, //!< Indicates that the text should not be drawn inverted + kTextInversion, //!< Indicates that the text should be drawn inverted, but not focused + kTextInversionFocus //!< Indicates thte the test should be drawn inverted, and focused + }; + enum ScrollbarState { kScrollbarStateNo, kScrollbarStateUp, @@ -175,6 +195,13 @@ public: kFontStyleMax }; + //! Font color selector + enum FontColor { + kFontColorNormal = 0, //!< The default color of the theme + kFontColorAlternate = 1, //!< Alternative font color + kFontColorMax + }; + //! Function used to process areas other than the current dialog enum ShadingStyle { kShadingNone, //!< No special post processing @@ -302,9 +329,9 @@ public: void drawDialogBackground(const Common::Rect &r, DialogBackground type, WidgetStateInfo state = kStateEnabled); - void drawText(const Common::Rect &r, const Common::String &str, WidgetStateInfo state = kStateEnabled, Graphics::TextAlign align = Graphics::kTextAlignCenter, bool inverted = false, int deltax = 0, bool useEllipsis = true, FontStyle font = kFontStyleBold); + void drawText(const Common::Rect &r, const Common::String &str, WidgetStateInfo state = kStateEnabled, Graphics::TextAlign align = Graphics::kTextAlignCenter, TextInversionState inverted = kTextInversionNone, int deltax = 0, bool useEllipsis = true, FontStyle font = kFontStyleBold, FontColor color = kFontColorNormal); - void drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, WidgetStateInfo state = kStateEnabled); + void drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, WidgetStateInfo state = kStateEnabled, FontColor color = kFontColorNormal); //@} @@ -332,6 +359,7 @@ public: DrawData parseDrawDataId(const Common::String &name) const; TextData getTextData(DrawData ddId) const; + TextColor getTextColor(DrawData ddId) const; /** @@ -346,7 +374,7 @@ public: void addDrawStep(const Common::String &drawDataId, const Graphics::DrawStep &step); /** - * Interfacefor the ThemeParser class: Parsed DrawData sets are added via this function. + * Interface for the ThemeParser class: Parsed DrawData sets are added via this function. * The goal of the function is to initialize each DrawData set before their DrawSteps can * be added, hence this must be called for each DD set before addDrawStep() can be called * for that given set. @@ -363,9 +391,16 @@ public: * * @param fontName Identifier name for the font. * @param file Name of the font file. - * @param r, g, b Color of the font. */ - bool addFont(TextData textId, const Common::String &file, int r, int g, int b); + bool addFont(TextData textId, const Common::String &file); + + /** + * Interface for the ThemeParser class: adds a text color value. + * + * @param colorId Identifier for the color type. + * @param r, g, b Color of the font. + */ + bool addTextColor(TextColor colorId, int r, int g, int b); /** @@ -380,7 +415,7 @@ public: * Adds a new TextStep from the ThemeParser. This will be deprecated/removed once the * new Font API is in place. FIXME: Is that so ??? */ - bool addTextData(const Common::String &drawDataId, TextData textId, Graphics::TextAlign alignH, TextAlignVertical alignV); + bool addTextData(const Common::String &drawDataId, TextData textId, TextColor id, Graphics::TextAlign alignH, TextAlignVertical alignV); protected: /** @@ -503,7 +538,7 @@ protected: * This function is called from all the Widget Drawing methods. */ void queueDD(DrawData type, const Common::Rect &r, uint32 dynamic = 0, bool restore = false); - void queueDDText(TextData type, const Common::Rect &r, const Common::String &text, bool restoreBg, + void queueDDText(TextData type, TextColor color, const Common::Rect &r, const Common::String &text, bool restoreBg, bool elipsis, Graphics::TextAlign alignH = Graphics::kTextAlignLeft, TextAlignVertical alignV = kTextAlignVTop, int deltax = 0); void queueBitmap(const Graphics::Surface *bitmap, const Common::Rect &r, bool alpha); @@ -529,7 +564,7 @@ private: static Common::String getThemeFile(const Common::String &id); static Common::String getThemeId(const Common::String &filename); - static void listUsableThemes(Common::FSNode node, Common::List &list, int depth=-1); + static void listUsableThemes(const Common::FSNode &node, Common::List &list, int depth = -1); protected: OSystem *_system; /** Global system object. */ @@ -572,8 +607,14 @@ protected: /** Array of all the text fonts that can be drawn. */ TextDrawData *_texts[kTextDataMAX]; + /** Array of all font colors available. */ + TextColorData *_textColors[kTextColorMAX]; + ImagesMap _bitmaps; Graphics::PixelFormat _overlayFormat; +#ifdef USE_RGB_COLOR + Graphics::PixelFormat _cursorFormat; +#endif /** List of all the dirty screens that must be blitted to the overlay. */ Common::List _dirtyScreen; diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp index 4a26177dd99..9b15389d150 100644 --- a/gui/ThemeParser.cpp +++ b/gui/ThemeParser.cpp @@ -23,17 +23,11 @@ * */ -#include "common/util.h" -#include "common/system.h" -#include "common/events.h" -#include "common/hashmap.h" -#include "common/hash-str.h" -#include "common/xmlparser.h" - #include "gui/ThemeEngine.h" #include "gui/ThemeEval.h" #include "gui/ThemeParser.h" #include "gui/GuiManager.h" + #include "graphics/VectorRenderer.h" namespace GUI { @@ -44,13 +38,9 @@ struct TextDataInfo { }; static const TextDataInfo kTextDataDefaults[] = { - {kTextDataDefault, "text_default"}, - {kTextDataHover, "text_hover"}, - {kTextDataDisabled, "text_disabled"}, - {kTextDataInverted, "text_inverted"}, - {kTextDataButton, "text_button"}, - {kTextDataButtonHover, "text_button_hover"}, - {kTextDataNormalFont, "text_normal"} + { kTextDataDefault, "text_default" }, + { kTextDataButton, "text_button" }, + { kTextDataNormalFont, "text_normal" } }; @@ -62,6 +52,33 @@ static TextData parseTextDataId(const Common::String &name) { return kTextDataNone; } +struct TextColorDataInfo { + TextColor id; + const char *name; +}; + +static const TextColorDataInfo kTextColorDefaults[] = { + { kTextColorNormal, "color_normal" }, + { kTextColorNormalInverted, "color_normal_inverted" }, + { kTextColorNormalHover, "color_normal_hover" }, + { kTextColorNormalDisabled, "color_normal_disabled" }, + { kTextColorAlternative, "color_alternative" }, + { kTextColorAlternativeInverted, "color_alternative_inverted" }, + { kTextColorAlternativeHover, "color_alternative_hover" }, + { kTextColorAlternativeDisabled, "color_alternative_disabled" }, + { kTextColorButton, "color_button" }, + { kTextColorButtonHover, "color_button_hover" }, + { kTextColorButtonDisabled, "color_button_disabled" } +}; + +static TextColor parseTextColorId(const Common::String &name) { + for (int i = 0; i < kTextColorMAX; ++i) + if (name.compareToIgnoreCase(kTextColorDefaults[i].name) == 0) + return kTextColorDefaults[i].id; + + return kTextColorMAX; +} + static Graphics::TextAlign parseTextHAlign(const Common::String &val) { if (val == "left") return Graphics::kTextAlignLeft; @@ -86,19 +103,6 @@ static GUI::ThemeEngine::TextAlignVertical parseTextVAlign(const Common::String ThemeParser::ThemeParser(ThemeEngine *parent) : XMLParser() { - - _drawFunctions["circle"] = &Graphics::VectorRenderer::drawCallback_CIRCLE; - _drawFunctions["square"] = &Graphics::VectorRenderer::drawCallback_SQUARE; - _drawFunctions["roundedsq"] = &Graphics::VectorRenderer::drawCallback_ROUNDSQ; - _drawFunctions["bevelsq"] = &Graphics::VectorRenderer::drawCallback_BEVELSQ; - _drawFunctions["line"] = &Graphics::VectorRenderer::drawCallback_LINE; - _drawFunctions["triangle"] = &Graphics::VectorRenderer::drawCallback_TRIANGLE; - _drawFunctions["fill"] = &Graphics::VectorRenderer::drawCallback_FILLSURFACE; - _drawFunctions["tab"] = &Graphics::VectorRenderer::drawCallback_TAB; - _drawFunctions["void"] = &Graphics::VectorRenderer::drawCallback_VOID; - _drawFunctions["bitmap"] = &Graphics::VectorRenderer::drawCallback_BITMAP; - _drawFunctions["cross"] = &Graphics::VectorRenderer::drawCallback_CROSS; - _defaultStepGlobal = defaultDrawStep(); _defaultStepLocal = 0; _theme = parent; @@ -107,8 +111,6 @@ ThemeParser::ThemeParser(ThemeEngine *parent) : XMLParser() { ThemeParser::~ThemeParser() { delete _defaultStepGlobal; delete _defaultStepLocal; - _palette.clear(); - _drawFunctions.clear(); } void ThemeParser::cleanup() { @@ -169,21 +171,32 @@ bool ThemeParser::parserCallback_defaults(ParserNode *node) { } bool ThemeParser::parserCallback_font(ParserNode *node) { - int red, green, blue; - if (resolutionCheck(node->values["resolution"]) == false) { node->ignore = true; return true; } + TextData textDataId = parseTextDataId(node->values["id"]); + if (!_theme->addFont(textDataId, node->values["file"])) + return parserError("Error loading Font in theme engine."); + + return true; +} + +bool ThemeParser::parserCallback_text_color(ParserNode *node) { + int red, green, blue; + + TextColor colorId = parseTextColorId(node->values["id"]); + if (colorId == kTextColorMAX) + return parserError("Error text color is not defined."); + if (_palette.contains(node->values["color"])) getPaletteColor(node->values["color"], red, green, blue); else if (!parseIntegerKey(node->values["color"].c_str(), 3, &red, &green, &blue)) - return parserError("Error parsing color value for font definition."); + return parserError("Error parsing color value for text color definition."); - TextData textDataId = parseTextDataId(node->values["id"]); - if (!_theme->addFont(textDataId, node->values["file"], red, green, blue)) - return parserError("Error loading Font in theme engine."); + if (!_theme->addTextColor(colorId, red, green, blue)) + return parserError("Error while adding text color information."); return true; } @@ -236,8 +249,9 @@ bool ThemeParser::parserCallback_text(ParserNode *node) { Common::String id = getParentNode(node)->values["id"]; TextData textDataId = parseTextDataId(node->values["font"]); + TextColor textColorId = parseTextColorId(node->values["text_color"]); - if (!_theme->addTextData(id, textDataId, alignH, alignV)) + if (!_theme->addTextData(id, textDataId, textColorId, alignH, alignV)) return parserError("Error adding Text Data for '%s'.", id.c_str()); return true; @@ -281,15 +295,44 @@ bool ThemeParser::parserCallback_color(ParserNode *node) { } +static Graphics::DrawingFunctionCallback getDrawingFunctionCallback(const Common::String &name) { + + if (name == "circle") + return &Graphics::VectorRenderer::drawCallback_CIRCLE; + if (name == "square") + return &Graphics::VectorRenderer::drawCallback_SQUARE; + if (name == "roundedsq") + return &Graphics::VectorRenderer::drawCallback_ROUNDSQ; + if (name == "bevelsq") + return &Graphics::VectorRenderer::drawCallback_BEVELSQ; + if (name == "line") + return &Graphics::VectorRenderer::drawCallback_LINE; + if (name == "triangle") + return &Graphics::VectorRenderer::drawCallback_TRIANGLE; + if (name == "fill") + return &Graphics::VectorRenderer::drawCallback_FILLSURFACE; + if (name == "tab") + return &Graphics::VectorRenderer::drawCallback_TAB; + if (name == "void") + return &Graphics::VectorRenderer::drawCallback_VOID; + if (name == "bitmap") + return &Graphics::VectorRenderer::drawCallback_BITMAP; + if (name == "cross") + return &Graphics::VectorRenderer::drawCallback_CROSS; + + return 0; +} + + bool ThemeParser::parserCallback_drawstep(ParserNode *node) { Graphics::DrawStep *drawstep = newDrawStep(); Common::String functionName = node->values["func"]; - if (_drawFunctions.contains(functionName) == false) - return parserError("%s is not a valid drawing function name", functionName.c_str()); + drawstep->drawingCall = getDrawingFunctionCallback(functionName); - drawstep->drawingCall = _drawFunctions[functionName]; + if (drawstep->drawingCall == 0) + return parserError("%s is not a valid drawing function name", functionName.c_str()); if (!parseDrawStep(node, drawstep, true)) return false; @@ -416,7 +459,7 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst if (stepNode->values.contains("orientation")) { val = stepNode->values["orientation"]; - if ( val == "top") + if (val == "top") drawstep->extraData = Graphics::VectorRenderer::kTriangleUp; else if (val == "bottom") drawstep->extraData = Graphics::VectorRenderer::kTriangleDown; @@ -579,7 +622,7 @@ bool ThemeParser::parserCallback_widget(ParserNode *node) { Graphics::TextAlign alignH = Graphics::kTextAlignLeft; if (node->values.contains("textalign")) { - if((alignH = parseTextHAlign(node->values["textalign"])) == Graphics::kTextAlignInvalid) + if ((alignH = parseTextHAlign(node->values["textalign"])) == Graphics::kTextAlignInvalid) return parserError("Invalid value for text alignment."); } @@ -804,7 +847,7 @@ bool ThemeParser::parseCommonLayoutProps(ParserNode *node, const Common::String if (node->values.contains("textalign")) { Graphics::TextAlign alignH = Graphics::kTextAlignLeft; - if((alignH = parseTextHAlign(node->values["textalign"])) == Graphics::kTextAlignInvalid) + if ((alignH = parseTextHAlign(node->values["textalign"])) == Graphics::kTextAlignInvalid) return parserError("Invalid value for text alignment."); _theme->getEvaluator()->setVar(var + "Align", alignH); diff --git a/gui/ThemeParser.h b/gui/ThemeParser.h index 80499bdafe8..5b86b32df9e 100644 --- a/gui/ThemeParser.h +++ b/gui/ThemeParser.h @@ -27,7 +27,6 @@ #define THEME_PARSER_H #include "common/sys.h" -#include "common/system.h" #include "common/xmlparser.h" namespace GUI { @@ -35,8 +34,6 @@ namespace GUI { class ThemeEngine; class ThemeParser : public Common::XMLParser { - typedef void (Graphics::VectorRenderer::*DrawingFunctionCallback)(const Common::Rect &, const Graphics::DrawStep &); - public: ThemeParser(ThemeEngine *parent); @@ -70,9 +67,13 @@ protected: XML_KEY(font) XML_PROP(id, true) XML_PROP(file, true) - XML_PROP(color, true) XML_PROP(resolution, false) KEY_END() + + XML_KEY(text_color) + XML_PROP(id, true); + XML_PROP(color, true); + KEY_END() KEY_END() XML_KEY(bitmaps) @@ -146,6 +147,7 @@ protected: XML_KEY(text) XML_PROP(font, true) + XML_PROP(text_color, true) XML_PROP(vertical_align, true) XML_PROP(horizontal_align, true) KEY_END() @@ -214,6 +216,7 @@ protected: bool parserCallback_render_info(ParserNode *node); bool parserCallback_defaults(ParserNode *node); bool parserCallback_font(ParserNode *node); + bool parserCallback_text_color(ParserNode *node); bool parserCallback_fonts(ParserNode *node); bool parserCallback_text(ParserNode *node); bool parserCallback_palette(ParserNode *node); @@ -249,8 +252,6 @@ protected: Graphics::DrawStep *_defaultStepGlobal; Graphics::DrawStep *_defaultStepLocal; - Common::HashMap _drawFunctions; - struct PaletteColor { uint8 r, g, b; }; diff --git a/gui/about.cpp b/gui/about.cpp index dc3a424b971..5631d1a3314 100644 --- a/gui/about.cpp +++ b/gui/about.cpp @@ -100,7 +100,7 @@ AboutDialog::AboutDialog() _w = 0; for (i = 0; i < ARRAYSIZE(credits); i++) { int tmp = g_gui.getStringWidth(credits[i] + 5); - if ( _w < tmp && tmp <= maxW) { + if (_w < tmp && tmp <= maxW) { _w = tmp; } } @@ -247,7 +247,7 @@ void AboutDialog::drawDialog() { str++; if (*str && y > _y && y + g_gui.theme()->getFontHeight() < _y + _h) - g_gui.theme()->drawText(Common::Rect(_x + _xOff, y, _x + _w - _xOff, y + g_gui.theme()->getFontHeight()), str, state, align, false, 0, false); + g_gui.theme()->drawText(Common::Rect(_x + _xOff, y, _x + _w - _xOff, y + g_gui.theme()->getFontHeight()), str, state, align, ThemeEngine::kTextInversionNone, 0, false); y += _lineHeight; } } @@ -311,7 +311,7 @@ void AboutDialog::reflowLayout() { _w = 0; for (int i = 0; i < ARRAYSIZE(credits); i++) { int tmp = g_gui.getStringWidth(credits[i] + 5); - if ( _w < tmp && tmp <= maxW) { + if (_w < tmp && tmp <= maxW) { _w = tmp; } } diff --git a/gui/browser.cpp b/gui/browser.cpp index 6a31a296251..0bf533c171b 100644 --- a/gui/browser.cpp +++ b/gui/browser.cpp @@ -43,7 +43,7 @@ enum { */ BrowserDialog::BrowserDialog(const char *title, bool dirBrowser) - : Dialog("browser") { + : Dialog("Browser") { _titleRef = CFStringCreateWithCString(0, title, CFStringGetSystemEncoding()); _isDirBrowser = dirBrowser; } diff --git a/gui/console.cpp b/gui/console.cpp index c8b9a6d8c40..b39e2307e61 100644 --- a/gui/console.cpp +++ b/gui/console.cpp @@ -112,6 +112,7 @@ void ConsoleDialog::init() { _w = _w - _w / 20; _h = _h * kConsoleLineHeight + 2; + _x = _w / 40; // Set scrollbar dimensions int scrollBarWidth = g_gui.xmlEval()->getVar("Globals.Scrollbar.Width", 0); @@ -149,8 +150,8 @@ void ConsoleDialog::open() { if (_w != w || _h != h) init(); - _x = _w / 40; _y = -_h; + _slideTime = g_system->getMillis(); _slideMode = kDownSlideMode; diff --git a/gui/console.h b/gui/console.h index 10ca4b36b44..19fbdea4e15 100644 --- a/gui/console.h +++ b/gui/console.h @@ -143,7 +143,7 @@ public: void handleKeyDown(Common::KeyState state); void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); - int printf(const char *format, ...); + int printf(const char *format, ...) GCC_PRINTF(2, 3); int vprintf(const char *format, va_list argptr); #undef putchar void putchar(int c); diff --git a/gui/debugger.cpp b/gui/debugger.cpp index 1679a175a4b..2a208655045 100644 --- a/gui/debugger.cpp +++ b/gui/debugger.cpp @@ -27,10 +27,14 @@ #include "common/system.h" #include "gui/debugger.h" -#if USE_CONSOLE +#ifndef USE_TEXT_CONSOLE #include "gui/console.h" +#elif defined(USE_READLINE) + #include + #include #endif + namespace GUI { Debugger::Debugger() { @@ -39,9 +43,11 @@ Debugger::Debugger() { _isAttached = false; _errStr = NULL; _firstTime = true; - _debuggerDialog = new GUI::ConsoleDialog(1.0, 0.67F); +#ifndef USE_TEXT_CONSOLE + _debuggerDialog = new GUI::ConsoleDialog(1.0f, 0.67f); _debuggerDialog->setInputCallback(debuggerInputCallback, this); _debuggerDialog->setCompletionCallback(debuggerCompletionCallback, this); +#endif //DCmd_Register("continue", WRAP_METHOD(Debugger, Cmd_Exit)); DCmd_Register("exit", WRAP_METHOD(Debugger, Cmd_Exit)); @@ -55,7 +61,9 @@ Debugger::Debugger() { } Debugger::~Debugger() { +#ifndef USE_TEXT_CONSOLE delete _debuggerDialog; +#endif } @@ -65,7 +73,7 @@ int Debugger::DebugPrintf(const char *format, ...) { va_start(argptr, format); int count; -#if USE_CONSOLE +#ifndef USE_TEXT_CONSOLE count = _debuggerDialog->vprintf(format, argptr); #else count = ::vprintf(format, argptr); @@ -112,9 +120,22 @@ void Debugger::onFrame() { } } +#if defined(USE_TEXT_CONSOLE) && defined(USE_READLINE) +namespace { +Debugger *g_readline_debugger; + +char *readline_completionFunction(const char *text, int state) { + return g_readline_debugger->readlineComplete(text, state); +} +} // end of anonymous namespace +#endif + // Main Debugger Loop void Debugger::enter() { -#if USE_CONSOLE + // TODO: Having three I/O methods #ifdef-ed in this file is not the + // cleanest approach to this... + +#ifndef USE_TEXT_CONSOLE if (_firstTime) { DebugPrintf("Debugger started, type 'exit' to return to the game.\n"); DebugPrintf("Type 'help' to see a little list of commands and variables.\n"); @@ -129,18 +150,28 @@ void Debugger::enter() { _debuggerDialog->runModal(); #else - // TODO: compared to the console input, this here is very bare bone. - // For example, no support for tab completion and no history. At least - // we should re-add (optional) support for the readline library. - // Or maybe instead of choosing between a console dialog and stdio, - // we should move that choice into the ConsoleDialog class - that is, - // the console dialog code could be #ifdef'ed to not print to the dialog - // but rather to stdio. This way, we could also reuse the command history - // and tab completion of the console. It would still require a lot of - // work, but at least no dependency on a 3rd party library... - printf("Debugger entered, please switch to this console for input.\n"); +#ifdef USE_READLINE + // TODO: add support for saving/loading history? + + g_readline_debugger = this; + rl_completion_entry_function = &readline_completionFunction; + + char *line_read = 0; + do { + free(line_read); + line_read = readline("debug> "); + + if (line_read && line_read[0]) + add_history(line_read); + + } while (line_read && parseCommand(line_read)); + + free(line_read); + line_read = 0; + +#else int i; char buf[256]; @@ -156,6 +187,7 @@ void Debugger::enter() { if (i == 0) continue; } while (parseCommand(buf)); +#endif #endif } @@ -211,7 +243,7 @@ bool Debugger::parseCommand(const char *inputOrig) { break; // Integer Array case DVAR_INTARRAY: { - char *chr = (char *)strchr(param[0], '['); + const char *chr = strchr(param[0], '['); if (!chr) { DebugPrintf("You must access this array as %s[element]\n", param[0]); } else { @@ -307,7 +339,8 @@ bool Debugger::tabComplete(const char *input, Common::String &completion) const } else { // take common prefix of previous match and this command for (uint j = 0; j < completion.size(); j++) { - if (completion[j] != i->_key[inputlen + j]) { + if (inputlen + j >= i->_key.size() || + completion[j] != i->_key[inputlen + j]) { completion = Common::String(completion.begin(), completion.begin() + j); // If there is no unambiguous completion, abort if (completion.empty()) @@ -325,6 +358,29 @@ bool Debugger::tabComplete(const char *input, Common::String &completion) const return true; } +#if defined(USE_TEXT_CONSOLE) && defined(USE_READLINE) +char *Debugger::readlineComplete(const char *input, int state) { + static CommandsMap::const_iterator iter; + + // We assume that _cmds isn't changed between calls to readlineComplete, + // unless state is 0. + if (state == 0) { + iter = _cmds.begin(); + } else { + ++iter; + } + + for (; iter != _cmds.end(); ++iter) { + if (iter->_key.hasPrefix(input)) { + char *ret = (char *)malloc(iter->_key.size() + 1); + strcpy(ret, iter->_key.c_str()); + return ret; + } + } + return 0; +} +#endif + // Variable registration function void Debugger::DVar_Register(const Common::String &varname, void *pointer, int type, int optional) { // TODO: Filter out duplicates @@ -356,8 +412,15 @@ bool Debugger::Cmd_Exit(int argc, const char **argv) { // Print a list of all registered commands (and variables, if any), // nicely word-wrapped. bool Debugger::Cmd_Help(int argc, const char **argv) { - +#ifndef USE_TEXT_CONSOLE const int charsPerLine = _debuggerDialog->getCharsPerLine(); +#elif defined(USE_READLINE) + int charsPerLine, rows; + rl_get_screen_size(&rows, &charsPerLine); +#else + // Can we do better? + const int charsPerLine = 80; +#endif int width, size; uint i; @@ -452,7 +515,7 @@ bool Debugger::Cmd_DebugFlagDisable(int argc, const char **argv) { } // Console handler -#if USE_CONSOLE +#ifndef USE_TEXT_CONSOLE bool Debugger::debuggerInputCallback(GUI::ConsoleDialog *console, const char *input, void *refCon) { Debugger *debugger = (Debugger *)refCon; diff --git a/gui/debugger.h b/gui/debugger.h index 6ae02b60c7a..697cd04a841 100644 --- a/gui/debugger.h +++ b/gui/debugger.h @@ -32,10 +32,7 @@ namespace GUI { -// Choose between text console or ScummConsole -#define USE_CONSOLE 1 - -#ifdef USE_CONSOLE +#ifndef USE_TEXT_CONSOLE class ConsoleDialog; #endif @@ -86,7 +83,9 @@ private: bool _isAttached; char *_errStr; bool _firstTime; +#ifndef USE_TEXT_CONSOLE GUI::ConsoleDialog *_debuggerDialog; +#endif protected: // Hook for subclasses: Called just before enter() is run @@ -118,11 +117,15 @@ protected: bool Cmd_DebugFlagEnable(int argc, const char **argv); bool Cmd_DebugFlagDisable(int argc, const char **argv); -#if USE_CONSOLE +#ifndef USE_TEXT_CONSOLE private: static bool debuggerInputCallback(GUI::ConsoleDialog *console, const char *input, void *refCon); static bool debuggerCompletionCallback(GUI::ConsoleDialog *console, const char *input, Common::String &completion, void *refCon); +#elif defined(USE_READLINE) +public: + char *readlineComplete(const char *input, int state); #endif + }; } // End of namespace GUI diff --git a/gui/launcher.cpp b/gui/launcher.cpp index 2e50273a07e..987ed93214e 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -638,6 +638,10 @@ void LauncherDialog::updateListing() { // Select the last entry if the list has been reduced _list->setSelected(_list->getList().size() - 1); updateButtons(); + + // Update the filter settings, those are lost when "setList" + // is called. + _list->setFilter(_searchWidget->getEditString()); } void LauncherDialog::addGame() { diff --git a/gui/massadd.cpp b/gui/massadd.cpp index 3162078ee7f..42ddfa4e7a7 100644 --- a/gui/massadd.cpp +++ b/gui/massadd.cpp @@ -149,8 +149,10 @@ void MassAddDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data ConfMan.flushToDisk(); // And scroll to first detected game - sort(_games.begin(), _games.end(), GameDescLess()); - ConfMan.set("temp_selection", _games.front().gameid()); + if (!_games.empty()) { + sort(_games.begin(), _games.end(), GameDescLess()); + ConfMan.set("temp_selection", _games.front().gameid()); + } close(); } else if (cmd == kCancelCmd) { diff --git a/gui/options.cpp b/gui/options.cpp index e1a495238e1..bc8abc3afac 100644 --- a/gui/options.cpp +++ b/gui/options.cpp @@ -44,6 +44,7 @@ enum { kMidiGainChanged = 'mgch', kMusicVolumeChanged = 'muvc', kSfxVolumeChanged = 'sfvc', + kMuteAllChanged = 'mute', kSubtitleToggle = 'sttg', kSubtitleSpeedChanged = 'stsc', kSpeechVolumeChanged = 'vcvc', @@ -64,8 +65,8 @@ enum { static const char *savePeriodLabels[] = { "Never", "every 5 mins", "every 10 mins", "every 15 mins", "every 30 mins", 0 }; static const int savePeriodValues[] = { 0, 5 * 60, 10 * 60, 15 * 60, 30 * 60, -1 }; -static const char *outputRateLabels[] = { "", "22 kHz", "8 kHz", "11kHz", "44 kHz", "48 kHz", 0 }; -static const int outputRateValues[] = { 0, 22050, 8000, 11025, 44100, 48000, -1 }; +static const char *outputRateLabels[] = { "", "8 kHz", "11kHz", "22 kHz", "44 kHz", "48 kHz", 0 }; +static const int outputRateValues[] = { 0, 8000, 11025, 22050, 44100, 48000, -1 }; @@ -325,7 +326,7 @@ void OptionsDialog::setVolumeSettingsState(bool enabled) { _enableVolumeSettings = enabled; - ena = enabled; + ena = enabled && !_muteCheckbox->getState(); if (_guioptions & Common::GUIO_NOMUSIC) ena = false; @@ -333,7 +334,7 @@ void OptionsDialog::setVolumeSettingsState(bool enabled) { _musicVolumeSlider->setEnabled(ena); _musicVolumeLabel->setEnabled(ena); - ena = enabled; + ena = enabled && !_muteCheckbox->getState(); if (_guioptions & Common::GUIO_NOSFX) ena = false; @@ -341,7 +342,7 @@ void OptionsDialog::setVolumeSettingsState(bool enabled) { _sfxVolumeSlider->setEnabled(ena); _sfxVolumeLabel->setEnabled(ena); - ena = enabled; + ena = enabled && !_muteCheckbox->getState(); if (_guioptions & Common::GUIO_NOSPEECH) ena = false; @@ -453,10 +454,10 @@ int OptionsDialog::getSubtitleMode(bool subtitles, bool speech_mute) { } void OptionsDialog::reflowLayout() { - Dialog::reflowLayout(); - if (_graphicsTabId != -1 && _tabWidget) _tabWidget->setTabTitle(_graphicsTabId, g_system->getOverlayWidth() > 320 ? "Graphics" : "GFX"); + + Dialog::reflowLayout(); } #pragma mark - diff --git a/gui/saveload.cpp b/gui/saveload.cpp index a451a2eb618..0a3ad92d70f 100644 --- a/gui/saveload.cpp +++ b/gui/saveload.cpp @@ -42,15 +42,15 @@ enum { }; SaveLoadChooser::SaveLoadChooser(const String &title, const String &buttonLabel) - : Dialog("ScummSaveLoad"), _delSupport(0), _list(0), _chooseButton(0), _deleteButton(0), _gfxWidget(0) { + : Dialog("SaveLoadChooser"), _delSupport(0), _list(0), _chooseButton(0), _deleteButton(0), _gfxWidget(0) { _delSupport = _metaInfoSupport = _thumbnailSupport = _saveDateSupport = _playTimeSupport = false; _backgroundType = ThemeEngine::kDialogBackgroundSpecial; - new StaticTextWidget(this, "ScummSaveLoad.Title", title); + new StaticTextWidget(this, "SaveLoadChooser.Title", title); // Add choice list - _list = new GUI::ListWidget(this, "ScummSaveLoad.List"); + _list = new GUI::ListWidget(this, "SaveLoadChooser.List"); _list->setNumberingMode(GUI::kListNumberingZero); setSaveMode(false); @@ -61,11 +61,11 @@ SaveLoadChooser::SaveLoadChooser(const String &title, const String &buttonLabel) _playtime = new StaticTextWidget(this, 0, 0, 10, 10, "No playtime saved", Graphics::kTextAlignCenter); // Buttons - new GUI::ButtonWidget(this, "ScummSaveLoad.Cancel", "Cancel", kCloseCmd, 0); - _chooseButton = new GUI::ButtonWidget(this, "ScummSaveLoad.Choose", buttonLabel, kChooseCmd, 0); + new GUI::ButtonWidget(this, "SaveLoadChooser.Cancel", "Cancel", kCloseCmd, 0); + _chooseButton = new GUI::ButtonWidget(this, "SaveLoadChooser.Choose", buttonLabel, kChooseCmd, 0); _chooseButton->setEnabled(false); - _deleteButton = new GUI::ButtonWidget(this, "ScummSaveLoad.Delete", "Delete", kDelCmd, 0); + _deleteButton = new GUI::ButtonWidget(this, "SaveLoadChooser.Delete", "Delete", kDelCmd, 0); _deleteButton->setEnabled(false); _delSupport = _metaInfoSupport = _thumbnailSupport = false; @@ -172,11 +172,11 @@ void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 da } void SaveLoadChooser::reflowLayout() { - if (g_gui.xmlEval()->getVar("Globals.ScummSaveLoad.ExtInfo.Visible") == 1 && _thumbnailSupport) { + if (g_gui.xmlEval()->getVar("Globals.SaveLoadChooser.ExtInfo.Visible") == 1 && _thumbnailSupport) { int16 x, y; uint16 w, h; - if (!g_gui.xmlEval()->getWidgetData("ScummSaveLoad.Thumbnail", x, y, w, h)) + if (!g_gui.xmlEval()->getWidgetData("SaveLoadChooser.Thumbnail", x, y, w, h)) error("Error when loading position data for Save/Load Thumbnails."); int thumbW = kThumbnailWidth; @@ -235,6 +235,11 @@ void SaveLoadChooser::updateSelection(bool redraw) { bool isWriteProtected = false; bool startEditMode = _list->isEditable(); + _gfxWidget->setGfx(-1, -1, _fillR, _fillG, _fillB); + _date->setLabel("No date saved"); + _time->setLabel("No time saved"); + _playtime->setLabel("No playtime saved"); + if (selItem >= 0 && !_list->getSelectedString().empty() && _metaInfoSupport) { SaveStateDescriptor desc = (*_plugin)->querySaveMetaInfos(_target.c_str(), atoi(_saveList[selItem].save_slot().c_str())); @@ -250,36 +255,20 @@ void SaveLoadChooser::updateSelection(bool redraw) { if (thumb) { _gfxWidget->setGfx(thumb); _gfxWidget->useAlpha(256); - } else { - _gfxWidget->setGfx(-1, -1, _fillR, _fillG, _fillB); } } if (_saveDateSupport) { - Common::String date = "Date: "; if (desc.contains("save_date")) - date += desc.getVal("save_date"); - else - date = "No date saved"; + _date->setLabel("Date: " + desc.getVal("save_date")); - Common::String time = "Time: "; if (desc.contains("save_time")) - time += desc.getVal("save_time"); - else - time = "No time saved"; - - _date->setLabel(date); - _time->setLabel(time); + _time->setLabel("Time: " + desc.getVal("save_time")); } if (_playTimeSupport) { - Common::String time = "Playtime: "; if (desc.contains("play_time")) - time += desc.getVal("play_time"); - else - time = "No playtime saved"; - - _playtime->setLabel(time); + _playtime->setLabel("Playtime: " + desc.getVal("play_time")); } } @@ -289,8 +278,15 @@ void SaveLoadChooser::updateSelection(bool redraw) { // game is write protected _chooseButton->setEnabled(selItem >= 0 && !isWriteProtected); - if (startEditMode) + if (startEditMode) { _list->startEditMode(); + + if (_chooseButton->isEnabled() && _list->getSelectedString() == "Untitled savestate" && + _list->getSelectionColor() == ThemeEngine::kFontColorAlternate) { + _list->setEditString(""); + _list->setEditColor(ThemeEngine::kFontColorNormal); + } + } } else { // Disable the load button if nothing is selected, or if an empty // list item is selected. @@ -327,6 +323,7 @@ void SaveLoadChooser::updateSaveList() { int curSlot = 0; int saveSlot = 0; StringList saveNames; + ListWidget::ColorList colors; for (SaveStateList::const_iterator x = _saveList.begin(); x != _saveList.end(); ++x) { // Handle gaps in the list of save games saveSlot = atoi(x->save_slot().c_str()); @@ -335,6 +332,7 @@ void SaveLoadChooser::updateSaveList() { SaveStateDescriptor dummySave(curSlot, ""); _saveList.insert_at(curSlot, dummySave); saveNames.push_back(dummySave.description()); + colors.push_back(ThemeEngine::kFontColorNormal); curSlot++; } @@ -345,7 +343,18 @@ void SaveLoadChooser::updateSaveList() { } } - saveNames.push_back(x->description()); + // Show "Untitled savestate" for empty/whitespace savegame descriptions + Common::String description = x->description(); + Common::String trimmedDescription = description; + trimmedDescription.trim(); + if (trimmedDescription.empty()) { + description = "Untitled savestate"; + colors.push_back(ThemeEngine::kFontColorAlternate); + } else { + colors.push_back(ThemeEngine::kFontColorNormal); + } + + saveNames.push_back(description); curSlot++; } @@ -355,9 +364,10 @@ void SaveLoadChooser::updateSaveList() { saveNames.push_back(emptyDesc); SaveStateDescriptor dummySave(i, ""); _saveList.push_back(dummySave); + colors.push_back(ThemeEngine::kFontColorNormal); } - _list->setList(saveNames); + _list->setList(saveNames, &colors); } } // End of namespace GUI diff --git a/gui/themes/default.inc b/gui/themes/default.inc index d2fe7dade5f..d088f4181db 100644 --- a/gui/themes/default.inc +++ b/gui/themes/default.inc @@ -20,37 +20,57 @@ " " " " -" " -" " -" " " " -" " " " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " " " " " " " " " +" " +" " +" " " " @@ -121,7 +141,8 @@ "/> " " " " " -" " @@ -133,6 +154,7 @@ " " " " " " @@ -189,6 +211,7 @@ "orientation='bottom' " "/> " " " @@ -207,7 +230,8 @@ "ypos='center' " "orientation='bottom' " "/> " -" " @@ -226,7 +250,8 @@ "ypos='center' " "orientation='bottom' " "/> " -" " @@ -255,6 +280,7 @@ " " " " " " @@ -264,7 +290,8 @@ "/> " " " " " -" " @@ -274,7 +301,8 @@ "/> " " " " " -" " @@ -284,7 +312,8 @@ "/> " " " " " -" " @@ -295,6 +324,7 @@ " " " " " " @@ -310,6 +340,7 @@ " " " " " " @@ -324,7 +355,764 @@ "/> " " " " " -" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " " " " " " " @@ -333,7 +1121,7 @@ " " " " " " -" " +" " " " " " " " @@ -981,7 +1769,7 @@ " " " " " " -" " +" " " " " " " " " " " " " " " " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " diff --git a/ports.mk b/ports.mk index ed8ddc1b48d..2d0a245e95d 100644 --- a/ports.mk +++ b/ports.mk @@ -16,7 +16,7 @@ install: all $(INSTALL) -d "$(DESTDIR)$(PREFIX)/share/pixmaps/" $(INSTALL) -c -m 644 "$(srcdir)/icons/residual.xpm" "$(DESTDIR)$(PREFIX)/share/pixmaps/residual.xpm" $(INSTALL) -d "$(DESTDIR)$(PREFIX)/share/doc/residual/" - $(INSTALL) -c -m 644 "$(srcdir)/AUTHORS" "$(srcdir)/COPYING.LGPL" "$(srcdir)/COPYING.GPL" "$(srcdir)/NEWS" "$(srcdir)/README" "$(srcdir)/TODO" "$(DESTDIR)$(PREFIX)/share/doc/residual/" + $(INSTALL) -c -m 644 $(DIST_FILES_DOCS) "$(DESTDIR)$(PREFIX)/share/doc/residual/" $(INSTALL) -d "$(DESTDIR)$(DATADIR)/residual/" $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) "$(DESTDIR)$(DATADIR)/residual/" ifdef DYNAMIC_MODULES @@ -26,7 +26,7 @@ endif uninstall: rm -f "$(DESTDIR)$(BINDIR)/$(EXECUTABLE)" - #rm -f "$(DESTDIR)$(MANDIR)/man6/residual.6" + rm -f "$(DESTDIR)$(MANDIR)/man6/residual.6" rm -f "$(DESTDIR)$(PREFIX)/share/pixmaps/residual.xpm" rm -rf "$(DESTDIR)$(PREFIX)/share/doc/residual/" rm -rf "$(DESTDIR)$(DATADIR)/residual/" @@ -42,34 +42,30 @@ deb: # Special target to create a application wrapper for Mac OS X bundle_name = Residual.app -bundle: residual-static $(srcdir)/dists/macosx/Info.plist +bundle: residual-static mkdir -p $(bundle_name)/Contents/MacOS mkdir -p $(bundle_name)/Contents/Resources echo "APPL????" > $(bundle_name)/Contents/PkgInfo cp $(srcdir)/dists/macosx/Info.plist $(bundle_name)/Contents/ cp $(srcdir)/icons/residual.icns $(bundle_name)/Contents/Resources/ + cp $(DIST_FILES_DOCS) $(bundle_name)/ + cp $(DIST_FILES_THEMES) $(bundle_name)/Contents/Resources/ + cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/Contents/Resources/ $(srcdir)/tools/credits.pl --rtf > $(bundle_name)/Contents/Resources/Credits.rtf chmod 644 $(bundle_name)/Contents/Resources/* cp residual-static $(bundle_name)/Contents/MacOS/residual chmod 755 $(bundle_name)/Contents/MacOS/residual $(STRIP) $(bundle_name)/Contents/MacOS/residual -iphonebundle: $(srcdir)/dists/iphone/Info.plist +iphonebundle: iphone mkdir -p $(bundle_name) cp $(srcdir)/dists/iphone/Info.plist $(bundle_name)/ cp $(DIST_FILES_THEMES) $(bundle_name)/ - cp $(srcdir)/AUTHORS $(bundle_name)/ - cp $(srcdir)/COPYING $(bundle_name)/ - cp $(srcdir)/COPYING.LGPL $(bundle_name)/ - cp $(srcdir)/COPYRIGHT $(bundle_name)/ + cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/ cp residual $(bundle_name)/Residual cp $(srcdir)/dists/iphone/icon.png $(bundle_name)/icon.png cp $(srcdir)/dists/iphone/Default.png $(bundle_name)/Default.png -# location of additional libs for OS X usually /sw/ for fink or -# /opt/local/ for darwinports -OSXOPT=/sw - # Location of static libs for the iPhone ifneq ($(BACKEND), iphone) # Static libaries, used for the residual-static and iphone targets @@ -99,8 +95,9 @@ ifdef USE_MPEG2 OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libmpeg2.a endif -BUILD_DATE := `date +%y%m%d` -#BUILD_DATE := rev`svn info | grep '^Revision' | sed -e 's/Revision: *//'` +ifdef USE_ZLIB +OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libz.a +endif # Special target to create a static linked binary for Mac OS X. # We use -force_cpusubtype_ALL to ensure the binary runs on every @@ -109,13 +106,13 @@ residual-static: $(OBJS) $(CXX) $(LDFLAGS) -force_cpusubtype_ALL -o residual-static $(OBJS) \ -framework CoreMIDI \ $(OSX_STATIC_LIBS) \ - -lSystemStubs -lz + -lSystemStubs # Special target to create a static linked binary for the iPhone iphone: $(OBJS) $(CXX) $(LDFLAGS) -o residual $(OBJS) \ $(OSX_STATIC_LIBS) \ - -framework UIKit -framework CoreGraphics -framework CoreSurface \ + -framework UIKit -framework CoreGraphics -framework OpenGLES \ -framework GraphicsServices -framework CoreFoundation -framework QuartzCore \ -framework Foundation -framework AudioToolbox -framework CoreAudio \ -lobjc -lz @@ -124,11 +121,11 @@ iphone: $(OBJS) # TODO: Replace AUTHORS by Credits.rtf osxsnap: bundle mkdir Residual-snapshot + $(srcdir)/tools/credits.pl --text > $(srcdir)/AUTHORS cp $(srcdir)/AUTHORS ./Residual-snapshot/Authors cp $(srcdir)/COPYING.GPL ./Residual-snapshot/License\ \(GPL\) cp $(srcdir)/COPYING.LGPL ./Residual-snapshot/License\ \(LGPL\) cp $(srcdir)/NEWS ./Residual-snapshot/News - cp $(srcdir)/TODO ./Residual-snapshot/Todo cp $(srcdir)/README ./Residual-snapshot/Residual\ ReadMe /Developer/Tools/SetFile -t ttro -c ttxt ./Residual-snapshot/* /Developer/Tools/CpMac -r $(bundle_name) ./Residual-snapshot/ @@ -138,8 +135,8 @@ osxsnap: bundle #/Developer/Tools/SetFile -a V ./Residual-snapshot/background.jpg hdiutil create -ov -format UDZO -imagekey zlib-level=9 -fs HFS+ \ -srcfolder Residual-snapshot \ - -volname "Residual $(BUILD_DATE)" \ - Residual-svn-$(BUILD_DATE).dmg + -volname "Residual" \ + ScummVM-snapshot.dmg rm -rf Residual-snapshot # @@ -147,19 +144,19 @@ osxsnap: bundle # residualico.o: $(srcdir)/icons/residual.ico - $(WINDRES) -I$(srcdir) $(srcdir)/dists/residual.rc residualico.o + $(WINDRES) $(WINDRESFLAGS) -I$(srcdir) $(srcdir)/dists/residual.rc residualico.o -# Special target to create a win32 snapshot binary under Windows +# Special target to create a win32 snapshot binary win32dist: $(EXECUTABLE) mkdir -p $(WIN32PATH) $(STRIP) $(EXECUTABLE) -o $(WIN32PATH)/$(EXECUTABLE) cp $(DIST_FILES_THEMES) $(WIN32PATH) + cp $(DIST_FILES_ENGINEDATA) $(WIN32PATH) cp $(srcdir)/AUTHORS $(WIN32PATH)/AUTHORS.txt cp $(srcdir)/COPYING.LGPL $(WIN32PATH)/COPYING_LGPL.txt cp $(srcdir)/COPYING.GPL $(WIN32PATH)/COPYING_GPL.txt cp $(srcdir)/NEWS $(WIN32PATH)/NEWS.txt cp $(srcdir)/README $(WIN32PATH)/README.txt - cp $(srcdir)/TODO $(WIN32PATH)/TODO.txt cp /usr/local/README-SDL.txt $(WIN32PATH) cp /usr/local/bin/SDL.dll $(WIN32PATH) u2d $(WIN32PATH)/*.txt @@ -174,7 +171,6 @@ crosswin32dist: $(EXECUTABLE) cp $(srcdir)/COPYING.GPL ResidualWin32/COPYING_GPL.txt cp $(srcdir)/NEWS ResidualWin32/NEWS.txt cp $(srcdir)/README ResidualWin32/README.txt - cp $(srcdir)/TODO ResidualWin32/TODO.txt cp $(srcdir)/dists/residual.ini.example ResidualWin32 cp $(srcdir)/dists/residual.iss ResidualWin32 cp /usr/i586-mingw32msvc/README-SDL.txt ResidualWin32 @@ -191,36 +187,30 @@ crosswin32dist: $(EXECUTABLE) aos4dist: $(EXECUTABLE) mkdir -p $(AOS4PATH) $(STRIP) $(EXECUTABLE) -o $(AOS4PATH)/$(EXECUTABLE)_SVN - cp $(DIST_FILES_THEMES) $(AOS4PATH)/themes/ cp icons/residual.info $(AOS4PATH)/$(EXECUTABLE)_SVN.info + cp $(DIST_FILES_THEMES) $(AOS4PATH)/themes/ + cp $(DIST_FILES_ENGINEDATA) $(AOS4PATH)/extras/ cp $(srcdir)/AUTHORS $(AOS4PATH)/AUTHORS.txt cp $(srcdir)/COPYING.LGPL $(AOS4PATH)/COPYING.LGPL.txt cp $(srcdir)/COPYING.GPL $(AOS4PATH)/COPYING.GPL.txt cp $(srcdir)/NEWS $(AOS4PATH)/NEWS.txt cp $(srcdir)/README $(AOS4PATH)/README.txt - cp $(srcdir)/TODO $(AOS4PATH)/TODO.txt + +# Mark special targets as phony +.PHONY: deb bundle osxsnap win32dist install uninstall # -# Wii/Gamecube specific +# ARM specific # - -# Special target to create an Wii snapshot -wiidist: $(EXECUTABLE) - $(MKDIR) wiidist/residual -ifeq ($(GAMECUBE),1) - $(DEVKITPPC)/bin/elf2dol $(EXECUTABLE) wiidist/residual/residual.dol -else - $(STRIP) $(EXECUTABLE) -o wiidist/residual/boot.elf - $(CP) $(srcdir)/dists/wii/icon.png wiidist/residual/ - sed "s/@REVISION@/$(VER_SVNREV)/;s/@TIMESTAMP@/`date +%Y%m%d%H%M%S`/" < $(srcdir)/dists/wii/meta.xml > wiidist/residual/meta.xml +ifdef USE_TREMOLO +DEFINES += -DUSE_TREMOR -DUSE_VORBIS -DUSE_TREMOLO +LIBS += -ltremolo endif - $(CP) $(srcdir)/dists/wii/READMII wiidist/residual/ - $(CP) $(srcdir)/AUTHORS wiidist/residual/AUTHORS.txt - $(CP) $(srcdir)/COPYING.GPL wiidist/residual/COPYING.GPL.txt - $(CP) $(srcdir)/COPYING.LGPL wiidist/residual/COPYING.LGPL.txt - $(CP) $(srcdir)/NEWS wiidist/residual/NEWS.txt - $(CP) $(srcdir)/README wiidist/residual/README.txt - $(CP) $(DIST_FILES_THEMES) wiidist/residual/ - sed -i 's/$$/\r/' wiidist/residual/*.txt -.PHONY: deb bundle osxsnap win32dist crosswin32dist wiidist install uninstall +ifdef USE_ARM_SMUSH_ASM +DEFINES += -DUSE_ARM_SMUSH_ASM +endif + +ifdef USE_ARM_SOUND_ASM +DEFINES += -DUSE_ARM_SOUND_ASM +endif diff --git a/sound/audiocd.h b/sound/audiocd.h index fba4b1f7a0d..ca493e81352 100644 --- a/sound/audiocd.h +++ b/sound/audiocd.h @@ -67,6 +67,14 @@ private: friend class Common::Singleton; AudioCDManager(); + // FIXME: It might make sense to stop CD playback, when the AudioCDManager singleton + // is destroyed. Currently we can not do this, since in worst case the OSystem and + // along with it the Mixer will be destroyed before the AudioCDManager, thus + // leading to invalid memory access. If we can fix up the code to destroy the + // AudioCDManager before OSystem in *all* cases, that is including calling + // OSystem::quit, we might be able to implement it via a simple "stop()" + // call in a custom destructor of AudioCDManager. + /* used for emulated CD music */ SoundHandle _handle; bool _emulating; diff --git a/sound/audiostream.cpp b/sound/audiostream.cpp index ad257557ec1..3d6cad9bb71 100644 --- a/sound/audiostream.cpp +++ b/sound/audiostream.cpp @@ -173,6 +173,169 @@ int LinearMemoryStream::readBuffer(int16 *buf } + +#pragma mark - +#pragma mark --- LinearDiskStream --- +#pragma mark - + + + +/** + * LinearDiskStream. This can stream linear (PCM) audio from disk. The + * function takes an pointer to an array of LinearDiskStreamAudioBlock which defines the + * start position and length of each block of uncompressed audio in the stream. + */ +template +class LinearDiskStream : public AudioStream { + +// Allow backends to override buffer size +#ifdef CUSTOM_AUDIO_BUFFER_SIZE + static const int32 BUFFER_SIZE = CUSTOM_AUDIO_BUFFER_SIZE; +#else + static const int32 BUFFER_SIZE = 16384; +#endif + +protected: + byte* _buffer; ///< Streaming buffer + const byte *_ptr; ///< Pointer to current position in stream buffer + const int _rate; ///< Sample rate of stream + + int32 _playtime; ///< Calculated total play time + Common::SeekableReadStream *_stream; ///< Stream to read data from + int32 _filePos; ///< Current position in stream + int32 _diskLeft; ///< Samples left in stream in current block not yet read to buffer + int32 _bufferLeft; ///< Samples left in buffer in current block + bool _disposeAfterUse; ///< If true, delete stream object when LinearDiskStream is destructed + + LinearDiskStreamAudioBlock *_audioBlock; ///< Audio block list + int _audioBlockCount; ///< Number of blocks in _audioBlock + int _currentBlock; ///< Current audio block number + + int _beginLoop; ///< Loop start parameter + int _endLoop; ///< Loop end parameter, currently not implemented + bool _loop; ///< Determines if the stream should be looped when it finishes + +public: + LinearDiskStream(int rate, uint beginLoop, uint endLoop, bool disposeStream, Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, uint numBlocks, bool loop) + : _rate(rate), _stream(stream), _beginLoop(beginLoop), _endLoop(endLoop), _disposeAfterUse(disposeStream), + _audioBlockCount(numBlocks), _loop(loop) { + + // Allocate streaming buffer + if (is16Bit) { + _buffer = (byte *)malloc(BUFFER_SIZE * sizeof(int16)); + } else { + _buffer = (byte *)malloc(BUFFER_SIZE * sizeof(byte)); + } + + _ptr = _buffer; + _bufferLeft = 0; + + // Copy audio block data to our buffer + // TODO: Replace this with a Common::Array or Common::List to + // make it a little friendlier. + _audioBlock = new LinearDiskStreamAudioBlock[numBlocks]; + memcpy(_audioBlock, block, numBlocks * sizeof(LinearDiskStreamAudioBlock)); + + // Set current buffer state, playing first block + _currentBlock = 0; + _filePos = _audioBlock[_currentBlock].pos; + _diskLeft = _audioBlock[_currentBlock].len; + + // Add up length of all blocks in order to caluclate total play time + int len = 0; + for (int r = 0; r < _audioBlockCount; r++) { + len += _audioBlock[r].len; + } + _playtime = calculatePlayTime(rate, len / (is16Bit ? 2 : 1) / (stereo ? 2 : 1)); + } + + + virtual ~LinearDiskStream() { + if (_disposeAfterUse) { + delete _stream; + } + + delete[] _audioBlock; + free(_buffer); + } + int readBuffer(int16 *buffer, const int numSamples); + + bool isStereo() const { return stereo; } + bool endOfData() const { return (_currentBlock == _audioBlockCount - 1) && (_diskLeft == 0) && (_bufferLeft == 0); } + + int getRate() const { return _rate; } + int32 getTotalPlayTime() const { return _playtime; } +}; + +template +int LinearDiskStream::readBuffer(int16 *buffer, const int numSamples) { + int oldPos = _stream->pos(); + bool restoreFilePosition = false; + + int samples = numSamples; + + while (samples > 0 && ((_diskLeft > 0 || _bufferLeft > 0) || (_currentBlock != _audioBlockCount - 1)) ) { + + // Output samples in the buffer to the output + int len = MIN(samples, _bufferLeft); + samples -= len; + _bufferLeft -= len; + + while (len > 0) { + *buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE); + _ptr += (is16Bit ? 2 : 1); + len--; + } + + // Have we now finished this block? If so, read the next block + if ((_bufferLeft == 0) && (_diskLeft == 0) && (_currentBlock != _audioBlockCount - 1)) { + // Next block + _currentBlock++; + + _filePos = _audioBlock[_currentBlock].pos; + _diskLeft = _audioBlock[_currentBlock].len; + } + + // Now read more data from disk if there is more to be read + if ((_bufferLeft == 0) && (_diskLeft > 0)) { + int32 readAmount = MIN(_diskLeft, BUFFER_SIZE); + + _stream->seek(_filePos, SEEK_SET); + _stream->read(_buffer, readAmount * (is16Bit? 2: 1)); + + // Amount of data in buffer is now the amount read in, and + // the amount left to read on disk is decreased by the same amount + _bufferLeft = readAmount; + _diskLeft -= readAmount; + _ptr = (byte *)_buffer; + _filePos += readAmount * (is16Bit? 2: 1); + + // Set this flag now we've used the file, it restores it's + // original position. + restoreFilePosition = true; + } + + // Looping + if (_diskLeft == 0 && _loop) { + // Reset the stream + _currentBlock = 0; + _filePos = _audioBlock[_currentBlock].pos + _beginLoop; + _diskLeft = _audioBlock[_currentBlock].len; + } + } + + // In case calling code relies on the position of this stream staying + // constant, I restore the location if I've changed it. This is probably + // not necessary. + if (restoreFilePosition) { + _stream->seek(oldPos, SEEK_SET); + } + + return numSamples-samples; +} + + + #pragma mark - #pragma mark --- Input stream factory --- #pragma mark - @@ -202,6 +365,7 @@ AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte f const bool isLE = (flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0; const bool autoFree = (flags & Audio::Mixer::FLAG_AUTOFREE) != 0; + uint loopOffset = 0, loopLen = 0; if (flags & Audio::Mixer::FLAG_LOOP) { if (loopEnd == 0) @@ -236,6 +400,44 @@ AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte f } + + + +#define MAKE_LINEAR_DISK(STEREO, UNSIGNED) \ + if (is16Bit) { \ + if (isLE) \ + return new LinearDiskStream(rate, loopStart, loopEnd, takeOwnership, stream, block, numBlocks, loop); \ + else \ + return new LinearDiskStream(rate, loopStart, loopEnd, takeOwnership, stream, block, numBlocks, loop); \ + } else \ + return new LinearDiskStream(rate, loopStart, loopEnd, takeOwnership, stream, block, numBlocks, loop) + + +AudioStream *makeLinearDiskStream(Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, int numBlocks, int rate, byte flags, bool takeOwnership, uint loopStart, uint loopEnd) { + const bool isStereo = (flags & Audio::Mixer::FLAG_STEREO) != 0; + const bool is16Bit = (flags & Audio::Mixer::FLAG_16BITS) != 0; + const bool isUnsigned = (flags & Audio::Mixer::FLAG_UNSIGNED) != 0; + const bool isLE = (flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0; + const bool loop = (flags & Audio::Mixer::FLAG_LOOP) != 0; + + if (isStereo) { + if (isUnsigned) { + MAKE_LINEAR_DISK(true, true); + } else { + MAKE_LINEAR_DISK(true, false); + } + } else { + if (isUnsigned) { + MAKE_LINEAR_DISK(false, true); + } else { + MAKE_LINEAR_DISK(false, false); + } + } +} + + + + #pragma mark - #pragma mark --- Appendable audio stream --- #pragma mark - @@ -306,7 +508,6 @@ BaseAppendableMemoryStream::~BaseAppendableMemoryStream() { template int AppendableMemoryStream::readBuffer(int16 *buffer, const int numSamples) { Common::StackLock lock(_mutex); - int samples = numSamples; while (samples > 0 && !eosIntern()) { Buffer buf = *_bufferQueue.begin(); diff --git a/sound/audiostream.h b/sound/audiostream.h index 3d414d81ba6..74cea62e8d9 100644 --- a/sound/audiostream.h +++ b/sound/audiostream.h @@ -28,7 +28,7 @@ #include "common/util.h" #include "common/sys.h" - +#include "common/stream.h" namespace Audio { @@ -109,6 +109,7 @@ public: virtual int32 getTotalPlayTime() const { return kUnknownPlayTime; } }; + /** * Factory function for a raw linear AudioStream, which will simply treat all data * in the buffer described by ptr and len as raw sample data in the specified @@ -118,6 +119,23 @@ public: */ AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte flags, uint loopStart, uint loopEnd); + +/** Struct used to define the audio data to be played by a LinearDiskStream */ + +struct LinearDiskStreamAudioBlock { + int32 pos; ///< Position in stream of the block + int32 len; ///< Length of the block (in samples) +}; + + +/** Factory function for a Linear Disk Stream. This can stream linear (PCM) audio from disk. The + * function takes an pointer to an array of LinearDiskStreamAudioBlock which defines the + * start position and length of each block of uncompressed audio in the stream. + */ + +AudioStream *makeLinearDiskStream(Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, int + numBlocks, int rate, byte flags, bool disposeStream, uint loopStart, uint loopEnd); + /** * An audio stream to which additional data can be appended on-the-fly. * Used by SMUSH, iMuseDigital, the Kyrandia 3 VQA player, etc. diff --git a/sound/flac.cpp b/sound/flac.cpp index 3a46c114014..931219dfb0f 100644 --- a/sound/flac.cpp +++ b/sound/flac.cpp @@ -402,21 +402,23 @@ int FlacInputStream::readBuffer(int16 *buffer, const int numSamples) { } inline ::FLAC__SeekableStreamDecoderReadStatus FlacInputStream::callbackRead(FLAC__byte buffer[], FLAC_size_t *bytes) { - if (*bytes == 0) + if (*bytes == 0) { #ifdef LEGACY_FLAC return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; /* abort to avoid a deadlock */ #else return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */ #endif + } const uint32 bytesRead = _inStream->read(buffer, *bytes); - if (bytesRead == 0 && _inStream->ioFailed()) + if (bytesRead == 0) { #ifdef LEGACY_FLAC - return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; + return _inStream->eos() ? FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK : FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; #else - return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + return _inStream->eos() ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_ABORT; #endif + } *bytes = static_cast(bytesRead); #ifdef LEGACY_FLAC diff --git a/sound/vorbis.cpp b/sound/vorbis.cpp index 435dbbf455a..119306071df 100644 --- a/sound/vorbis.cpp +++ b/sound/vorbis.cpp @@ -60,7 +60,7 @@ static size_t read_stream_wrap(void *ptr, size_t size, size_t nmemb, void *datas static int seek_stream_wrap(void *datasource, ogg_int64_t offset, int whence) { Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource; - stream->seek(offset, whence); + stream->seek((int32)offset, whence); return stream->pos(); } @@ -132,7 +132,7 @@ public: } protected: - void refill(); + bool refill(); }; VorbisInputStream::VorbisInputStream(Common::SeekableReadStream *inStream, bool dispose, uint startTime, uint endTime, uint numLoops) : @@ -142,9 +142,12 @@ VorbisInputStream::VorbisInputStream(Common::SeekableReadStream *inStream, bool _totalNumLoops(numLoops), _bufferEnd(_buffer + ARRAYSIZE(_buffer)) { - bool err = (ov_open_callbacks(inStream, &_ovFile, NULL, 0, g_stream_wrap) < 0); - // FIXME: proper error handling! - assert(!err); + int res = ov_open_callbacks(inStream, &_ovFile, NULL, 0, g_stream_wrap); + if (res < 0) { + warning("Could not create Vorbis stream (%d)", res); + _pos = _bufferEnd; + return; + } #ifdef USE_TREMOR /* TODO: Symbian may have to use scumm_fixdfdi here? To quote: @@ -175,10 +178,16 @@ VorbisInputStream::VorbisInputStream(Common::SeekableReadStream *inStream, bool } // Seek to the start position - ov_time_seek(&_ovFile, _startTime); + res = ov_time_seek(&_ovFile, _startTime); + if (res < 0) { + warning("Error seeking in Vorbis stream (%d)", res); + _pos = _bufferEnd; + return; + } // Read in initial data - refill(); + if (!refill()) + return; // Setup some header information _isStereo = ov_info(&_ovFile, -1)->channels >= 2; @@ -192,7 +201,7 @@ VorbisInputStream::~VorbisInputStream() { } int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) { - int samples = 0; + int res, samples = 0; while (samples < numSamples && _pos < _bufferEnd) { const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos)); memcpy(buffer, _pos, len * 2); @@ -200,7 +209,9 @@ int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) { _pos += len; samples += len; if (_pos >= _bufferEnd) { - refill(); + if (!refill()) + break; + // If we are still out of data, and also past the end of specified // time range, check whether looping is enabled... if (_pos >= _bufferEnd && ov_time_tell(&_ovFile) >= _endTime) { @@ -208,8 +219,16 @@ int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) { // If looping is on and there are loops left, rewind to the start if (_numLoops != 0) _numLoops--; - ov_time_seek(&_ovFile, _startTime); - refill(); + + res = ov_time_seek(&_ovFile, _startTime); + if (res < 0) { + warning("Error seeking in Vorbis stream (%d)", res); + _pos = _bufferEnd; + break; + } + + if (!refill()) + break; } } } @@ -217,8 +236,9 @@ int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) { return samples; } -void VorbisInputStream::refill() { +bool VorbisInputStream::refill() { // Read the samples + int res; uint len_left = sizeof(_buffer); char *read_pos = (char *)_buffer; @@ -229,7 +249,12 @@ void VorbisInputStream::refill() { break; // Last loop, abort if (_numLoops != 0) _numLoops--; - ov_time_seek(&_ovFile, _startTime); + res = ov_time_seek(&_ovFile, _startTime); + if (res < 0) { + warning("Error seeking in Vorbis stream (%d)", res); + _pos = _bufferEnd; + return false; + } } long result; @@ -257,13 +282,16 @@ void VorbisInputStream::refill() { if (result == OV_HOLE) { // Possibly recoverable, just warn about it warning("Corrupted data in Vorbis file"); - } else if (result <= 0) { - if (result < 0) - debug(1, "Decode error %ld in Vorbis file", result); + } else if (result == 0) { + warning("End of file while reading from Vorbis file"); + _pos = _bufferEnd; + return false; + } else if (result < 0) { + warning("Error reading from Vorbis stream (%d)", int(result)); + _pos = _bufferEnd; // Don't delete it yet, that causes problems in // the CD player emulation code. - memset(read_pos, 0, len_left); - break; + return false; } else { len_left -= result; read_pos += result; @@ -272,6 +300,8 @@ void VorbisInputStream::refill() { _pos = _buffer; _bufferEnd = (int16 *)read_pos; + + return true; } @@ -289,7 +319,14 @@ AudioStream *makeVorbisStream( uint32 endTime = duration ? (startTime + duration) : 0; - return new VorbisInputStream(stream, disposeAfterUse, startTime, endTime, numLoops); + VorbisInputStream *input = new VorbisInputStream(stream, disposeAfterUse, startTime, endTime, numLoops); + + if (input->endOfData()) { + delete input; + return 0; + } + + return input; }