diff --git a/.gitignore b/.gitignore index f117c76f933..0885c70cb1a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,9 +23,12 @@ lib*.a /.project /.cproject /.settings +/.autotools /Icon.* /build +/staging + /dists/codeblocks/*.cbp /dists/codeblocks/*.depend /dists/codeblocks/*.layout @@ -43,6 +46,7 @@ project.xcworkspace /dists/msvc*/[Dd]ebug*/ /dists/msvc*/[Rr]elease*/ +/dists/msvc*/[Aa]nalysis*/ /dists/msvc*/*.lib /dists/msvc*/*.SAV /dists/msvc*/*.dat @@ -87,3 +91,9 @@ ipch/ #Ignore default Visual Studio build folders [Dd]ebug*/ [Rr]elease*/ + +#Ignore Qt Creator project files +Residual.config +Residual.creator +Residual.files +Residual.includes diff --git a/audio/mididrv.cpp b/audio/mididrv.cpp index 80981eeccb2..b71c02f9911 100644 --- a/audio/mididrv.cpp +++ b/audio/mididrv.cpp @@ -219,8 +219,6 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) { } } - reslt = 0; - // If the selected driver did not match the flags setting, // we try to determine a suitable and "optimal" music driver. const MusicPlugin::List p = MusicMan.getPlugins(); @@ -231,7 +229,7 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) { if ((flags & MDT_MIDI) && !skipMidi) { // If a preferred MT32 or GM device has been selected that device gets returned if available. Common::String devStr; - if (flags & MDT_PREFER_MT32) + if (flags & MDT_PREFER_MT32) devStr = ConfMan.get("mt32_device"); else if (flags & MDT_PREFER_GM) devStr = ConfMan.get("gm_device"); @@ -263,12 +261,11 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) { // If the preferred (expressly requested) device cannot be used we display a warning and continue. // Don't warn about the failing device if we did already (this becomes relevant if the failing // device is selected as preferred device and also as GM or MT-32 device). - if (failedDevStr != getDeviceString(hdl, MidiDriver::kDeviceName)) { + if (failedDevStr != getDeviceString(hdl, MidiDriver::kDeviceName)) { Common::String warningMsg = Common::String::format(_("The preferred audio device '%s' cannot be used. See log file for more information. Attempting to fall back to the next available device..."), getDeviceString(hdl, MidiDriver::kDeviceName).c_str()); GUI::MessageDialog dialog(warningMsg); dialog.runModal(); } - hdl = 0; } } @@ -283,15 +280,12 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) { hdl = d->getHandle(); if (checkDevice(hdl)) return hdl; - else - // No warning here, since the user hasn't expressly requested anything. - hdl = 0; } } } } - // Now we default to the first available device with music type 'MT_GM' if not + // Now we default to the first available device with music type 'MT_GM' if not // MT-32 is preferred or if MT-32 is preferred but all other devices have failed. if (!(flags & MDT_PREFER_MT32) || flags == (MDT_PREFER_MT32 | MDT_MIDI)) { for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) { @@ -301,9 +295,6 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) { hdl = d->getHandle(); if (checkDevice(hdl)) return hdl; - else - // No warning here, since the user hasn't expressly requested anything. - hdl = 0; } } } @@ -357,15 +348,12 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) { hdl = d->getHandle(); if (checkDevice(hdl)) return hdl; - else - // No warning here, since the user hasn't expressly requested anything. - hdl = 0; } } } } - return reslt; + return 0; } MidiDriver *MidiDriver::createMidi(MidiDriver::DeviceHandle handle) { diff --git a/audio/mixer.cpp b/audio/mixer.cpp index fb4fffb8d8a..128224ae85a 100644 --- a/audio/mixer.cpp +++ b/audio/mixer.cpp @@ -93,6 +93,13 @@ public: */ void setVolume(const byte volume); + /** + * Gets the channel's own volume. + * + * @return volume + */ + byte getVolume(); + /** * Sets the channel's balance setting. * @@ -100,6 +107,13 @@ public: */ void setBalance(const int8 balance); + /** + * Gets the channel's balance setting. + * + * @return balance + */ + int8 getBalance(); + /** * Notifies the channel that the global sound type * volume settings changed. @@ -342,6 +356,14 @@ void MixerImpl::setChannelVolume(SoundHandle handle, byte volume) { _channels[index]->setVolume(volume); } +byte MixerImpl::getChannelVolume(SoundHandle handle) { + const int index = handle._val % NUM_CHANNELS; + if (!_channels[index] || _channels[index]->getHandle()._val != handle._val) + return 0; + + return _channels[index]->getVolume(); +} + void MixerImpl::setChannelBalance(SoundHandle handle, int8 balance) { Common::StackLock lock(_mutex); @@ -352,6 +374,14 @@ void MixerImpl::setChannelBalance(SoundHandle handle, int8 balance) { _channels[index]->setBalance(balance); } +int8 MixerImpl::getChannelBalance(SoundHandle handle) { + const int index = handle._val % NUM_CHANNELS; + if (!_channels[index] || _channels[index]->getHandle()._val != handle._val) + return 0; + + return _channels[index]->getBalance(); +} + uint32 MixerImpl::getSoundElapsedTime(SoundHandle handle) { return getElapsedTime(handle).msecs(); } @@ -482,11 +512,19 @@ void Channel::setVolume(const byte volume) { updateChannelVolumes(); } +byte Channel::getVolume() { + return _volume; +} + void Channel::setBalance(const int8 balance) { _balance = balance; updateChannelVolumes(); } +int8 Channel::getBalance() { + return _balance; +} + void Channel::updateChannelVolumes() { // From the channel balance/volume and the global volume, we compute // the effective volume for the left and right channel. Note the diff --git a/audio/mixer.h b/audio/mixer.h index 1fbe2654889..de709e13fef 100644 --- a/audio/mixer.h +++ b/audio/mixer.h @@ -210,6 +210,14 @@ public: */ virtual void setChannelVolume(SoundHandle handle, byte volume) = 0; + /** + * Get the channel volume for the given handle. + * + * @param handle the sound to affect + * @return channel volume + */ + virtual byte getChannelVolume(SoundHandle handle) = 0; + /** * Set the channel balance for the given handle. * @@ -219,6 +227,14 @@ public: */ virtual void setChannelBalance(SoundHandle handle, int8 balance) = 0; + /** + * Get the channel balance for the given handle. + * + * @param handle the sound to affect + * @return channel balance + */ + virtual int8 getChannelBalance(SoundHandle handle) = 0; + /** * Get approximation of for how long the channel has been playing. */ diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h index 39ee33a97cf..f3306a83cae 100644 --- a/audio/mixer_intern.h +++ b/audio/mixer_intern.h @@ -105,7 +105,9 @@ public: virtual bool isSoundTypeMuted(SoundType type) const; virtual void setChannelVolume(SoundHandle handle, byte volume); + virtual byte getChannelVolume(SoundHandle handle); virtual void setChannelBalance(SoundHandle handle, int8 balance); + virtual int8 getChannelBalance(SoundHandle handle); virtual uint32 getSoundElapsedTime(SoundHandle handle); virtual Timestamp getElapsedTime(SoundHandle handle); diff --git a/audio/mpu401.cpp b/audio/mpu401.cpp index caad945258f..966e367a934 100644 --- a/audio/mpu401.cpp +++ b/audio/mpu401.cpp @@ -33,7 +33,10 @@ void MidiChannel_MPU401::init(MidiDriver *owner, byte channel) { bool MidiChannel_MPU401::allocate() { if (_allocated) return false; - return (_allocated = true); + + _allocated = true; + + return true; } MidiDriver *MidiChannel_MPU401::device() { diff --git a/audio/rate.cpp b/audio/rate.cpp index 83abd6150b6..0fc23a8a544 100644 --- a/audio/rate.cpp +++ b/audio/rate.cpp @@ -298,6 +298,9 @@ public: _bufferSize = osamp; } + if (!_buffer) + error("[CopyRateConverter::flow] Cannot allocate memory for temp buffer"); + // Read up to 'osamp' samples into our temporary buffer len = input.readBuffer(_buffer, osamp); diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp index 5f278c4029a..7aa0b91627c 100644 --- a/audio/softsynth/mt32.cpp +++ b/audio/softsynth/mt32.cpp @@ -44,6 +44,7 @@ #include "graphics/fontman.h" #include "graphics/surface.h" #include "graphics/pixelformat.h" +//#include "graphics/palette.h" #include "graphics/font.h" class MidiChannel_MT32 : public MidiChannel_MPU401 { @@ -570,8 +571,8 @@ bool MT32EmuMusicPlugin::checkDevice(MidiDriver::DeviceHandle) const { warning("The MT-32 emulator requires one of the two following file sets (not bundled with ScummVM):\n Either 'MT32_CONTROL.ROM' and 'MT32_PCM.ROM' or 'CM32L_CONTROL.ROM' and 'CM32L_PCM.ROM'"); return false; } - - return true; + + return true; } Common::Error MT32EmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp index 74699ba4c63..15e869ba33f 100644 --- a/audio/softsynth/opl/mame.cpp +++ b/audio/softsynth/opl/mame.cpp @@ -725,6 +725,8 @@ static int OPLOpenTable(void) { ENV_CURVE = (int *)malloc(sizeof(int) * (2*EG_ENT+1)); + if (!ENV_CURVE) + error("[OPLOpenTable] Cannot allocate memory"); /* envelope counter -> envelope output table */ for (i=0; i < EG_ENT; i++) { diff --git a/backends/audiocd/audiocd.h b/backends/audiocd/audiocd.h index 92b7598cb5d..0afc6af991c 100644 --- a/backends/audiocd/audiocd.h +++ b/backends/audiocd/audiocd.h @@ -113,7 +113,7 @@ public: * @return true if the CD drive was inited succesfully */ virtual bool openCD(int drive) = 0; - + /** * Poll CD status. * @return true if CD audio is playing diff --git a/backends/base-backend.cpp b/backends/base-backend.cpp new file mode 100644 index 00000000000..d919c7a1781 --- /dev/null +++ b/backends/base-backend.cpp @@ -0,0 +1,51 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "backends/base-backend.h" + +#ifndef DISABLE_DEFAULT_EVENT_MANAGER +#include "backends/events/default/default-events.h" +#endif + +#ifndef DISABLE_DEFAULT_AUDIOCD_MANAGER +#include "backends/audiocd/default/default-audiocd.h" +#endif + + +void BaseBackend::initBackend() { + // Init Event manager +#ifndef DISABLE_DEFAULT_EVENT_MANAGER + if (!_eventManager) + _eventManager = new DefaultEventManager(getDefaultEventSource()); +#endif + + // Init audio CD manager +#ifndef DISABLE_DEFAULT_AUDIOCD_MANAGER + if (!_audiocdManager) + _audiocdManager = new DefaultAudioCDManager(); +#endif + + OSystem::initBackend(); +} diff --git a/backends/base-backend.h b/backends/base-backend.h new file mode 100644 index 00000000000..9efe6ac6ecc --- /dev/null +++ b/backends/base-backend.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef BACKENDS_BASE_BACKEND_H +#define BACKENDS_BASE_BACKEND_H + +#include "common/system.h" +#include "common/events.h" + +class BaseBackend : public OSystem { +protected: + virtual Common::EventSource *getDefaultEventSource() = 0; +public: + virtual void initBackend(); +}; + +class EventsBaseBackend : public BaseBackend, Common::EventSource { +protected: + virtual Common::EventSource *getDefaultEventSource() { return this; } +public: +}; + + +#endif diff --git a/backends/events/default/default-events.cpp b/backends/events/default/default-events.cpp index bb74933596c..78072b00211 100644 --- a/backends/events/default/default-events.cpp +++ b/backends/events/default/default-events.cpp @@ -26,6 +26,7 @@ #include "common/system.h" #include "common/config-manager.h" +#include "common/translation.h" #include "backends/events/default/default-events.h" #include "backends/keymapper/keymapper.h" #include "backends/keymapper/remap-dialog.h" @@ -218,7 +219,7 @@ bool DefaultEventManager::pollEvent(Common::Event &event) { if (ConfMan.getBool("confirm_exit")) { if (g_engine) g_engine->pauseEngine(true); - GUI::MessageDialog alert("Do you really want to return to the Launcher?", "Launcher", "Cancel"); + GUI::MessageDialog alert(_("Do you really want to return to the Launcher?"), _("Launcher"), _("Cancel")); result = _shouldRTL = (alert.runModal() == GUI::kMessageOK); if (g_engine) g_engine->pauseEngine(false); @@ -240,7 +241,7 @@ bool DefaultEventManager::pollEvent(Common::Event &event) { _confirmExitDialogActive = true; if (g_engine) g_engine->pauseEngine(true); - GUI::MessageDialog alert("Do you really want to quit?", "Quit", "Cancel"); + GUI::MessageDialog alert(_("Do you really want to quit?"), _("Quit"), _("Cancel")); result = _shouldQuit = (alert.runModal() == GUI::kMessageOK); if (g_engine) g_engine->pauseEngine(false); diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp index 79811f10620..9b0ffcc102f 100644 --- a/backends/events/sdl/sdl-events.cpp +++ b/backends/events/sdl/sdl-events.cpp @@ -208,7 +208,6 @@ bool SdlEventSource::pollEvent(Common::Event &event) { }*/ SDL_Event ev; - ev.type = SDL_NOEVENT; while (SDL_PollEvent(&ev)) { preprocessEvents(&ev); if (dispatchSDLEvent(ev, event)) @@ -306,7 +305,7 @@ bool SdlEventSource::handleKeyDown(SDL_Event &ev, Common::Event &event) { event.type = Common::EVENT_KEYDOWN; event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym; - event.kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode); + event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, (Uint16)ev.key.keysym.unicode); return true; } @@ -350,7 +349,7 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) { event.type = Common::EVENT_KEYUP; event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym; - event.kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode); + event.kbd.ascii = mapKey(ev.key.keysym.sym, (SDLMod)ev.key.keysym.mod, (Uint16)ev.key.keysym.unicode); // Ctrl-Alt- will change the GFX mode SDLModToOSystemKeyFlags(mod, event); @@ -420,19 +419,19 @@ bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) { switch (ev.jbutton.button) { case JOY_BUT_ESCAPE: event.kbd.keycode = Common::KEYCODE_ESCAPE; - event.kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0); + event.kbd.ascii = mapKey(SDLK_ESCAPE, (SDLMod)ev.key.keysym.mod, 0); break; case JOY_BUT_PERIOD: event.kbd.keycode = Common::KEYCODE_PERIOD; - event.kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0); + event.kbd.ascii = mapKey(SDLK_PERIOD, (SDLMod)ev.key.keysym.mod, 0); break; case JOY_BUT_SPACE: event.kbd.keycode = Common::KEYCODE_SPACE; - event.kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0); + event.kbd.ascii = mapKey(SDLK_SPACE, (SDLMod)ev.key.keysym.mod, 0); break; case JOY_BUT_F5: event.kbd.keycode = Common::KEYCODE_F5; - event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0); + event.kbd.ascii = mapKey(SDLK_F5, (SDLMod)ev.key.keysym.mod, 0); break; } } @@ -451,19 +450,19 @@ bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) { switch (ev.jbutton.button) { case JOY_BUT_ESCAPE: event.kbd.keycode = Common::KEYCODE_ESCAPE; - event.kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0); + event.kbd.ascii = mapKey(SDLK_ESCAPE, (SDLMod)ev.key.keysym.mod, 0); break; case JOY_BUT_PERIOD: event.kbd.keycode = Common::KEYCODE_PERIOD; - event.kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0); + event.kbd.ascii = mapKey(SDLK_PERIOD, (SDLMod)ev.key.keysym.mod, 0); break; case JOY_BUT_SPACE: event.kbd.keycode = Common::KEYCODE_SPACE; - event.kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0); + event.kbd.ascii = mapKey(SDLK_SPACE, (SDLMod)ev.key.keysym.mod, 0); break; case JOY_BUT_F5: event.kbd.keycode = Common::KEYCODE_F5; - event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0); + event.kbd.ascii = mapKey(SDLK_F5, (SDLMod)ev.key.keysym.mod, 0); break; } } diff --git a/backends/events/sdl/sdl-events.h b/backends/events/sdl/sdl-events.h index 227d6e1607a..805b76b108a 100644 --- a/backends/events/sdl/sdl-events.h +++ b/backends/events/sdl/sdl-events.h @@ -20,19 +20,19 @@ * */ -#if !defined(BACKEND_EVENTS_SDL_H) && !defined(DISABLE_DEFAULT_EVENTMANAGER) +#ifndef BACKEND_EVENTS_SDL_H #define BACKEND_EVENTS_SDL_H -#include "backends/events/default/default-events.h" - #include "backends/platform/sdl/sdl-sys.h" +#include "common/events.h" + /** * The SDL event source. */ class SdlEventSource : public Common::EventSource { -public: +public: SdlEventSource(); virtual ~SdlEventSource(); @@ -69,7 +69,7 @@ protected: /** Scroll lock state - since SDL doesn't track it */ bool _scrollLock; - + /** Joystick */ SDL_Joystick *_joystick; diff --git a/backends/fs/posix/posix-fs-factory.cpp b/backends/fs/posix/posix-fs-factory.cpp index 829355be843..776ea861555 100644 --- a/backends/fs/posix/posix-fs-factory.cpp +++ b/backends/fs/posix/posix-fs-factory.cpp @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#if defined(POSIX) +#if defined(POSIX) || defined(PLAYSTATION3) // Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h. // Also with clock() in sys/time.h in some Mac OS X SDKs. diff --git a/backends/fs/posix/posix-fs-factory.h b/backends/fs/posix/posix-fs-factory.h index a7075db94ce..c7cec6fab59 100644 --- a/backends/fs/posix/posix-fs-factory.h +++ b/backends/fs/posix/posix-fs-factory.h @@ -30,6 +30,7 @@ * Parts of this class are documented in the base interface class, FilesystemFactory. */ class POSIXFilesystemFactory : public FilesystemFactory { +protected: virtual AbstractFSNode *makeRootFileNode() const; virtual AbstractFSNode *makeCurrentDirectoryFileNode() const; virtual AbstractFSNode *makeFileNodePath(const Common::String &path) const; diff --git a/backends/fs/posix/posix-fs.cpp b/backends/fs/posix/posix-fs.cpp index 0b94c37b165..320c5a6f39f 100644 --- a/backends/fs/posix/posix-fs.cpp +++ b/backends/fs/posix/posix-fs.cpp @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#if defined(POSIX) +#if defined(POSIX) || defined(PLAYSTATION3) // Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h. // Also with clock() in sys/time.h in some Mac OS X SDKs. diff --git a/backends/fs/windows/windows-fs.cpp b/backends/fs/windows/windows-fs.cpp index 9e864753c92..c32ad2da943 100644 --- a/backends/fs/windows/windows-fs.cpp +++ b/backends/fs/windows/windows-fs.cpp @@ -54,7 +54,7 @@ bool WindowsFilesystemNode::isWritable() const { return _access(_path.c_str(), W_OK) == 0; } -void WindowsFilesystemNode::addFile(AbstractFSList &list, ListMode mode, const char *base, WIN32_FIND_DATA* find_data) { +void WindowsFilesystemNode::addFile(AbstractFSList &list, ListMode mode, const char *base, bool hidden, WIN32_FIND_DATA* find_data) { WindowsFilesystemNode entry; char *asciiName = toAscii(find_data->cFileName); bool isDirectory; @@ -63,6 +63,10 @@ void WindowsFilesystemNode::addFile(AbstractFSList &list, ListMode mode, const c if (!strcmp(asciiName, ".") || !strcmp(asciiName, "..")) return; + // Skip hidden files if asked + if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) && !hidden) + return; + isDirectory = (find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? true : false); if ((!isDirectory && mode == Common::FSNode::kListDirectoriesOnly) || @@ -163,8 +167,6 @@ AbstractFSNode *WindowsFilesystemNode::getChild(const Common::String &n) const { bool WindowsFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const { assert(_isDirectory); - //TODO: honor the hidden flag - if (_isPseudoRoot) { #ifndef _WIN32_WCE // Drives enumeration @@ -200,10 +202,10 @@ bool WindowsFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, b if (handle == INVALID_HANDLE_VALUE) return false; - addFile(myList, mode, _path.c_str(), &desc); + addFile(myList, mode, _path.c_str(), hidden, &desc); while (FindNextFile(handle, &desc)) - addFile(myList, mode, _path.c_str(), &desc); + addFile(myList, mode, _path.c_str(), hidden, &desc); FindClose(handle); } diff --git a/backends/fs/windows/windows-fs.h b/backends/fs/windows/windows-fs.h index 071b767f6aa..351307bef0e 100644 --- a/backends/fs/windows/windows-fs.h +++ b/backends/fs/windows/windows-fs.h @@ -37,7 +37,6 @@ #include #include #include -#undef ARRAYSIZE /** * Implementation of the ScummVM file system API based on Windows API. @@ -94,12 +93,13 @@ private: * Adds a single WindowsFilesystemNode to a given list. * This method is used by getChildren() to populate the directory entries list. * - * @param list List to put the file entry node in. - * @param mode Mode to use while adding the file entry to the list. - * @param base Common::String with the directory being listed. - * @param find_data Describes a file that the FindFirstFile, FindFirstFileEx, or FindNextFile functions find. + * @param list List to put the file entry node in. + * @param mode Mode to use while adding the file entry to the list. + * @param base Common::String with the directory being listed. + * @param hidden true if hidden files should be added, false otherwise + * @param find_data Describes a file that the FindFirstFile, FindFirstFileEx, or FindNextFile functions find. */ - static void addFile(AbstractFSList &list, ListMode mode, const char *base, WIN32_FIND_DATA* find_data); + static void addFile(AbstractFSList &list, ListMode mode, const char *base, bool hidden, WIN32_FIND_DATA* find_data); /** * Converts a Unicode string to Ascii format. diff --git a/backends/graphics/graphics.h b/backends/graphics/graphics.h index 566b208e8c5..978cf2a16c8 100644 --- a/backends/graphics/graphics.h +++ b/backends/graphics/graphics.h @@ -27,6 +27,8 @@ #include "common/noncopyable.h" #include "common/keyboard.h" +//#include "graphics/palette.h" + /** * Abstract class for graphics manager. Subclasses * implement the real functionality. diff --git a/backends/graphics/sdl/sdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp similarity index 89% rename from backends/graphics/sdl/sdl-graphics.cpp rename to backends/graphics/surfacesdl/surfacesdl-graphics.cpp index 1b2b973b06e..79c033ebe1c 100644 --- a/backends/graphics/sdl/sdl-graphics.cpp +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp @@ -24,7 +24,7 @@ #if defined(SDL_BACKEND) -#include "backends/graphics/sdl/sdl-graphics.h" +#include "backends/graphics/surfacesdl/surfacesdl-graphics.h" #include "backends/events/sdl/sdl-events.h" #include "backends/platform/sdl/sdl.h" #include "common/config-manager.h" @@ -40,7 +40,7 @@ #include "graphics/scaler.h" #include "graphics/surface.h" -SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *sdlEventSource) +SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource) : _sdlEventSource(sdlEventSource), _screen(0), @@ -69,18 +69,18 @@ SdlGraphicsManager::SdlGraphicsManager(SdlEventSource *sdlEventSource) #endif } -SdlGraphicsManager::~SdlGraphicsManager() { +SurfaceSdlGraphicsManager::~SurfaceSdlGraphicsManager() { // Unregister the event observer if (g_system->getEventManager()->getEventDispatcher() != NULL) g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this); } -void SdlGraphicsManager::initEventObserver() { +void SurfaceSdlGraphicsManager::initEventObserver() { // Register the graphics manager as a event observer g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 10, false); } -bool SdlGraphicsManager::hasFeature(OSystem::Feature f) { +bool SurfaceSdlGraphicsManager::hasFeature(OSystem::Feature f) { return #ifdef USE_OPENGL (f == OSystem::kFeatureOpenGL); @@ -89,19 +89,19 @@ bool SdlGraphicsManager::hasFeature(OSystem::Feature f) { #endif } -void SdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { +void SurfaceSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { } -bool SdlGraphicsManager::getFeatureState(OSystem::Feature f) { +bool SurfaceSdlGraphicsManager::getFeatureState(OSystem::Feature f) { return false; } -void SdlGraphicsManager::launcherInitSize(uint w, uint h) { +void SurfaceSdlGraphicsManager::launcherInitSize(uint w, uint h) { closeOverlay(); setupScreen(w, h, false, false); } -byte *SdlGraphicsManager::setupScreen(int screenW, int screenH, bool fullscreen, bool accel3d) { +byte *SurfaceSdlGraphicsManager::setupScreen(int screenW, int screenH, bool fullscreen, bool accel3d) { uint32 sdlflags; int bpp; @@ -220,7 +220,7 @@ byte *SdlGraphicsManager::setupScreen(int screenW, int screenH, bool fullscreen, #define BITMAP_TEXTURE_SIZE 256 -void SdlGraphicsManager::updateScreen() { +void SurfaceSdlGraphicsManager::updateScreen() { #ifdef USE_OPENGL if (_opengl) { if (_overlayVisible) { @@ -324,11 +324,11 @@ void SdlGraphicsManager::updateScreen() { } } -int16 SdlGraphicsManager::getHeight() { +int16 SurfaceSdlGraphicsManager::getHeight() { return _screen->h; } -int16 SdlGraphicsManager::getWidth() { +int16 SurfaceSdlGraphicsManager::getWidth() { return _screen->w; } @@ -337,7 +337,7 @@ int16 SdlGraphicsManager::getWidth() { #pragma mark --- Overlays --- #pragma mark - -void SdlGraphicsManager::showOverlay() { +void SurfaceSdlGraphicsManager::showOverlay() { if (_overlayVisible) return; @@ -346,7 +346,7 @@ void SdlGraphicsManager::showOverlay() { clearOverlay(); } -void SdlGraphicsManager::hideOverlay() { +void SurfaceSdlGraphicsManager::hideOverlay() { if (!_overlayVisible) return; @@ -356,7 +356,7 @@ void SdlGraphicsManager::hideOverlay() { clearOverlay(); } -void SdlGraphicsManager::clearOverlay() { +void SurfaceSdlGraphicsManager::clearOverlay() { if (!_overlayVisible) return; @@ -385,7 +385,7 @@ void SdlGraphicsManager::clearOverlay() { _overlayDirty = true; } -void SdlGraphicsManager::grabOverlay(OverlayColor *buf, int pitch) { +void SurfaceSdlGraphicsManager::grabOverlay(OverlayColor *buf, int pitch) { if (_overlayscreen == NULL) return; @@ -403,7 +403,7 @@ void SdlGraphicsManager::grabOverlay(OverlayColor *buf, int pitch) { SDL_UnlockSurface(_overlayscreen); } -void SdlGraphicsManager::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) { +void SurfaceSdlGraphicsManager::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) { if (_overlayscreen == NULL) return; @@ -444,7 +444,7 @@ void SdlGraphicsManager::copyRectToOverlay(const OverlayColor *buf, int pitch, i SDL_UnlockSurface(_overlayscreen); } -void SdlGraphicsManager::closeOverlay() { +void SurfaceSdlGraphicsManager::closeOverlay() { if (_overlayscreen) { SDL_FreeSurface(_overlayscreen); _overlayscreen = NULL; @@ -462,16 +462,16 @@ void SdlGraphicsManager::closeOverlay() { #pragma mark --- Mouse --- #pragma mark - -bool SdlGraphicsManager::showMouse(bool visible) { +bool SurfaceSdlGraphicsManager::showMouse(bool visible) { SDL_ShowCursor(visible); return true; } -void SdlGraphicsManager::warpMouse(int x, int y) { +void SurfaceSdlGraphicsManager::warpMouse(int x, int y) { SDL_WarpMouse(x, y); } -bool SdlGraphicsManager::notifyEvent(const Common::Event &event) { +bool SurfaceSdlGraphicsManager::notifyEvent(const Common::Event &event) { return false; } diff --git a/backends/graphics/sdl/sdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h similarity index 94% rename from backends/graphics/sdl/sdl-graphics.h rename to backends/graphics/surfacesdl/surfacesdl-graphics.h index 5668c7ef144..f5b058d9aa1 100644 --- a/backends/graphics/sdl/sdl-graphics.h +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h @@ -42,10 +42,10 @@ /** * SDL graphics manager */ -class SdlGraphicsManager : public GraphicsManager, public Common::EventObserver { +class SurfaceSdlGraphicsManager : public GraphicsManager, public Common::EventObserver { public: - SdlGraphicsManager(SdlEventSource *sdlEventSource); - virtual ~SdlGraphicsManager(); + SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource); + virtual ~SurfaceSdlGraphicsManager(); virtual void initEventObserver(); diff --git a/backends/mixer/doublebuffersdl/doublebuffersdl-mixer.cpp b/backends/mixer/doublebuffersdl/doublebuffersdl-mixer.cpp index 526a01d1bff..3e5b9940e06 100644 --- a/backends/mixer/doublebuffersdl/doublebuffersdl-mixer.cpp +++ b/backends/mixer/doublebuffersdl/doublebuffersdl-mixer.cpp @@ -28,7 +28,7 @@ DoubleBufferSDLMixerManager::DoubleBufferSDLMixerManager() : _soundMutex(0), _soundCond(0), _soundThread(0), _soundThreadIsRunning(false), _soundThreadShouldQuit(false) { - + } DoubleBufferSDLMixerManager::~DoubleBufferSDLMixerManager() { @@ -45,7 +45,7 @@ void DoubleBufferSDLMixerManager::startAudio() { // Create two sound buffers _activeSoundBuf = 0; - uint bufSize = _obtainedRate.samples * 4; + uint bufSize = _obtained.samples * 4; _soundBufSize = bufSize; _soundBuffers[0] = (byte *)calloc(1, bufSize); _soundBuffers[1] = (byte *)calloc(1, bufSize); diff --git a/backends/mixer/sdl/sdl-mixer.cpp b/backends/mixer/sdl/sdl-mixer.cpp index 16e7f22db5c..001309a7770 100644 --- a/backends/mixer/sdl/sdl-mixer.cpp +++ b/backends/mixer/sdl/sdl-mixer.cpp @@ -61,18 +61,38 @@ void SdlMixerManager::init() { // Get the desired audio specs SDL_AudioSpec desired = getAudioSpec(SAMPLES_PER_SEC); + // Needed as SDL_OpenAudio as of SDL-1.2.14 mutates fields in + // "desired" if used directly. + SDL_AudioSpec fmt = desired; + // Start SDL audio with the desired specs - if (SDL_OpenAudio(&desired, &_obtainedRate) != 0) { + if (SDL_OpenAudio(&fmt, &_obtained) != 0) { warning("Could not open audio device: %s", SDL_GetError()); _mixer = new Audio::MixerImpl(g_system, desired.freq); - assert(_mixer); + assert(_mixer); _mixer->setReady(false); } else { - debug(1, "Output sample rate: %d Hz", _obtainedRate.freq); + debug(1, "Output sample rate: %d Hz", _obtained.freq); + if (_obtained.freq != desired.freq) + warning("SDL mixer output sample rate: %d differs from desired: %d", _obtained.freq, desired.freq); - _mixer = new Audio::MixerImpl(g_system, _obtainedRate.freq); - assert(_mixer); + debug(1, "Output buffer size: %d samples", _obtained.samples); + if (_obtained.samples != desired.samples) + warning("SDL mixer output buffer size: %d differs from desired: %d", _obtained.samples, desired.samples); + + if (_obtained.format != desired.format) + warning("SDL mixer sound format: %d differs from desired: %d", _obtained.format, desired.format); + +#ifndef __SYMBIAN32__ + // The SymbianSdlMixerManager does stereo->mono downmixing, + // but otherwise we require stereo output. + if (_obtained.channels != 2) + error("SDL mixer output requires stereo output device"); +#endif + + _mixer = new Audio::MixerImpl(g_system, _obtained.freq); + assert(_mixer); _mixer->setReady(true); startAudio(); @@ -133,7 +153,7 @@ void SdlMixerManager::suspendAudio() { int SdlMixerManager::resumeAudio() { if (!_audioSuspended) return -2; - if (SDL_OpenAudio(&_obtainedRate, NULL) < 0){ + if (SDL_OpenAudio(&_obtained, NULL) < 0){ return -1; } SDL_PauseAudio(0); diff --git a/backends/mixer/sdl/sdl-mixer.h b/backends/mixer/sdl/sdl-mixer.h index 5590c90ab3e..6fee26bd1fe 100644 --- a/backends/mixer/sdl/sdl-mixer.h +++ b/backends/mixer/sdl/sdl-mixer.h @@ -67,13 +67,13 @@ protected: * The obtained audio specification after opening the * audio system. */ - SDL_AudioSpec _obtainedRate; + SDL_AudioSpec _obtained; /** State of the audio system */ bool _audioSuspended; /** - * Returns the desired audio specification + * Returns the desired audio specification */ virtual SDL_AudioSpec getAudioSpec(uint32 rate); diff --git a/backends/modular-backend.h b/backends/modular-backend.h index 4eaea1ec9fa..23fc1d1010e 100644 --- a/backends/modular-backend.h +++ b/backends/modular-backend.h @@ -23,28 +23,28 @@ #ifndef BACKENDS_MODULAR_BACKEND_H #define BACKENDS_MODULAR_BACKEND_H -#include "common/system.h" +#include "backends/base-backend.h" class GraphicsManager; class MutexManager; /** * Base class for modular backends. - * + * * It wraps most functions to their manager equivalent, but not * all OSystem functions are implemented here. - * + * * A backend derivated from this class, will need to implement * these functions on its own: * OSystem::pollEvent() * OSystem::getMillis() * OSystem::delayMillis() * OSystem::getTimeAndDate() - * + * * And, it should also initialize all the managers variables * declared in this class, or override their related functions. */ -class ModularBackend : public OSystem { +class ModularBackend : public BaseBackend { public: ModularBackend(); virtual ~ModularBackend(); @@ -86,11 +86,11 @@ public: /** @name Events and Time */ //@{ - + virtual Common::HardwareKeySet *getHardwareKeySet() { return 0; } //@} - + /** @name Mutex handling */ //@{ diff --git a/backends/module.mk b/backends/module.mk index ffd3b531912..9af85c9d392 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -1,6 +1,7 @@ MODULE := backends MODULE_OBJS := \ + base-backend.o \ modular-backend.o \ audiocd/default/default-audiocd.o \ events/default/default-events.o \ @@ -51,14 +52,19 @@ endif # derive from the SDL backend, and they all need the following files. ifdef SDL_BACKEND MODULE_OBJS += \ - audiocd/sdl/sdl-audiocd.o \ events/sdl/sdl-events.o \ - graphics/sdl/sdl-graphics.o \ + graphics/surfacesdl/surfacesdl-graphics.o \ mixer/doublebuffersdl/doublebuffersdl-mixer.o \ mixer/sdl/sdl-mixer.o \ mutex/sdl/sdl-mutex.o \ plugins/sdl/sdl-provider.o \ timer/sdl/sdl-timer.o + +# SDL 1.3 removed audio CD support +ifndef USE_SDL13 +MODULE_OBJS += \ + audiocd/sdl/sdl-audiocd.o +endif endif ifdef POSIX @@ -66,7 +72,8 @@ MODULE_OBJS += \ fs/posix/posix-fs.o \ fs/posix/posix-fs-factory.o \ plugins/posix/posix-provider.o \ - saves/posix/posix-saves.o + saves/posix/posix-saves.o \ + taskbar/unity/unity-taskbar.o endif ifdef MACOSX @@ -80,7 +87,8 @@ MODULE_OBJS += \ fs/windows/windows-fs.o \ fs/windows/windows-fs-factory.o \ midi/windows.o \ - plugins/win32/win32-provider.o + plugins/win32/win32-provider.o \ + taskbar/win32/win32-taskbar.o endif ifdef AMIGAOS @@ -90,6 +98,15 @@ MODULE_OBJS += \ midi/camd.o endif +ifdef PLAYSTATION3 +MODULE_OBJS += \ + fs/posix/posix-fs.o \ + fs/posix/posix-fs-factory.o \ + fs/ps3/ps3-fs-factory.o \ + events/ps3sdl/ps3sdl-events.o \ + mixer/sdl13/sdl13-mixer.o +endif + ifeq ($(BACKEND),ds) MODULE_OBJS += \ fs/ds/ds-fs.o \ @@ -109,14 +126,6 @@ MODULE_OBJS += \ graphics/gph/gph-graphics.o endif -# TODO/FIXME: The gp2xsdl files are only compiled if GP2X_OLD is defined, -# which currently is never the case (unless the user manually requests it). -# ifeq ($(BACKEND),gp2x) -# MODULE_OBJS += \ -# events/gp2xsdl/gp2xsdl-events.o \ -# graphics/gp2xsdl/gp2xsdl-graphics.o -# endif - ifeq ($(BACKEND),linuxmoto) MODULE_OBJS += \ events/linuxmotosdl/linuxmotosdl-events.o \ diff --git a/backends/platform/sdl/macosx/macosx.cpp b/backends/platform/sdl/macosx/macosx.cpp index 67af92dda03..f14745d0e19 100644 --- a/backends/platform/sdl/macosx/macosx.cpp +++ b/backends/platform/sdl/macosx/macosx.cpp @@ -74,7 +74,7 @@ void OSystem_MacOSX::addSysArchivesToSearchSet(Common::SearchSet &s, int priorit } void OSystem_MacOSX::setupIcon() { - // Don't set icon on OS X, as we use a nicer external icon there. + // Don't set icon on OS X, as we use a nicer external icon there. } bool OSystem_MacOSX::hasFeature(Feature f) { diff --git a/backends/platform/sdl/main.cpp b/backends/platform/sdl/main.cpp index e2b428ba67e..b1c7f0ddb3b 100644 --- a/backends/platform/sdl/main.cpp +++ b/backends/platform/sdl/main.cpp @@ -34,6 +34,7 @@ !defined(CAANOO) && \ !defined(LINUXMOTO) && \ !defined(SAMSUNGTV) && \ + !defined(PLAYSTATION3) && \ !defined(OPENPANDORA) #include "backends/platform/sdl/sdl.h" diff --git a/backends/platform/sdl/module.mk b/backends/platform/sdl/module.mk index efc5168d5b3..e67bf859d6f 100644 --- a/backends/platform/sdl/module.mk +++ b/backends/platform/sdl/module.mk @@ -29,6 +29,12 @@ MODULE_OBJS += \ amigaos/amigaos.o endif +ifdef PLAYSTATION3 +MODULE_OBJS += \ + ps3/ps3-main.o \ + ps3/ps3.o +endif + # We don't use rules.mk but rather manually update OBJS and MODULE_DIRS. MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) OBJS := $(MODULE_OBJS) $(OBJS) diff --git a/backends/platform/sdl/posix/posix-main.cpp b/backends/platform/sdl/posix/posix-main.cpp index 2ee48151a00..eee4c1777a4 100644 --- a/backends/platform/sdl/posix/posix-main.cpp +++ b/backends/platform/sdl/posix/posix-main.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#if defined(POSIX) && !defined(MACOSX) && !defined(SAMSUNGTV) && !defined(WEBOS) && !defined(LINUXMOTO) && !defined(GPH_DEVICE) && !defined(GP2X) && !defined(DINGUX) && !defined(OPENPANDORA) +#if defined(POSIX) && !defined(MACOSX) && !defined(SAMSUNGTV) && !defined(WEBOS) && !defined(LINUXMOTO) && !defined(GPH_DEVICE) && !defined(GP2X) && !defined(DINGUX) && !defined(OPENPANDORA) && !defined(PLAYSTATION3) #include "backends/platform/sdl/posix/posix.h" #include "backends/plugins/sdl/sdl-provider.h" diff --git a/backends/platform/sdl/posix/posix.cpp b/backends/platform/sdl/posix/posix.cpp index 58dd52b552b..59518fc5672 100644 --- a/backends/platform/sdl/posix/posix.cpp +++ b/backends/platform/sdl/posix/posix.cpp @@ -33,6 +33,7 @@ #include "backends/platform/sdl/posix/posix.h" #include "backends/saves/posix/posix-saves.h" #include "backends/fs/posix/posix-fs-factory.h" +#include "backends/taskbar/unity/unity-taskbar.h" #include #include @@ -49,6 +50,11 @@ void OSystem_POSIX::init() { // Initialze File System Factory _fsFactory = new POSIXFilesystemFactory(); +#if defined(USE_TASKBAR) && defined(USE_TASKBAR_UNITY) + // Initialize taskbar manager + _taskbarManager = new UnityTaskbarManager(); +#endif + // Invoke parent implementation of this method OSystem_SDL::init(); } @@ -60,6 +66,11 @@ void OSystem_POSIX::initBackend() { // Invoke parent implementation of this method OSystem_SDL::initBackend(); + +#if defined(USE_TASKBAR) && defined(USE_TASKBAR_UNITY) + // Register the taskbar manager as an event source (this is necessary for the glib event loop to be run) + _eventManager->getEventDispatcher()->registerSource((UnityTaskbarManager *)_taskbarManager, false); +#endif } bool OSystem_POSIX::hasFeature(Feature f) { diff --git a/backends/platform/sdl/sdl-sys.h b/backends/platform/sdl/sdl-sys.h index 77515ff4e5d..ca3c586e037 100644 --- a/backends/platform/sdl/sdl-sys.h +++ b/backends/platform/sdl/sdl-sys.h @@ -27,7 +27,7 @@ // fashion, even on the Symbian port. // Moreover, it contains a workaround for the fact that SDL_rwops.h uses // a FILE pointer in one place, which conflicts with common/forbidden.h. - +// The SDL 1.3 headers also include strings.h #include "common/scummsys.h" @@ -39,6 +39,16 @@ typedef struct { int FAKE; } FAKE_FILE; #define FILE FAKE_FILE #endif +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_strcasecmp) +#undef strcasecmp +#define strcasecmp FAKE_strcasecmp +#endif + +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_strncasecmp) +#undef strncasecmp +#define strncasecmp FAKE_strncasecmp +#endif + #if defined(__SYMBIAN32__) #include #else @@ -47,8 +57,19 @@ typedef struct { int FAKE; } FAKE_FILE; // Finally forbid FILE again (if it was forbidden to start with) #if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_FILE) -#undef FILE +#undef FILE #define FILE FORBIDDEN_SYMBOL_REPLACEMENT #endif +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_strcasecmp) +#undef strcasecmp +#define strcasecmp FORBIDDEN_SYMBOL_REPLACEMENT +#endif + +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_strncasecmp) +#undef strncasecmp +#define strncasecmp FORBIDDEN_SYMBOL_REPLACEMENT +#endif + + #endif diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index c7da131eeeb..86a69001a75 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -31,14 +31,22 @@ #include "backends/platform/sdl/sdl.h" #include "common/config-manager.h" #include "common/EventRecorder.h" +#include "common/taskbar.h" #include "common/textconsole.h" #include "backends/saves/default/default-saves.h" + +// Audio CD support was removed with SDL 1.3 +#if SDL_VERSION_ATLEAST(1, 3, 0) +#include "backends/audiocd/default/default-audiocd.h" +#else #include "backends/audiocd/sdl/sdl-audiocd.h" +#endif + #include "backends/events/sdl/sdl-events.h" #include "backends/mutex/sdl/sdl-mutex.h" #include "backends/timer/sdl/sdl-timer.h" -#include "backends/graphics/sdl/sdl-graphics.h" +#include "backends/graphics/surfacesdl/surfacesdl-graphics.h" #include "icons/residual.xpm" @@ -111,6 +119,11 @@ void OSystem_SDL::init() { if (_timerManager == 0) _timerManager = new SdlTimerManager(); + +#if defined(USE_TASKBAR) + if (_taskbarManager == 0) + _taskbarManager = new Common::TaskbarManager(); +#endif } void OSystem_SDL::initBackend() { @@ -126,23 +139,11 @@ void OSystem_SDL::initBackend() { if (_graphicsManager == 0) { if (_graphicsManager == 0) { - _graphicsManager = new SdlGraphicsManager(_eventSource); + _graphicsManager = new SurfaceSdlGraphicsManager(_eventSource); graphicsManagerType = 0; } } - // Creates the backend managers, if they don't exist yet (we check - // for this to allow subclasses to provide their own). - if (_eventManager == 0) - _eventManager = new DefaultEventManager(_eventSource); - - // We have to initialize the graphics manager before the event manager - // so the virtual keyboard can be initialized, but we have to add the - // graphics manager as an event observer after initializing the event - // manager. - if (graphicsManagerType == 0) - ((SdlGraphicsManager *)_graphicsManager)->initEventObserver(); - if (_savefileManager == 0) _savefileManager = new DefaultSaveFileManager(); @@ -153,8 +154,15 @@ void OSystem_SDL::initBackend() { _mixerManager->init(); } - if (_audiocdManager == 0) + if (_audiocdManager == 0) { + // Audio CD support was removed with SDL 1.3 +#if SDL_VERSION_ATLEAST(1, 3, 0) + _audiocdManager = new DefaultAudioCDManager(); +#else _audiocdManager = new SdlAudioCDManager(); +#endif + + } // Setup a custom program icon. setupIcon(); @@ -162,8 +170,30 @@ void OSystem_SDL::initBackend() { _inited = true; ModularBackend::initBackend(); + + // We have to initialize the graphics manager before the event manager + // so the virtual keyboard can be initialized, but we have to add the + // graphics manager as an event observer after initializing the event + // manager. + if (graphicsManagerType == 0) + ((SurfaceSdlGraphicsManager *)_graphicsManager)->initEventObserver(); } +#if defined(USE_TASKBAR) +void OSystem_SDL::engineInit() { + // Add the started engine to the list of recent tasks + _taskbarManager->addRecent(ConfMan.getActiveDomainName(), ConfMan.get("description")); + + // Set the overlay icon the current running engine + _taskbarManager->setOverlayIcon(ConfMan.getActiveDomainName(), ConfMan.get("description")); +} + +void OSystem_SDL::engineDone() { + // Remove overlay icon + _taskbarManager->setOverlayIcon("", ""); +} +#endif + void OSystem_SDL::initSDL() { // Check if SDL has not been initialized if (!_initedSDL) { @@ -230,10 +260,22 @@ void OSystem_SDL::fatalError() { void OSystem_SDL::logMessage(LogMessageType::Type type, const char *message) { - ModularBackend::logMessage(type, message); + // First log to stdout/stderr + FILE *output = 0; + + if (type == LogMessageType::kInfo || type == LogMessageType::kDebug) + output = stdout; + else + output = stderr; + + fputs(message, output); + fflush(output); + + // Then log into file (via the logger) if (_logger) _logger->print(message); + // Finally, some Windows / WinCE specific logging code. #if defined( USE_WINDBG ) #if defined( _WIN32_WCE ) TCHAR buf_unicode[1024]; @@ -256,7 +298,7 @@ void OSystem_SDL::logMessage(LogMessageType::Type type, const char *message) { } Common::String OSystem_SDL::getSystemLanguage() const { -#ifdef USE_DETECTLANG +#if defined(USE_DETECTLANG) && !defined(_WIN32_WCE) #ifdef WIN32 // We can not use "setlocale" (at least not for MSVC builds), since it // will return locales like: "English_USA.1252", thus we need a special @@ -323,7 +365,7 @@ void OSystem_SDL::setupIcon() { if (sscanf(residual_icon[0], "%d %d %d %d", &w, &h, &ncols, &nbytes) != 4) { warning("Wrong format of residual_icon[0] (%s)", residual_icon[0]); - + return; } if ((w > 512) || (h > 512) || (ncols > 255) || (nbytes > 1)) { @@ -339,6 +381,7 @@ void OSystem_SDL::setupIcon() { for (i = 0; i < ncols; i++) { unsigned char code; char color[32]; + memset(color, 0, sizeof(color)); unsigned int col; if (sscanf(residual_icon[1 + i], "%c c %s", &code, color) != 2) { warning("Wrong format of residual_icon[%d] (%s)", 1 + i, residual_icon[1 + i]); diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h index 089df8b2db5..465ab87751a 100644 --- a/backends/platform/sdl/sdl.h +++ b/backends/platform/sdl/sdl.h @@ -34,7 +34,7 @@ #include "backends/events/sdl/sdl-events.h" #include "backends/log/log.h" -/** +/** * Base OSystem class for all SDL ports. */ class OSystem_SDL : public ModularBackend { @@ -42,7 +42,7 @@ public: OSystem_SDL(); virtual ~OSystem_SDL(); - /** + /** * Pre-initialize backend. It should be called after * instantiating the backend. Early needed managers are * created here. @@ -58,6 +58,10 @@ public: // Override functions from ModularBackend and OSystem virtual void initBackend(); +#if defined(USE_TASKBAR) + virtual void engineInit(); + virtual void engineDone(); +#endif virtual Common::HardwareKeySet *getHardwareKeySet(); virtual void quit(); virtual void fatalError(); diff --git a/backends/platform/sdl/win32/win32.cpp b/backends/platform/sdl/win32/win32.cpp index c239c6c83fa..80faf321878 100644 --- a/backends/platform/sdl/win32/win32.cpp +++ b/backends/platform/sdl/win32/win32.cpp @@ -24,6 +24,7 @@ #define FORBIDDEN_SYMBOL_ALLOW_ALL #include "common/scummsys.h" +#include "common/config-manager.h" #include "common/error.h" #include "common/textconsole.h" @@ -34,60 +35,49 @@ #undef ARRAYSIZE // winnt.h defines ARRAYSIZE, but we want our own one... #include +#include // For setting the icon + #include "backends/platform/sdl/win32/win32.h" #include "backends/fs/windows/windows-fs-factory.h" +#include "backends/taskbar/win32/win32-taskbar.h" #include "common/memstream.h" #define DEFAULT_CONFIG_FILE "residual.ini" -//#define HIDE_CONSOLE - -#ifdef HIDE_CONSOLE -struct SdlConsoleHidingWin32 { - DWORD myPid; - DWORD myTid; - HWND consoleHandle; -}; - -// console hiding for win32 -static BOOL CALLBACK initBackendFindConsoleWin32Proc(HWND hWnd, LPARAM lParam) { - DWORD pid, tid; - SdlConsoleHidingWin32 *variables = (SdlConsoleHidingWin32 *)lParam; - tid = GetWindowThreadProcessId(hWnd, &pid); - if ((tid == variables->myTid) && (pid == variables->myPid)) { - variables->consoleHandle = hWnd; - return FALSE; - } - return TRUE; -} - -#endif - void OSystem_Win32::init() { -#ifdef HIDE_CONSOLE - // console hiding for win32 - SdlConsoleHidingWin32 consoleHidingWin32; - consoleHidingWin32.consoleHandle = 0; - consoleHidingWin32.myPid = GetCurrentProcessId(); - consoleHidingWin32.myTid = GetCurrentThreadId(); - EnumWindows (initBackendFindConsoleWin32Proc, (LPARAM)&consoleHidingWin32); - - if (!ConfMan.getBool("show_console")) { - if (consoleHidingWin32.consoleHandle) { - // We won't find a window with our TID/PID in case we were started from command-line - ShowWindow(consoleHidingWin32.consoleHandle, SW_HIDE); - } - } -#endif - - // Initialze File System Factory + // Initialize File System Factory _fsFactory = new WindowsFilesystemFactory(); +#if defined(USE_TASKBAR) + // Initialize taskbar manager + _taskbarManager = new Win32TaskbarManager(); +#endif + // Invoke parent implementation of this method OSystem_SDL::init(); } +void OSystem_Win32::initBackend() { + // Console window is enabled by default on Windows + ConfMan.registerDefault("console", true); + + // Enable or disable the window console window + if (ConfMan.getBool("console")) { + if (AllocConsole()) { + freopen("CONIN$","r",stdin); + freopen("CONOUT$","w",stdout); + freopen("CONOUT$","w",stderr); + } + SetConsoleTitle("Residual Status Window"); + } else { + FreeConsole(); + } + + // Invoke parent implementation of this method + OSystem_SDL::initBackend(); +} + bool OSystem_Win32::hasFeature(Feature f) { if (f == kFeatureDisplayLogFile) @@ -131,6 +121,28 @@ bool OSystem_Win32::displayLogFile() { return false; } +void OSystem_Win32::setupIcon() { + HMODULE handle = GetModuleHandle(NULL); + HICON ico = LoadIcon(handle, MAKEINTRESOURCE(1001 /* IDI_ICON */)); + if (ico) { + SDL_SysWMinfo wminfo; + SDL_VERSION(&wminfo.version); + if (SDL_GetWMInfo(&wminfo)) { + // Replace the handle to the icon associated with the window class by our custom icon + SetClassLongPtr(wminfo.window, GCLP_HICON, (ULONG_PTR)ico); + + // Since there wasn't any default icon, we can't use the return value from SetClassLong + // to check for errors (it would be 0 in both cases: error or no previous value for the + // icon handle). Instead we check for the last-error code value. + if (GetLastError() == ERROR_SUCCESS) + return; + } + } + + // If no icon has been set, fallback to default path + OSystem_SDL::setupIcon(); +} + Common::String OSystem_Win32::getDefaultConfigFileName() { char configFile[MAXPATHLEN]; @@ -149,18 +161,31 @@ Common::String OSystem_Win32::getDefaultConfigFileName() { error("Unable to access user profile directory"); strcat(configFile, "\\Application Data"); - CreateDirectory(configFile, NULL); + + // If the directory already exists (as it should in most cases), + // we don't want to fail, but we need to stop on other errors (such as ERROR_PATH_NOT_FOUND) + if (!CreateDirectory(configFile, NULL)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) + error("Cannot create Application data folder"); + } } strcat(configFile, "\\Residual"); - CreateDirectory(configFile, NULL); + if (!CreateDirectory(configFile, NULL)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) + error("Cannot create Residual application data folder"); + } + strcat(configFile, "\\" DEFAULT_CONFIG_FILE); FILE *tmp = NULL; if ((tmp = fopen(configFile, "r")) == NULL) { // Check windows directory char oldConfigFile[MAXPATHLEN]; - GetWindowsDirectory(oldConfigFile, MAXPATHLEN); + uint ret = GetWindowsDirectory(oldConfigFile, MAXPATHLEN); + if (ret == 0 || ret > MAXPATHLEN) + error("Cannot retrieve the path of the Windows directory"); + strcat(oldConfigFile, "\\" DEFAULT_CONFIG_FILE); if ((tmp = fopen(oldConfigFile, "r"))) { strcpy(configFile, oldConfigFile); @@ -172,7 +197,10 @@ Common::String OSystem_Win32::getDefaultConfigFileName() { } } else { // Check windows directory - GetWindowsDirectory(configFile, MAXPATHLEN); + uint ret = GetWindowsDirectory(configFile, MAXPATHLEN); + if (ret == 0 || ret > MAXPATHLEN) + error("Cannot retrieve the path of the Windows directory"); + strcat(configFile, "\\" DEFAULT_CONFIG_FILE); } @@ -300,7 +328,7 @@ Common::SeekableReadStream *Win32ResourceArchive::createReadStreamForMember(cons } // End of anonymous namespace void OSystem_Win32::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) { - s.add("Win32Res", new Win32ResourceArchive()); + s.add("Win32Res", new Win32ResourceArchive(), priority); OSystem_SDL::addSysArchivesToSearchSet(s, priority); } diff --git a/backends/platform/sdl/win32/win32.h b/backends/platform/sdl/win32/win32.h index ef7b6af3f1a..b56997a63b6 100644 --- a/backends/platform/sdl/win32/win32.h +++ b/backends/platform/sdl/win32/win32.h @@ -28,6 +28,7 @@ class OSystem_Win32 : public OSystem_SDL { public: virtual void init(); + virtual void initBackend(); virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0); @@ -46,6 +47,7 @@ protected: */ Common::String _logFilePath; + virtual void setupIcon(); virtual Common::String getDefaultConfigFileName(); virtual Common::WriteStream *createLogFile(); }; diff --git a/backends/plugins/win32/win32-provider.cpp b/backends/plugins/win32/win32-provider.cpp index 9cbff085203..0a08e48e6f7 100644 --- a/backends/plugins/win32/win32-provider.cpp +++ b/backends/plugins/win32/win32-provider.cpp @@ -31,7 +31,6 @@ #define WIN32_LEAN_AND_MEAN #include -#undef ARRAYSIZE // winnt.h defines ARRAYSIZE, but we want our own one... class Win32Plugin : public DynamicPlugin { diff --git a/backends/taskbar/unity/unity-taskbar.cpp b/backends/taskbar/unity/unity-taskbar.cpp new file mode 100644 index 00000000000..da053734d24 --- /dev/null +++ b/backends/taskbar/unity/unity-taskbar.cpp @@ -0,0 +1,114 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h +#define FORBIDDEN_SYMBOL_EXCEPTION_time_h +#include "common/scummsys.h" + +#if defined(POSIX) && defined(USE_TASKBAR) && defined(USE_TASKBAR_UNITY) + +#include "backends/taskbar/unity/unity-taskbar.h" + +#include "common/textconsole.h" + +#include + +UnityTaskbarManager::UnityTaskbarManager() { + g_type_init(); + + _loop = g_main_loop_new(NULL, FALSE); + + _launcher = unity_launcher_entry_get_for_desktop_id("scummvm.desktop"); +} + +UnityTaskbarManager::~UnityTaskbarManager() { + g_main_loop_unref(_loop); + _loop = NULL; +} + +void UnityTaskbarManager::setProgressValue(int completed, int total) { + if (_launcher == NULL) + return; + + double percentage = (double)completed / (double)total; + unity_launcher_entry_set_progress(_launcher, percentage); + unity_launcher_entry_set_progress_visible(_launcher, TRUE); +} + +void UnityTaskbarManager::setProgressState(TaskbarProgressState state) { + if (_launcher == NULL) + return; + + switch (state) { + default: + warning("[UnityTaskbarManager::setProgressState] Unknown state / Not implemented (%d)", state); + // fallback to noprogress state + + case kTaskbarNoProgress: + unity_launcher_entry_set_progress_visible(_launcher, FALSE); + break; + + // Unity only support two progress states as of 3.0: visible or not visible + // We show progress in all of those states + case kTaskbarIndeterminate: + case kTaskbarNormal: + case kTaskbarError: + case kTaskbarPaused: + unity_launcher_entry_set_progress_visible(_launcher, TRUE); + break; + } +} + +void UnityTaskbarManager::addRecent(const Common::String &name, const Common::String &description) { + warning("[UnityTaskbarManager::addRecent] Not implemented"); +} + +void UnityTaskbarManager::setCount(int count) { + if (_launcher == NULL) + return; + + unity_launcher_entry_set_count(_launcher, count); + + unity_launcher_entry_set_count_visible(_launcher, (count == 0) ? FALSE : TRUE); +} + +// Unity requires the glib event loop to the run to function properly +// as events are sent asynchronously +bool UnityTaskbarManager::pollEvent(Common::Event &event) { + if (!_loop) + return false; + + // Get context + GMainContext *context = g_main_loop_get_context(_loop); + if (!context) + return false; + + // Dispatch events + g_main_context_iteration(context, FALSE); + + return false; +} + +#endif diff --git a/backends/taskbar/unity/unity-taskbar.h b/backends/taskbar/unity/unity-taskbar.h new file mode 100644 index 00000000000..06ea0ca769f --- /dev/null +++ b/backends/taskbar/unity/unity-taskbar.h @@ -0,0 +1,58 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKEND_UNITY_TASKBAR_H +#define BACKEND_UNITY_TASKBAR_H + +#if defined(POSIX) && defined(USE_TASKBAR) && defined(USE_TASKBAR_UNITY) + +#include "common/events.h" +#include "common/str.h" +#include "common/taskbar.h" + +typedef struct _GMainLoop GMainLoop; +typedef struct _UnityLauncherEntry UnityLauncherEntry; + +class UnityTaskbarManager : public Common::TaskbarManager, public Common::EventSource { +public: + UnityTaskbarManager(); + virtual ~UnityTaskbarManager(); + + virtual void setProgressValue(int completed, int total); + virtual void setProgressState(TaskbarProgressState state); + virtual void addRecent(const Common::String &name, const Common::String &description); + virtual void setCount(int count); + + // Implementation of the EventSource interface + virtual bool pollEvent(Common::Event &event); + +private: + GMainLoop *_loop; + UnityLauncherEntry *_launcher; +}; + +#endif + +#endif // BACKEND_UNITY_TASKBAR_H diff --git a/backends/taskbar/win32/mingw-compat.h b/backends/taskbar/win32/mingw-compat.h new file mode 100644 index 00000000000..30ce8181410 --- /dev/null +++ b/backends/taskbar/win32/mingw-compat.h @@ -0,0 +1,154 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +// TODO: Remove header when the latest changes to the Windows SDK have been integrated into MingW +// For reference, the interface definitions here are imported the SDK headers and from the +// EcWin7 project (https://code.google.com/p/dukto/) + +#ifndef BACKEND_WIN32_TASKBAR_MINGW_H +#define BACKEND_WIN32_TASKBAR_MINGW_H + +#if defined(WIN32) +#if defined(__GNUC__) +#ifdef __MINGW32__ + +#ifdef _WIN32_WINNT + #undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0501 +#include +#include +#include +#include +#include +#define CMIC_MASK_ASYNCOK SEE_MASK_ASYNCOK + +extern const GUID CLSID_ShellLink; + +// Shard enumeration value +#define SHARD_LINK 0x00000006 + +// Taskbar GUID definitions +DEFINE_GUID(CLSID_TaskbarList,0x56fdf344,0xfd6d,0x11d0,0x95,0x8a,0x0,0x60,0x97,0xc9,0xa0,0x90); +DEFINE_GUID(IID_ITaskbarList3,0xea1afb91,0x9e28,0x4b86,0x90,0xE9,0x9e,0x9f,0x8a,0x5e,0xef,0xaf); +DEFINE_GUID(IID_IPropertyStore,0x886d8eeb,0x8cf2,0x4446,0x8d,0x02,0xcd,0xba,0x1d,0xbd,0xcf,0x99); + +// Property key +typedef struct _tagpropertykey { + GUID fmtid; + DWORD pid; +} PROPERTYKEY; + +#define REFPROPERTYKEY const PROPERTYKEY & + +typedef struct tagPROPVARIANT PROPVARIANT; +#define REFPROPVARIANT const PROPVARIANT & + +// Property store +DECLARE_INTERFACE_(IPropertyStore, IUnknown) { + STDMETHOD (GetCount) (DWORD *cProps) PURE; + STDMETHOD (GetAt) (DWORD iProp, PROPERTYKEY *pkey) PURE; + STDMETHOD (GetValue) (REFPROPERTYKEY key, PROPVARIANT *pv) PURE; + STDMETHOD (SetValue) (REFPROPERTYKEY key, REFPROPVARIANT propvar) PURE; + STDMETHOD (Commit) (void) PURE; + +private: + ~IPropertyStore(); +}; +typedef IPropertyStore *LPIPropertyStore; + +// Mingw-specific defines for taskbar integration +typedef enum THUMBBUTTONMASK { + THB_BITMAP = 0x1, + THB_ICON = 0x2, + THB_TOOLTIP = 0x4, + THB_FLAGS = 0x8 +} THUMBBUTTONMASK; + +typedef enum THUMBBUTTONFLAGS { + THBF_ENABLED = 0, + THBF_DISABLED = 0x1, + THBF_DISMISSONCLICK = 0x2, + THBF_NOBACKGROUND = 0x4, + THBF_HIDDEN = 0x8, + THBF_NONINTERACTIVE = 0x10 +} THUMBBUTTONFLAGS; + +typedef struct THUMBBUTTON { + THUMBBUTTONMASK dwMask; + UINT iId; + UINT iBitmap; + HICON hIcon; + WCHAR szTip[260]; + THUMBBUTTONFLAGS dwFlags; +} THUMBBUTTON; +typedef struct THUMBBUTTON *LPTHUMBBUTTON; + +typedef enum TBPFLAG { + TBPF_NOPROGRESS = 0, + TBPF_INDETERMINATE = 0x1, + TBPF_NORMAL = 0x2, + TBPF_ERROR = 0x4, + TBPF_PAUSED = 0x8 +} TBPFLAG; + +// Taskbar interface +DECLARE_INTERFACE_(ITaskbarList3, IUnknown) { + // IUnknown + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void **ppv) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + // ITaskbarList + STDMETHOD(HrInit) (THIS) PURE; + STDMETHOD(AddTab) (THIS_ HWND hwnd) PURE; + STDMETHOD(DeleteTab) (THIS_ HWND hwnd) PURE; + STDMETHOD(ActivateTab) (THIS_ HWND hwnd) PURE; + STDMETHOD(SetActiveAlt) (THIS_ HWND hwnd) PURE; + STDMETHOD (MarkFullscreenWindow) (THIS_ HWND hwnd, int fFullscreen) PURE; + // ITaskbarList3 + STDMETHOD (SetProgressValue) (THIS_ HWND hwnd, ULONGLONG ullCompleted, ULONGLONG ullTotal) PURE; + STDMETHOD (SetProgressState) (THIS_ HWND hwnd, TBPFLAG tbpFlags) PURE; + STDMETHOD (RegisterTab) (THIS_ HWND hwndTab, HWND hwndMDI) PURE; + STDMETHOD (UnregisterTab) (THIS_ HWND hwndTab) PURE; + STDMETHOD (SetTabOrder) (THIS_ HWND hwndTab, HWND hwndInsertBefore) PURE; + STDMETHOD (SetTabActive) (THIS_ HWND hwndTab, HWND hwndMDI, DWORD dwReserved) PURE; + STDMETHOD (ThumbBarAddButtons) (THIS_ HWND hwnd, UINT cButtons, LPTHUMBBUTTON pButton) PURE; + STDMETHOD (ThumbBarUpdateButtons) (THIS_ HWND hwnd, UINT cButtons, LPTHUMBBUTTON pButton) PURE; + STDMETHOD (ThumbBarSetImageList) (THIS_ HWND hwnd, HIMAGELIST himl) PURE; + STDMETHOD (SetOverlayIcon) (THIS_ HWND hwnd, HICON hIcon, LPCWSTR pszDescription) PURE; + STDMETHOD (SetThumbnailTooltip) (THIS_ HWND hwnd, LPCWSTR pszTip) PURE; + STDMETHOD (SetThumbnailClip) (THIS_ HWND hwnd, RECT *prcClip) PURE; + +private: + ~ITaskbarList3(); +}; + +typedef ITaskbarList3 *LPITaskbarList3; + +#endif // __MINGW32__ +#endif // __GNUC__ +#endif // WIN32 + +#endif // BACKEND_WIN32_TASKBAR_MINGW_H diff --git a/backends/taskbar/win32/win32-taskbar.cpp b/backends/taskbar/win32/win32-taskbar.cpp new file mode 100644 index 00000000000..04889f3dd7a --- /dev/null +++ b/backends/taskbar/win32/win32-taskbar.cpp @@ -0,0 +1,414 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +// We cannot use common/scummsys.h directly as it will include +// windows.h and we need to do it by hand to allow excluded functions +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#if defined(WIN32) && defined(USE_TASKBAR) + +// Needed for taskbar functions +#if defined(__GNUC__) +#ifdef __MINGW32__ + #include "backends/taskbar/win32/mingw-compat.h" +#else + #error Only compilation with MingW is supported +#endif +#else + // We need certain functions that are excluded by default + #undef NONLS + #undef NOICONS + #include + #if defined(ARRAYSIZE) + #undef ARRAYSIZE + #endif + + // Default MSVC headers for ITaskbarList3 and IShellLink + #include +#endif +#include + +// For HWND +#include + +#include "common/scummsys.h" + +#include "backends/taskbar/win32/win32-taskbar.h" + +#include "common/config-manager.h" +#include "common/textconsole.h" +#include "common/file.h" + +// System.Title property key, values taken from http://msdn.microsoft.com/en-us/library/bb787584.aspx +const PROPERTYKEY PKEY_Title = { /* fmtid = */ { 0xF29F85E0, 0x4FF9, 0x1068, { 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9 } }, /* propID = */ 2 }; + +Win32TaskbarManager::Win32TaskbarManager() : _taskbar(NULL), _count(0), _icon(NULL) { + // Do nothing if not running on Windows 7 or later + if (!isWin7OrLater()) + return; + + CoInitialize(NULL); + + // Try creating instance (on fail, _taskbar will contain NULL) + HRESULT hr = CoCreateInstance(CLSID_TaskbarList, + 0, + CLSCTX_INPROC_SERVER, + IID_ITaskbarList3, + reinterpret_cast (&(_taskbar))); + + if (SUCCEEDED(hr)) { + // Initialize taskbar object + if (FAILED(_taskbar->HrInit())) { + _taskbar->Release(); + _taskbar = NULL; + } + } else { + warning("[Win32TaskbarManager::init] Cannot create taskbar instance"); + } +} + +Win32TaskbarManager::~Win32TaskbarManager() { + if (_taskbar) + _taskbar->Release(); + _taskbar = NULL; + + if (_icon) + DestroyIcon(_icon); + + CoUninitialize(); +} + +void Win32TaskbarManager::setOverlayIcon(const Common::String &name, const Common::String &description) { + //warning("[Win32TaskbarManager::setOverlayIcon] Setting overlay icon to: %s (%s)", name.c_str(), description.c_str()); + + if (_taskbar == NULL) + return; + + if (name.empty()) { + _taskbar->SetOverlayIcon(getHwnd(), NULL, L""); + return; + } + + // Compute full icon path + Common::String path = getIconPath(name); + if (path.empty()) + return; + + HICON pIcon = (HICON)::LoadImage(NULL, path.c_str(), IMAGE_ICON, 16, 16, LR_LOADFROMFILE); + if (!pIcon) { + warning("[Win32TaskbarManager::setOverlayIcon] Cannot load icon!"); + return; + } + + // Sets the overlay icon + LPWSTR desc = ansiToUnicode(description.c_str()); + _taskbar->SetOverlayIcon(getHwnd(), pIcon, desc); + + DestroyIcon(pIcon); + + delete[] desc; +} + +void Win32TaskbarManager::setProgressValue(int completed, int total) { + if (_taskbar == NULL) + return; + + _taskbar->SetProgressValue(getHwnd(), completed, total); +} + +void Win32TaskbarManager::setProgressState(TaskbarProgressState state) { + if (_taskbar == NULL) + return; + + _taskbar->SetProgressState(getHwnd(), (TBPFLAG)state); +} + +void Win32TaskbarManager::setCount(int count) { + if (_taskbar == NULL) + return; + + if (count == 0) { + _taskbar->SetOverlayIcon(getHwnd(), NULL, L""); + return; + } + + // FIXME: This isn't really nice and could use a cleanup. + // The only good thing is that it doesn't use GDI+ + // and thus does not have a dependancy on it, + // with the downside of being a lot more ugly. + // Maybe replace it by a Graphic::Surface, use + // ScummVM font drawing and extract the contents at + // the end? + + if (_count != count || _icon == NULL) { + // Cleanup previous icon + _count = count; + if (_icon) + DestroyIcon(_icon); + + Common::String countString = (count < 100 ? Common::String::format("%d", count) : "9+"); + + // Create transparent background + BITMAPV5HEADER bi; + ZeroMemory(&bi, sizeof(BITMAPV5HEADER)); + bi.bV5Size = sizeof(BITMAPV5HEADER); + bi.bV5Width = 16; + bi.bV5Height = 16; + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_RGB; + // Set 32 BPP alpha format + bi.bV5RedMask = 0x00FF0000; + bi.bV5GreenMask = 0x0000FF00; + bi.bV5BlueMask = 0x000000FF; + bi.bV5AlphaMask = 0xFF000000; + + // Get DC + HDC hdc; + hdc = GetDC(NULL); + HDC hMemDC = CreateCompatibleDC(hdc); + ReleaseDC(NULL, hdc); + + // Create a bitmap mask + HBITMAP hBitmapMask = CreateBitmap(16, 16, 1, 1, NULL); + + // Create the DIB section with an alpha channel + void *lpBits; + HBITMAP hBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&lpBits, NULL, 0); + HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); + + // Load the icon background + HICON hIconBackground = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(1002 /* IDI_COUNT */)); + DrawIconEx(hMemDC, 0, 0, hIconBackground, 16, 16, 0, 0, DI_NORMAL); + DeleteObject(hIconBackground); + + // Draw the count + LOGFONT lFont; + memset(&lFont, 0, sizeof(LOGFONT)); + lFont.lfHeight = 10; + lFont.lfWeight = FW_BOLD; + lFont.lfItalic = 1; + strcpy(lFont.lfFaceName, "Arial"); + + HFONT hFont = CreateFontIndirect(&lFont); + SelectObject(hMemDC, hFont); + + RECT rect; + SetRect(&rect, 4, 4, 12, 12); + SetTextColor(hMemDC, RGB(48, 48, 48)); + SetBkMode(hMemDC, TRANSPARENT); + DrawText(hMemDC, countString.c_str(), -1, &rect, DT_NOCLIP|DT_CENTER); + + // Set the text alpha to fully opaque (we consider the data inside the text rect) + DWORD *lpdwPixel = (DWORD *)lpBits; + for (int x = 3; x < 12; x++) { + for(int y = 3; y < 12; y++) { + unsigned char *p = (unsigned char *)(lpdwPixel + x * 16 + y); + + if (p[0] != 0 && p[1] != 0 && p[2] != 0) + p[3] = 255; + } + } + + // Cleanup DC + DeleteObject(hFont); + SelectObject(hMemDC, hOldBitmap); + DeleteDC(hMemDC); + + // Prepare our new icon + ICONINFO ii; + ii.fIcon = FALSE; + ii.xHotspot = 0; + ii.yHotspot = 0; + ii.hbmMask = hBitmapMask; + ii.hbmColor = hBitmap; + + _icon = CreateIconIndirect(&ii); + + DeleteObject(hBitmap); + DeleteObject(hBitmapMask); + + if (!_icon) { + warning("[Win32TaskbarManager::setCount] Cannot create icon for count"); + return; + } + } + + // Sets the overlay icon + LPWSTR desc = ansiToUnicode(Common::String::format("Found games: %d", count).c_str()); + _taskbar->SetOverlayIcon(getHwnd(), _icon, desc); + delete[] desc; +} + +void Win32TaskbarManager::addRecent(const Common::String &name, const Common::String &description) { + //warning("[Win32TaskbarManager::addRecent] Adding recent list entry: %s (%s)", name.c_str(), description.c_str()); + + if (_taskbar == NULL) + return; + + // ANSI version doesn't seem to work correctly with Win7 jump lists, so explicitly use Unicode interface. + IShellLinkW *link; + + // Get the ScummVM executable path. + WCHAR path[MAX_PATH]; + GetModuleFileNameW(NULL, path, MAX_PATH); + + // Create a shell link. + if (SUCCEEDED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC, IID_IShellLinkW, reinterpret_cast (&link)))) { + // Convert game name and description to Unicode. + LPWSTR game = ansiToUnicode(name.c_str()); + LPWSTR desc = ansiToUnicode(description.c_str()); + + // Set link properties. + link->SetPath(path); + link->SetArguments(game); + + Common::String iconPath = getIconPath(name); + if (iconPath.empty()) { + link->SetIconLocation(path, 0); // No game-specific icon available + } else { + LPWSTR icon = ansiToUnicode(iconPath.c_str()); + + link->SetIconLocation(icon, 0); + + delete[] icon; + } + + // The link's display name must be set via property store. + IPropertyStore* propStore; + HRESULT hr = link->QueryInterface(IID_IPropertyStore, reinterpret_cast (&(propStore))); + if (SUCCEEDED(hr)) { + PROPVARIANT pv; + pv.vt = VT_LPWSTR; + pv.pwszVal = desc; + + hr = propStore->SetValue(PKEY_Title, pv); + + propStore->Commit(); + propStore->Release(); + } + + // SHAddToRecentDocs will cause the games to be added to the Recent list, allowing the user to pin them. + SHAddToRecentDocs(SHARD_LINK, link); + link->Release(); + delete[] game; + delete[] desc; + } +} + +Common::String Win32TaskbarManager::getIconPath(Common::String target) { + // We first try to look for a iconspath configuration variable then + // fallback to the extra path + // + // Icons can be either in a subfolder named "icons" or directly in the path + + Common::String iconsPath = ConfMan.get("iconspath"); + Common::String extraPath = ConfMan.get("extrapath"); + +#define TRY_ICON_PATH(path) { \ + Common::FSNode node((path)); \ + if (node.exists()) \ + return (path); \ +} + + if (!iconsPath.empty()) { + TRY_ICON_PATH(iconsPath + "/" + target + ".ico"); + TRY_ICON_PATH(iconsPath + "/" + ConfMan.get("gameid") + ".ico"); + TRY_ICON_PATH(iconsPath + "/icons/" + target + ".ico"); + TRY_ICON_PATH(iconsPath + "/icons/" + ConfMan.get("gameid") + ".ico"); + } + + if (!extraPath.empty()) { + TRY_ICON_PATH(extraPath + "/" + target + ".ico"); + TRY_ICON_PATH(extraPath + "/" + ConfMan.get("gameid") + ".ico"); + TRY_ICON_PATH(extraPath + "/icons/" + target + ".ico"); + TRY_ICON_PATH(extraPath + "/icons/" + ConfMan.get("gameid") + ".ico"); + } + + return ""; +} + +// VerSetConditionMask and VerifyVersionInfo didn't appear until Windows 2000, +// so we need to check for them at runtime +LONGLONG VerSetConditionMaskFunc(ULONGLONG dwlConditionMask, DWORD dwTypeMask, BYTE dwConditionMask) { + typedef BOOL (WINAPI *VerSetConditionMaskFunction)(ULONGLONG conditionMask, DWORD typeMask, BYTE conditionOperator); + + VerSetConditionMaskFunction verSetConditionMask = (VerSetConditionMaskFunction)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "VerSetConditionMask"); + if (verSetConditionMask == NULL) + return 0; + + return verSetConditionMask(dwlConditionMask, dwTypeMask, dwConditionMask); +} + +BOOL VerifyVersionInfoFunc(LPOSVERSIONINFOEXA lpVersionInformation, DWORD dwTypeMask, DWORDLONG dwlConditionMask) { + typedef BOOL (WINAPI *VerifyVersionInfoFunction)(LPOSVERSIONINFOEXA versionInformation, DWORD typeMask, DWORDLONG conditionMask); + + VerifyVersionInfoFunction verifyVersionInfo = (VerifyVersionInfoFunction)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "VerifyVersionInfoA"); + if (verifyVersionInfo == NULL) + return FALSE; + + return verifyVersionInfo(lpVersionInformation, dwTypeMask, dwlConditionMask); +} + +bool Win32TaskbarManager::isWin7OrLater() { + OSVERSIONINFOEX versionInfo; + DWORDLONG conditionMask = 0; + + ZeroMemory(&versionInfo, sizeof(OSVERSIONINFOEX)); + versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + versionInfo.dwMajorVersion = 6; + versionInfo.dwMinorVersion = 1; + + conditionMask = VerSetConditionMaskFunc(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + conditionMask = VerSetConditionMaskFunc(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); + + return VerifyVersionInfoFunc(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask); +} + +LPWSTR Win32TaskbarManager::ansiToUnicode(const char *s) { + DWORD size = MultiByteToWideChar(0, 0, s, -1, NULL, 0); + + if (size > 0) { + LPWSTR result = new WCHAR[size]; + if (MultiByteToWideChar(0, 0, s, -1, result, size) != 0) + return result; + } + + return NULL; +} + +HWND Win32TaskbarManager::getHwnd() { + SDL_SysWMinfo wmi; + SDL_VERSION(&wmi.version); + + if(!SDL_GetWMInfo(&wmi)) + return NULL; + + return wmi.window; +} + +#endif diff --git a/backends/taskbar/win32/win32-taskbar.h b/backends/taskbar/win32/win32-taskbar.h new file mode 100644 index 00000000000..c9d17610175 --- /dev/null +++ b/backends/taskbar/win32/win32-taskbar.h @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKEND_WIN32_TASKBAR_H +#define BACKEND_WIN32_TASKBAR_H + +#if defined(WIN32) && defined(USE_TASKBAR) + +#include "common/str.h" +#include "common/taskbar.h" + +struct ITaskbarList3; + +class Win32TaskbarManager : public Common::TaskbarManager { +public: + Win32TaskbarManager(); + virtual ~Win32TaskbarManager(); + + virtual void setOverlayIcon(const Common::String &name, const Common::String &description); + virtual void setProgressValue(int completed, int total); + virtual void setProgressState(TaskbarProgressState state); + virtual void setCount(int count); + virtual void addRecent(const Common::String &name, const Common::String &description); + +private: + ITaskbarList3 *_taskbar; + + // Count handling + HICON _icon; + int _count; + + /** + * Get the path to an icon for the game + * + * @param target The game target + * + * @return The icon path (or "" if no icon was found) + */ + Common::String getIconPath(Common::String target); + + // Helper functions + bool isWin7OrLater(); + LPWSTR ansiToUnicode(const char *s); + HWND getHwnd(); +}; + +#endif + +#endif // BACKEND_WIN32_TASKBAR_H diff --git a/backends/vkeybd/virtual-keyboard-parser.cpp b/backends/vkeybd/virtual-keyboard-parser.cpp index 5e4ce11fe49..58f0c468f6a 100644 --- a/backends/vkeybd/virtual-keyboard-parser.cpp +++ b/backends/vkeybd/virtual-keyboard-parser.cpp @@ -205,6 +205,9 @@ bool VirtualKeyboardParser::parserCallback_event(ParserNode *node) { evt->type = VirtualKeyboard::kVKEventModifier; byte *flags = (byte*) malloc(sizeof(byte)); + if (!flags) + error("[VirtualKeyboardParser::parserCallback_event] Cannot allocate memory"); + *(flags) = parseFlags(node->values["modifiers"]); evt->data = flags; @@ -217,6 +220,9 @@ bool VirtualKeyboardParser::parserCallback_event(ParserNode *node) { evt->type = VirtualKeyboard::kVKEventSwitchMode; String& mode = node->values["mode"]; char *str = (char*) malloc(sizeof(char) * mode.size() + 1); + if (!str) + error("[VirtualKeyboardParser::parserCallback_event] Cannot allocate memory"); + memcpy(str, mode.c_str(), sizeof(char) * mode.size()); str[mode.size()] = 0; evt->data = str; diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 6ed8d610e93..745959eed23 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -25,6 +25,8 @@ #define FORBIDDEN_SYMBOL_EXCEPTION_exit +#include + #include "engines/metaengine.h" #include "base/commandLine.h" #include "base/plugins.h" @@ -162,13 +164,6 @@ void registerDefaults() { ConfMan.registerDefault("record_temp_file_name", "record.tmp"); ConfMan.registerDefault("record_time_file_name", "record.time"); -#if 0 - // NEW CODE TO HIDE CONSOLE FOR WIN32 -#ifdef WIN32 - // console hiding for win32 - ConfMan.registerDefault("show_console", false); -#endif -#endif } // @@ -197,17 +192,19 @@ void registerDefaults() { if (!option) usage("Option '%s' requires an argument", argv[isLongCmd ? i : i-1]); // Use this for options which have a required integer value +// (we don't check ERANGE because WinCE doesn't support errno, so we're stuck just rejecting LONG_MAX/LONG_MIN..) #define DO_OPTION_INT(shortCmd, longCmd) \ DO_OPTION(shortCmd, longCmd) \ - char *endptr = 0; \ - strtol(option, &endptr, 0); \ - if (endptr == NULL || *endptr != 0) usage("--%s: Invalid number '%s'", longCmd, option); + char *endptr; \ + long int retval = strtol(option, &endptr, 0); \ + if (*endptr != '\0' || retval == LONG_MAX || retval == LONG_MIN) \ + usage("--%s: Invalid number '%s'", longCmd, option); // Use this for boolean options; this distinguishes between "-x" and "-X", // resp. between "--some-option" and "--no-some-option". #define DO_OPTION_BOOL(shortCmd, longCmd) \ if (isLongCmd ? (!strcmp(s+2, longCmd) || !strcmp(s+2, "no-"longCmd)) : (tolower(s[1]) == shortCmd)) { \ - bool boolValue = (islower(s[1]) != 0); \ + bool boolValue = (islower(static_cast(s[1])) != 0); \ s += 2; \ if (isLongCmd) { \ boolValue = !strcmp(s, longCmd); \ @@ -460,14 +457,11 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha END_OPTION #endif -#if 0 - // NEW CODE TO HIDE CONSOLE FOR WIN32 -#ifdef WIN32 - // console hiding for win32 - DO_LONG_OPTION_BOOL("show-console") +#if defined (WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) + // Optional console window on Windows (default: enabled) + DO_LONG_OPTION_BOOL("console") END_OPTION #endif -#endif unknownOption: // If we get till here, the option is unhandled and hence unknown. @@ -575,7 +569,7 @@ static Common::Error listSaves(const char *target) { " ---- ------------------------------------------------------\n"); for (SaveStateList::const_iterator x = saveList.begin(); x != saveList.end(); ++x) { - printf(" %-4s %s\n", x->save_slot().c_str(), x->description().c_str()); + printf(" %-4d %s\n", x->getSaveSlot(), x->getDescription().c_str()); // TODO: Could also iterate over the full hashmap, printing all key-value pairs } } else { diff --git a/base/main.cpp b/base/main.cpp index febe732e975..ea338ec4210 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -322,7 +322,7 @@ extern "C" int residual_main(int argc, const char * const argv[]) { PluginManager::instance().init(); PluginManager::instance().loadAllPlugins(); // load plugins for cached plugin manager - + // If we received an invalid music parameter via command line we check this here. // We can't check this before loading the music plugins. // On the other hand we cannot load the plugins before we know the file paths (in case of external plugins). @@ -355,7 +355,7 @@ extern "C" int residual_main(int argc, const char * const argv[]) { system.getAudioCDManager(); MusicManager::instance(); Common::DebugManager::instance(); - + // Init the event manager. As the virtual keyboard is loaded here, it must // take place after the backend is initiated and the screen has been setup system.getEventManager()->init(); @@ -394,10 +394,10 @@ extern "C" int residual_main(int argc, const char * const argv[]) { PluginManager::instance().unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false); // reallocate the config manager to get rid of any fragmentation ConfMan.defragment(); - #endif - + #endif + // Did an error occur ? - if (result.getCode() != Common::kNoError) { + if (result.getCode() != Common::kNoError && result.getCode() != Common::kUserCanceled) { // Shows an informative error dialog if starting the selected game failed. GUI::displayErrorDialog(result, _("Error running game:")); } diff --git a/base/plugins.cpp b/base/plugins.cpp index ba0fbd45ae2..1bc743929ee 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -220,7 +220,7 @@ PluginManager &PluginManager::instance() { if (_instance) return *_instance; -#if defined(UNCACHED_PLUGINS) && defined(DYNAMIC_MODULES) +#if defined(UNCACHED_PLUGINS) && defined(DYNAMIC_MODULES) _instance = new PluginManagerUncached(); #else _instance = new PluginManager(); @@ -255,7 +255,7 @@ void PluginManager::addPluginProvider(PluginProvider *pp) { void PluginManagerUncached::init() { unloadAllPlugins(); _allEnginePlugins.clear(); - + // Resize our pluginsInMem list to prevent fragmentation _pluginsInMem[PLUGIN_TYPE_ENGINE].resize(2); unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false); // empty the engine plugins @@ -264,7 +264,7 @@ void PluginManagerUncached::init() { pp != _providers.end(); ++pp) { PluginList pl((*pp)->getPlugins()); - + for (PluginList::iterator p = pl.begin(); p != pl.end(); ++p) { // This is a 'hack' based on the assumption that we have no sound // file plugins. Currently this is the case. If it changes, we @@ -272,15 +272,15 @@ void PluginManagerUncached::init() { // music or an engine plugin. if ((*pp)->isFilePluginProvider()) { _allEnginePlugins.push_back(*p); - } else if ((*p)->loadPlugin()) { // and this is the proper method + } else if ((*p)->loadPlugin()) { // and this is the proper method if ((*p)->getType() == PLUGIN_TYPE_ENGINE) { (*p)->unloadPlugin(); _allEnginePlugins.push_back(*p); } else { // add non-engine plugins to the 'in-memory' list // these won't ever get unloaded - addToPluginsInMemList(*p); + addToPluginsInMemList(*p); } - } + } } } } @@ -310,7 +310,7 @@ bool PluginManagerUncached::loadPluginFromGameId(const Common::String &gameId) { bool PluginManagerUncached::loadPluginByFileName(const Common::String &filename) { if (filename.empty()) return false; - + unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false); PluginList::iterator i; @@ -324,7 +324,7 @@ bool PluginManagerUncached::loadPluginByFileName(const Common::String &filename) return false; } -/** +/** * Update the config manager with a plugin file name that we found can handle * the game. **/ @@ -342,7 +342,7 @@ void PluginManagerUncached::updateConfigWithFileName(const Common::String &gameI } } -void PluginManagerUncached::loadFirstPlugin() { +void PluginManagerUncached::loadFirstPlugin() { unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL, false); // let's try to find one we can load @@ -424,7 +424,7 @@ void PluginManager::addToPluginsInMemList(Plugin *plugin) { bool found = false; // The plugin is valid, see if it provides the same module as an // already loaded one and should replace it. - + PluginList::iterator pl = _pluginsInMem[plugin->getType()].begin(); while (!found && pl != _pluginsInMem[plugin->getType()].end()) { if (!strcmp(plugin->getName(), (*pl)->getName())) { @@ -447,9 +447,11 @@ void PluginManager::addToPluginsInMemList(Plugin *plugin) { #include "engines/metaengine.h" +namespace Common { DECLARE_SINGLETON(EngineManager); +} -/** +/** * This function works for both cached and uncached PluginManagers. * For the cached version, most of the logic here will short circuit. * @@ -461,24 +463,24 @@ GameDescriptor EngineManager::findGame(const Common::String &gameName, const Eng // First look for the game using the plugins in memory. This is critical // for calls coming from inside games - result = findGameInLoadedPlugins(gameName, plugin); + result = findGameInLoadedPlugins(gameName, plugin); if (!result.gameid().empty()) { return result; } - + // Now look for the game using the gameId. This is much faster than scanning plugin // by plugin if (PluginMan.loadPluginFromGameId(gameName)) { - result = findGameInLoadedPlugins(gameName, plugin); + result = findGameInLoadedPlugins(gameName, plugin); if (!result.gameid().empty()) { return result; } } - + // We failed to find it using the gameid. Scan the list of plugins PluginMan.loadFirstPlugin(); do { - result = findGameInLoadedPlugins(gameName, plugin); + result = findGameInLoadedPlugins(gameName, plugin); if (!result.gameid().empty()) { // Update with new plugin file name PluginMan.updateConfigWithFileName(gameName); @@ -489,7 +491,7 @@ GameDescriptor EngineManager::findGame(const Common::String &gameName, const Eng return result; } -/** +/** * Find the game within the plugins loaded in memory **/ GameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &gameName, const EnginePlugin **plugin) const { @@ -501,7 +503,7 @@ GameDescriptor EngineManager::findGameInLoadedPlugins(const Common::String &game *plugin = 0; EnginePlugin::List::const_iterator iter; - + for (iter = plugins.begin(); iter != plugins.end(); ++iter) { result = (**iter)->findGame(gameName.c_str()); if (!result.gameid().empty()) { @@ -538,7 +540,9 @@ const EnginePlugin::List &EngineManager::getPlugins() const { #include "audio/musicplugin.h" +namespace Common { DECLARE_SINGLETON(MusicManager); +} const MusicPlugin::List &MusicManager::getPlugins() const { return (const MusicPlugin::List &)PluginManager::instance().getPlugins(PLUGIN_TYPE_MUSIC); diff --git a/base/plugins.h b/base/plugins.h index e5482c0f79d..d0add65c25b 100644 --- a/base/plugins.h +++ b/base/plugins.h @@ -143,7 +143,7 @@ extern int pluginTypeVersions[PLUGIN_TYPE_MAX]; // Abstract plugins /** - * Abstract base class for the plugin objects which handle plugins + * Abstract base class for the plugin objects which handle plugins * instantiation. Subclasses for this may be used for engine plugins and other * types of plugins. An existing PluginObject refers to an executable file * loaded in memory and ready to run. The plugin, on the other hand, is just @@ -310,7 +310,7 @@ protected: bool tryLoadPlugin(Plugin *plugin); void addToPluginsInMemList(Plugin *plugin); - + static PluginManager *_instance; PluginManager(); @@ -326,9 +326,9 @@ public: virtual void init() {} virtual void loadFirstPlugin() {} virtual bool loadNextPlugin() { return false; } - virtual bool loadPluginFromGameId(const Common::String &gameId) { return false; } - virtual void updateConfigWithFileName(const Common::String &gameId) {} - + virtual bool loadPluginFromGameId(const Common::String &gameId) { return false; } + virtual void updateConfigWithFileName(const Common::String &gameId) {} + // Functions used only by the cached PluginManager virtual void loadAllPlugins(); void unloadAllPlugins(); @@ -338,7 +338,7 @@ public: const PluginList &getPlugins(PluginType t) { return _pluginsInMem[t]; } }; -/** +/** * Uncached version of plugin manager * Keeps only one dynamic plugin in memory at a time **/ @@ -349,15 +349,15 @@ protected: PluginList::iterator _currentPlugin; PluginManagerUncached() {} - bool loadPluginByFileName(const Common::String &filename); + bool loadPluginByFileName(const Common::String &filename); public: virtual void init(); virtual void loadFirstPlugin(); virtual bool loadNextPlugin(); - virtual bool loadPluginFromGameId(const Common::String &gameId); - virtual void updateConfigWithFileName(const Common::String &gameId); - + virtual bool loadPluginFromGameId(const Common::String &gameId); + virtual void updateConfigWithFileName(const Common::String &gameId); + virtual void loadAllPlugins() {} // we don't allow this }; diff --git a/base/version.cpp b/base/version.cpp index 114e8ac7aab..9637341c07f 100644 --- a/base/version.cpp +++ b/base/version.cpp @@ -60,6 +60,11 @@ const char *gResidualBuildDate = __DATE__ " " __TIME__; const char *gResidualVersionDate = RESIDUAL_VERSION " (" __DATE__ " " __TIME__ ")"; const char *gResidualFullVersion = "Residual " RESIDUAL_VERSION " (" __DATE__ " " __TIME__ ")"; const char *gResidualFeatures = "" +#ifdef TAINTED_BUILD + // TAINTED means the build contains engines/subengines not enabled by default + "TAINTED " +#endif + #ifdef USE_TREMOR #ifdef USE_TREMOLO // libTremolo is used on WinCE for better ogg performance diff --git a/common/EventRecorder.cpp b/common/EventRecorder.cpp index eb22e1ea885..4441070050d 100644 --- a/common/EventRecorder.cpp +++ b/common/EventRecorder.cpp @@ -27,10 +27,10 @@ #include "common/savefile.h" #include "common/textconsole.h" -DECLARE_SINGLETON(Common::EventRecorder); - namespace Common { +DECLARE_SINGLETON(EventRecorder); + #define RECORD_SIGNATURE 0x54455354 #define RECORD_VERSION 1 diff --git a/common/archive.cpp b/common/archive.cpp index 0ef3893a8c9..102d7aaa3f3 100644 --- a/common/archive.cpp +++ b/common/archive.cpp @@ -285,7 +285,7 @@ void SearchManager::clear() { addDirectory(".", ".", -2); } +DECLARE_SINGLETON(SearchManager); + } // namespace Common -DECLARE_SINGLETON(Common::SearchManager); - diff --git a/common/archive.h b/common/archive.h index 3c75970d604..8400c56d69e 100644 --- a/common/archive.h +++ b/common/archive.h @@ -143,7 +143,7 @@ class SearchSet : public Archive { ArchiveNodeList::iterator find(const String &name); ArchiveNodeList::const_iterator find(const String &name) const; - // Add an archive keeping the list sorted by ascending priorities. + // Add an archive keeping the list sorted by descending priority. void insert(const Node& node); public: diff --git a/common/array.h b/common/array.h index 7ab4a1b0425..e5434091fb4 100644 --- a/common/array.h +++ b/common/array.h @@ -252,6 +252,13 @@ public: _size = newSize; } + void assign(const_iterator first, const_iterator last) { + resize(distance(first, last)); // FIXME: ineffective? + T *dst = _storage; + while (first != last) + *dst++ = *first++; + } + protected: static uint roundUpCapacity(uint capacity) { // Round up capacity to the next power of 2; diff --git a/common/config-file.cpp b/common/config-file.cpp index 55941131ac9..ea3feff8ae2 100644 --- a/common/config-file.cpp +++ b/common/config-file.cpp @@ -38,7 +38,7 @@ namespace Common { */ bool ConfigFile::isValidName(const Common::String &name) { const char *p = name.c_str(); - while (*p && (isalnum(*p) || *p == '-' || *p == '_' || *p == '.')) + while (*p && (isalnum(static_cast(*p)) || *p == '-' || *p == '_' || *p == '.')) p++; return *p == 0; } @@ -116,7 +116,7 @@ bool ConfigFile::loadFromStream(SeekableReadStream &stream) { // is, verify that it only consists of alphanumerics, // periods, dashes and underscores). Mohawk Living Books games // can have periods in their section names. - while (*p && (isalnum(*p) || *p == '-' || *p == '_' || *p == '.')) + while (*p && (isalnum(static_cast(*p)) || *p == '-' || *p == '_' || *p == '.')) p++; if (*p == '\0') @@ -139,7 +139,7 @@ bool ConfigFile::loadFromStream(SeekableReadStream &stream) { // Skip leading whitespaces const char *t = line.c_str(); - while (isspace(*t)) + while (isspace(static_cast(*t))) t++; // Skip empty lines / lines with only whitespace diff --git a/common/config-manager.cpp b/common/config-manager.cpp index 3bad8355af9..d8be82a8914 100644 --- a/common/config-manager.cpp +++ b/common/config-manager.cpp @@ -27,17 +27,17 @@ #include "common/system.h" #include "common/textconsole.h" -DECLARE_SINGLETON(Common::ConfigManager); - static bool isValidDomainName(const Common::String &domName) { const char *p = domName.c_str(); - while (*p && (isalnum(*p) || *p == '-' || *p == '_')) + while (*p && (isalnum(static_cast(*p)) || *p == '-' || *p == '_')) p++; return *p == 0; } namespace Common { +DECLARE_SINGLETON(ConfigManager); + const char *ConfigManager::kApplicationDomain = "residual"; const char *ConfigManager::kTransientDomain = "__TRANSIENT"; @@ -187,7 +187,7 @@ void ConfigManager::loadFromStream(SeekableReadStream &stream) { // Get the domain name, and check whether it's valid (that // is, verify that it only consists of alphanumerics, // dashes and underscores). - while (*p && (isalnum(*p) || *p == '-' || *p == '_')) + while (*p && (isalnum(static_cast(*p)) || *p == '-' || *p == '_')) p++; if (*p == '\0') @@ -205,7 +205,7 @@ void ConfigManager::loadFromStream(SeekableReadStream &stream) { // Skip leading whitespaces const char *t = line.c_str(); - while (isspace(*t)) + while (isspace(static_cast(*t))) t++; // Skip empty lines / lines with only whitespace @@ -491,11 +491,9 @@ int ConfigManager::getInt(const String &key, const String &domName) const { bool ConfigManager::getBool(const String &key, const String &domName) const { String value(get(key, domName)); - - if ((value == "true") || (value == "yes") || (value == "1")) - return true; - if ((value == "false") || (value == "no") || (value == "0")) - return false; + bool val; + if (Common::parseBool(value, val)) + return val; error("ConfigManager::getBool(%s,%s): '%s' is not a valid bool", key.c_str(), domName.c_str(), value.c_str()); diff --git a/common/config-manager.h b/common/config-manager.h index 6f3f554459b..78a62b98089 100644 --- a/common/config-manager.h +++ b/common/config-manager.h @@ -147,7 +147,7 @@ public: static void defragment(); // move in memory to reduce fragmentation void copyFrom(ConfigManager &source); - + private: friend class Singleton; ConfigManager(); diff --git a/common/debug.cpp b/common/debug.cpp index dbbb204debe..9c3a93e5a6e 100644 --- a/common/debug.cpp +++ b/common/debug.cpp @@ -23,16 +23,17 @@ #include "common/debug-channels.h" #include "common/system.h" #include "common/textconsole.h" +#include "common/algorithm.h" #include // For va_list etc. // TODO: Move gDebugLevel into namespace Common. int gDebugLevel = -1; -DECLARE_SINGLETON(Common::DebugManager); - namespace Common { +DECLARE_SINGLETON(DebugManager); + namespace { struct DebugLevelComperator { @@ -107,18 +108,13 @@ bool DebugManager::isDebugChannelEnabled(uint32 channel) { #ifndef DISABLE_TEXT_CONSOLE static void debugHelper(const char *s, va_list va, bool caret = true) { - char buf[STRINGBUFLEN]; + Common::String buf = Common::String::vformat(s, va); - vsnprintf(buf, STRINGBUFLEN, s, va); - buf[STRINGBUFLEN-1] = '\0'; - - if (caret) { - buf[STRINGBUFLEN-2] = '\0'; - strcat(buf, "\n"); - } + if (caret) + buf += '\n'; if (g_system) - g_system->logMessage(LogMessageType::kDebug, buf); + g_system->logMessage(LogMessageType::kDebug, buf.c_str()); // TODO: Think of a good fallback in case we do not have // any OSystem yet. } diff --git a/common/error.cpp b/common/error.cpp index a6c52a0ce95..78178f8e272 100644 --- a/common/error.cpp +++ b/common/error.cpp @@ -67,6 +67,9 @@ static String errorToString(ErrorCode errorCode) { case kEnginePluginNotSupportSaves: return _s("Engine plugin does not support save states"); + case kUserCanceled: + return _s("User canceled"); + case kUnknownError: default: return _s("Unknown error"); diff --git a/common/error.h b/common/error.h index 23c12b67e4c..7043862eeab 100644 --- a/common/error.h +++ b/common/error.h @@ -62,6 +62,8 @@ enum ErrorCode { kEnginePluginNotFound, ///< Failed to find plugin to handle target kEnginePluginNotSupportSaves, ///< Failed if plugin does not support listing save states + kUserCanceled, ///< User has canceled the launching of the game + kUnknownError ///< Catch-all error, used if no other error code matches }; diff --git a/common/forbidden.h b/common/forbidden.h index 9cba19cf5e0..95c1a47d65d 100644 --- a/common/forbidden.h +++ b/common/forbidden.h @@ -29,7 +29,7 @@ * infrastructure code do not make use of certain "forbidden" APIs, such * as fopen(), setjmp(), etc. * This is achieved by re-#defining various symbols to a "garbage" - * string which then trigers a compiler error. + * string which then triggers a compiler error. * * Backend files may #define FORBIDDEN_SYMBOL_ALLOW_ALL if they * have to access functions like fopen, fread etc. @@ -203,6 +203,11 @@ #define exit(a) FORBIDDEN_SYMBOL_REPLACEMENT #endif +#ifndef FORBIDDEN_SYMBOL_EXCEPTION_abort +#undef abort +#define abort() FORBIDDEN_SYMBOL_REPLACEMENT +#endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_getenv #undef getenv #define getenv(a) FORBIDDEN_SYMBOL_REPLACEMENT diff --git a/common/hashmap.h b/common/hashmap.h index f2a4d843b8d..347ac1fd257 100644 --- a/common/hashmap.h +++ b/common/hashmap.h @@ -106,8 +106,9 @@ private: HASHMAP_MEMORYPOOL_SIZE = HASHMAP_MIN_CAPACITY * HASHMAP_LOADFACTOR_NUMERATOR / HASHMAP_LOADFACTOR_DENOMINATOR }; - +#ifdef USE_HASHMAP_MEMORY_POOL ObjectPool _nodePool; +#endif Node **_storage; ///< hashtable of size arrsize. uint _mask; ///< Capacity of the HashMap minus one; must be a power of two of minus one @@ -128,12 +129,20 @@ private: #endif Node *allocNode(const Key &key) { +#ifdef USE_HASHMAP_MEMORY_POOL return new (_nodePool) Node(key); +#else + return new Node(key); +#endif } void freeNode(Node *node) { if (node && node != HASHMAP_DUMMY_NODE) +#ifdef USE_HASHMAP_MEMORY_POOL _nodePool.deleteChunk(node); +#else + delete node; +#endif } void assign(const HM_t &map); diff --git a/common/macresman.cpp b/common/macresman.cpp index 6cbc08da192..c1cab8b96ad 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -107,14 +107,17 @@ bool MacResManager::open(String filename) { #ifdef MACOSX // Check the actual fork on a Mac computer String fullPath = ConfMan.get("path") + "/" + filename + "/..namedfork/rsrc"; - SeekableReadStream *macResForkRawStream = FSNode(fullPath).createReadStream();; + FSNode resFsNode = FSNode(fullPath); + if (resFsNode.exists()) { + SeekableReadStream *macResForkRawStream = resFsNode.createReadStream();; - if (macResForkRawStream && loadFromRawFork(*macResForkRawStream)) { - _baseFileName = filename; - return true; + if (macResForkRawStream && loadFromRawFork(*macResForkRawStream)) { + _baseFileName = filename; + return true; + } + + delete macResForkRawStream; } - - delete macResForkRawStream; #endif File *file = new File(); @@ -167,14 +170,17 @@ bool MacResManager::open(FSNode path, String filename) { #ifdef MACOSX // Check the actual fork on a Mac computer String fullPath = path.getPath() + "/" + filename + "/..namedfork/rsrc"; - SeekableReadStream *macResForkRawStream = FSNode(fullPath).createReadStream(); + FSNode resFsNode = FSNode(fullPath); + if (resFsNode.exists()) { + SeekableReadStream *macResForkRawStream = resFsNode.createReadStream();; - if (macResForkRawStream && loadFromRawFork(*macResForkRawStream)) { - _baseFileName = filename; - return true; + if (macResForkRawStream && loadFromRawFork(*macResForkRawStream)) { + _baseFileName = filename; + return true; + } + + delete macResForkRawStream; } - - delete macResForkRawStream; #endif // First, let's try to see if the Mac converted name exists diff --git a/common/ptr.h b/common/ptr.h index e0d026f9645..fc272d3d41a 100644 --- a/common/ptr.h +++ b/common/ptr.h @@ -240,7 +240,7 @@ public: operator bool() const { return _pointer != 0; } ~ScopedPtr() { - delete _pointer; + delete _pointer; } /** diff --git a/common/scummsys.h b/common/scummsys.h index 04e1b52b768..63cd67d0cb1 100644 --- a/common/scummsys.h +++ b/common/scummsys.h @@ -306,6 +306,19 @@ #define MAXPATHLEN 256 #endif +#ifndef scumm_va_copy + #if defined(va_copy) + #define scumm_va_copy va_copy + #elif defined(__va_copy) + #define scumm_va_copy __va_copy + #elif defined(_MSC_VER) + #define scumm_va_copy(dst, src) ((dst) = (src)) + #else + #error scumm_va_copy undefined for this port + #endif +#endif + + // // Typedef our system types unless they have already been defined by config.h, diff --git a/common/singleton.h b/common/singleton.h index 2f5fa41877d..43f1c0c4d03 100644 --- a/common/singleton.h +++ b/common/singleton.h @@ -89,15 +89,13 @@ protected: }; /** - * Note that you need to use this macro from the global namespace. + * Note that you need to use this macro from the Common namespace. * * This is because C++ requires initial explicit specialization * to be placed in the same namespace as the template. - * It has to be put in the global namespace to assure the correct - * namespace Common is referenced. */ #define DECLARE_SINGLETON(T) \ - template<> T *Common::Singleton::_singleton = 0 + template<> T *Singleton::_singleton = 0 } // End of namespace Common diff --git a/common/str.cpp b/common/str.cpp index 223188bdd68..32f4b44e798 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -25,8 +25,6 @@ #include "common/str.h" #include "common/util.h" -#include - namespace Common { MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now @@ -407,7 +405,7 @@ void String::trim() { makeUnique(); // Trim trailing whitespace - while (_size >= 1 && isspace(_str[_size - 1])) + while (_size >= 1 && isspace(static_cast(_str[_size - 1]))) --_size; _str[_size] = 0; @@ -429,10 +427,22 @@ uint String::hash() const { // static String String::format(const char *fmt, ...) { String output; - assert(output.isStorageIntern()); va_list va; va_start(va, fmt); + output = String::vformat(fmt, va); + va_end(va); + + return output; +} + +// static +String String::vformat(const char *fmt, va_list args) { + String output; + assert(output.isStorageIntern()); + + va_list va; + scumm_va_copy(va, args); int len = vsnprintf(output._str, _builtinCapacity, fmt, va); va_end(va); @@ -457,7 +467,7 @@ String String::format(const char *fmt, ...) { assert(!output.isStorageIntern()); size = output._extern._capacity; - va_start(va, fmt); + scumm_va_copy(va, args); len = vsnprintf(output._str, size, fmt, va); va_end(va); } while (len == -1 || len >= size - 1); @@ -468,7 +478,7 @@ String String::format(const char *fmt, ...) { } else { // vsnprintf didn't have enough space, so grow buffer output.ensureCapacity(len, false); - va_start(va, fmt); + scumm_va_copy(va, args); int len2 = vsnprintf(output._str, len+1, fmt, va); va_end(va); assert(len == len2); @@ -596,14 +606,14 @@ String operator+(const String &x, char y) { } char *ltrim(char *t) { - while (isspace(*t)) + while (isspace(static_cast(*t))) t++; return t; } char *rtrim(char *t) { int l = strlen(t) - 1; - while (l >= 0 && isspace(t[l])) + while (l >= 0 && isspace(static_cast(t[l]))) t[l--] = 0; return t; } diff --git a/common/str.h b/common/str.h index 7b97dfe9456..8e07b6233d7 100644 --- a/common/str.h +++ b/common/str.h @@ -24,6 +24,8 @@ #include "common/scummsys.h" +#include + namespace Common { /** @@ -213,10 +215,19 @@ public: uint hash() const; /** - * Printf-like function. Returns a formatted String. + * Print formatted data into a String object. Similar to sprintf, + * except that it stores the result in (variably sized) String + * instead of a fixed size buffer. */ static Common::String format(const char *fmt, ...) GCC_PRINTF(1,2); + /** + * Print formatted data into a String object. Similar to vsprintf, + * except that it stores the result in (variably sized) String + * instead of a fixed size buffer. + */ + static Common::String vformat(const char *fmt, va_list args); + public: typedef char * iterator; typedef const char * const_iterator; diff --git a/common/system.cpp b/common/system.cpp index 1dc34fe4ab3..57648d63673 100644 --- a/common/system.cpp +++ b/common/system.cpp @@ -21,17 +21,13 @@ */ #define FORBIDDEN_SYMBOL_EXCEPTION_exit -#define FORBIDDEN_SYMBOL_EXCEPTION_FILE -#define FORBIDDEN_SYMBOL_EXCEPTION_fputs -#define FORBIDDEN_SYMBOL_EXCEPTION_fflush -#define FORBIDDEN_SYMBOL_EXCEPTION_stdout -#define FORBIDDEN_SYMBOL_EXCEPTION_stderr #include "common/system.h" #include "common/events.h" #include "common/fs.h" #include "common/savefile.h" #include "common/str.h" +#include "common/taskbar.h" #include "common/textconsole.h" #include "backends/audiocd/default/default-audiocd.h" @@ -45,6 +41,9 @@ OSystem::OSystem() { _eventManager = 0; _timerManager = 0; _savefileManager = 0; +#if defined(USE_TASKBAR) + _taskbarManager = 0; +#endif _fsFactory = 0; } @@ -58,6 +57,11 @@ OSystem::~OSystem() { delete _timerManager; _timerManager = 0; +#if defined(USE_TASKBAR) + delete _taskbarManager; + _taskbarManager = 0; +#endif + delete _savefileManager; _savefileManager = 0; @@ -115,20 +119,6 @@ Common::String OSystem::getDefaultConfigFileName() { return "residual.ini"; } -void OSystem::logMessage(LogMessageType::Type type, const char *message) { -#if !defined(__PLAYSTATION2__) && !defined(__DS__) - FILE *output = 0; - - if (type == LogMessageType::kInfo || type == LogMessageType::kDebug) - output = stdout; - else - output = stderr; - - fputs(message, output); - fflush(output); -#endif -} - Common::String OSystem::getSystemLanguage() const { return "en_US"; } diff --git a/common/system.h b/common/system.h index fd708d07846..0a4fd9f3d20 100644 --- a/common/system.h +++ b/common/system.h @@ -42,6 +42,9 @@ struct Rect; class SaveFileManager; class SearchSet; class String; +#if defined(USE_TASKBAR) +class TaskbarManager; +#endif class TimerManager; class SeekableReadStream; class WriteStream; @@ -149,6 +152,15 @@ protected: */ Common::SaveFileManager *_savefileManager; +#if defined(USE_TASKBAR) + /** + * No default value is provided for _savefileManager by OSystem. + * + * @note _savefileManager is deleted by the OSystem destructor. + */ + Common::TaskbarManager *_taskbarManager; +#endif + /** * No default value is provided for _fsFactory by OSystem. * @@ -622,6 +634,18 @@ public: return _savefileManager; } +#if defined(USE_TASKBAR) + /** + * Returns the TaskbarManager, used to handle progress bars, + * icon overlay, tasks and recent items list on the taskbar. + * + * @return the TaskbarManager for the current architecture + */ + virtual Common::TaskbarManager *getTaskbarManager() { + return _taskbarManager; + } +#endif + /** * Returns the FilesystemFactory object, depending on the current architecture. * @@ -676,7 +700,7 @@ public: * @param type the type of the message * @param message the message itself */ - virtual void logMessage(LogMessageType::Type type, const char *message); + virtual void logMessage(LogMessageType::Type type, const char *message) = 0; /** * Open the log file in a way that allows the user to review it, @@ -727,7 +751,7 @@ public: }; -/** The global OSystem instance. Initialised in main(). */ +/** The global OSystem instance. Initialized in main(). */ extern OSystem *g_system; #endif diff --git a/common/taskbar.h b/common/taskbar.h new file mode 100644 index 00000000000..023227e5e00 --- /dev/null +++ b/common/taskbar.h @@ -0,0 +1,133 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef COMMON_TASKBAR_MANAGER_H +#define COMMON_TASKBAR_MANAGER_H + +#include "common/scummsys.h" +#include "common/str.h" + +#if defined(USE_TASKBAR) + +namespace Common { + +/** + * The TaskbarManager allows interaction with the ScummVM application icon: + * - in the taskbar on Windows 7 and later + * - in the launcher for Unity + * - in the dock on MacOSX + * - ... + * + * This allows GUI code and engines to display a progress bar, an overlay icon and/or count + * associated with the ScummVM icon as well as add the started engine to the recent items + * list (so that the user can start the engine directly in one click). + * + * Examples of use: + * - Track search progress and found engines when running the Mass Add dialog + * - Add an entry to the recent items when starting an engine + * - Show the current running engine icon as an overlay + * + * @note functionality will vary between supported platforms (due to API limitations) + * and some of the methods will just be no-ops or approximate the functionality + * as best as possible + */ +class TaskbarManager { +public: + /** + * Values representing the taskbar progress state + */ + enum TaskbarProgressState { + kTaskbarNoProgress = 0, + kTaskbarIndeterminate = 1, + kTaskbarNormal = 2, + kTaskbarError = 4, + kTaskbarPaused = 8 + }; + + TaskbarManager() {} + virtual ~TaskbarManager() {} + + /** + * Sets an overlay icon on the taskbar icon + * + * When an empty name is given, no icon is shown + * and the current overlay icon (if any) is removed + * + * @param name Path to the icon + * @param description The description + * + * @note on Windows, the icon should be an ICO file + */ + virtual void setOverlayIcon(const String &name, const String &description) {} + + /** + * Sets a progress value on the taskbar icon + * + * @param completed The current progress value. + * @param total The maximum progress value. + */ + virtual void setProgressValue(int completed, int total) {} + + /** + * Sets the progress state on the taskbar icon + * + * State can be any of the following: + * - NoProgress: disable display of progress state + * - Indeterminate + * - Normal + * - Error + * - Paused + * + * @param state The progress state + */ + virtual void setProgressState(TaskbarProgressState state) {} + + /** + * Sets the count number associated with the icon as an overlay + * + * @param count The count + * + * @note Setting a count of 0 will hide the count + */ + virtual void setCount(int count) {} + + /** + * Adds an engine to the recent items list + * + * Path is automatically set to the current executable path, + * an icon name is generated (with fallback to default icon) + * and the command line is set to start the engine on click. + * + * @param name The target name. + * @param description The description. + */ + virtual void addRecent(const String &name, const String &description) {} + +}; + +} // End of namespace Common + +#endif + +#endif // COMMON_TASKBAR_MANAGER_H diff --git a/common/textconsole.cpp b/common/textconsole.cpp index f2325ac9ad9..ffa42e63a09 100644 --- a/common/textconsole.cpp +++ b/common/textconsole.cpp @@ -46,14 +46,14 @@ void setErrorHandler(ErrorHandler handler) { #ifndef DISABLE_TEXT_CONSOLE void warning(const char *s, ...) { - char buf[STRINGBUFLEN]; + Common::String output; va_list va; va_start(va, s); - vsnprintf(buf, STRINGBUFLEN, s, va); + output = Common::String::vformat(s, va); va_end(va); - Common::String output = Common::String::format("WARNING: %s!\n", buf); + output = "WARNING: " + output + "!\n"; if (g_system) g_system->logMessage(LogMessageType::kWarning, output.c_str()); @@ -64,6 +64,9 @@ void warning(const char *s, ...) { #endif void NORETURN_PRE error(const char *s, ...) { + // We don't use String::vformat here, as that require + // using the heap, and that might be impossible at this + // point, e.g. if the error was an "out-of-memory" error. char buf_input[STRINGBUFLEN]; char buf_output[STRINGBUFLEN]; va_list va; diff --git a/common/translation.cpp b/common/translation.cpp index 514ae44edff..3e3273c433a 100644 --- a/common/translation.cpp +++ b/common/translation.cpp @@ -37,10 +37,10 @@ #ifdef USE_TRANSLATION -DECLARE_SINGLETON(Common::TranslationManager); - namespace Common { +DECLARE_SINGLETON(TranslationManager); + bool operator<(const TLanguage &l, const TLanguage &r) { return strcmp(l.name, r.name) < 0; } @@ -223,13 +223,21 @@ String TranslationManager::getLangById(int id) const { } bool TranslationManager::openTranslationsFile(File& inFile) { - // First try to open it directly (i.e. using the SearchMan). - if (inFile.open("translations.dat")) + // First look in the Themepath if we can find the file. + if (ConfMan.hasKey("themepath") && openTranslationsFile(FSNode(ConfMan.get("themepath")), inFile)) return true; - // Then look in the Themepath if we can find the file. - if (ConfMan.hasKey("themepath")) - return openTranslationsFile(FSNode(ConfMan.get("themepath")), inFile); + // Then try to open it using the SearchMan. + ArchiveMemberList fileList; + SearchMan.listMatchingMembers(fileList, "translations.dat"); + for (ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) { + SeekableReadStream *stream = it->get()->createReadStream(); + if (stream && inFile.open(stream, it->get()->getName())) { + if (checkHeader(inFile)) + return true; + inFile.close(); + } + } return false; } @@ -243,8 +251,11 @@ bool TranslationManager::openTranslationsFile(const FSNode &node, File& inFile, // necessary to make them here. But it avoid printing warnings. FSNode fileNode = node.getChild("translations.dat"); if (fileNode.exists() && fileNode.isReadable() && !fileNode.isDirectory()) { - if (inFile.open(fileNode)) - return true; + if (inFile.open(fileNode)) { + if (checkHeader(inFile)) + return true; + inFile.close(); + } } // Check if we exceeded the given recursion depth @@ -268,13 +279,10 @@ bool TranslationManager::openTranslationsFile(const FSNode &node, File& inFile, void TranslationManager::loadTranslationsInfoDat() { File in; if (!openTranslationsFile(in)) { - warning("You are missing the 'translations.dat' file. GUI translation will not be available"); + warning("You are missing a valid 'translations.dat' file. GUI translation will not be available"); return; } - if (!checkHeader(in)) - return; - char buf[256]; int len; @@ -302,8 +310,13 @@ void TranslationManager::loadTranslationsInfoDat() { _messageIds.resize(numMessages); for (int i = 0; i < numMessages; ++i) { len = in.readUint16BE(); - in.read(buf, len); - _messageIds[i] = String(buf, len - 1); + String msg; + while (len > 0) { + in.read(buf, len > 256 ? 256 : len); + msg += String(buf, len > 256 ? 256 : len - 1); + len -= 256; + } + _messageIds[i] = msg; } } @@ -321,9 +334,6 @@ void TranslationManager::loadLanguageDat(int index) { if (!openTranslationsFile(in)) return; - if (!checkHeader(in)) - return; - char buf[1024]; int len; @@ -357,8 +367,13 @@ void TranslationManager::loadLanguageDat(int index) { for (int i = 0; i < nbMessages; ++i) { _currentTranslationMessages[i].msgid = in.readUint16BE(); len = in.readUint16BE(); - in.read(buf, len); - _currentTranslationMessages[i].msgstr = String(buf, len - 1); + String msg; + while (len > 0) { + in.read(buf, len > 256 ? 256 : len); + msg += String(buf, len > 256 ? 256 : len - 1); + len -= 256; + } + _currentTranslationMessages[i].msgstr = msg; len = in.readUint16BE(); if (len > 0) { in.read(buf, len); @@ -376,7 +391,7 @@ bool TranslationManager::checkHeader(File &in) { // Check header if (strcmp(buf, "TRANSLATIONS")) { - warning("Your 'translations.dat' file is corrupt. GUI translation will not be available"); + warning("File '%s' is not a valid translations data file. Skipping this file", in.getName()); return false; } @@ -384,7 +399,7 @@ bool TranslationManager::checkHeader(File &in) { ver = in.readByte(); if (ver != TRANSLATIONS_DAT_VER) { - warning("Your 'translations.dat' file has a mismatching version, expected was %d but you got %d. GUI translation will not be available", TRANSLATIONS_DAT_VER, ver); + warning("File '%s' has a mismatching version, expected was %d but you got %d. Skipping this file", in.getName(), TRANSLATIONS_DAT_VER, ver); return false; } diff --git a/common/util.h b/common/util.h index e3f6246d01b..232473f65ee 100644 --- a/common/util.h +++ b/common/util.h @@ -213,11 +213,6 @@ enum RenderMode { kRenderAmiga = 5 }; -enum HerculesDimensions { - kHercW = 720, - kHercH = 350 -}; - struct RenderModeDescription { const char *code; const char *description; diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp index 5217c4e82ce..623619914a4 100644 --- a/common/xmlparser.cpp +++ b/common/xmlparser.cpp @@ -263,7 +263,7 @@ bool XMLParser::vparseIntegerKey(const char *key, int count, va_list args) { int *num_ptr; while (count--) { - while (isspace(*key)) + while (isspace(static_cast(*key))) key++; num_ptr = va_arg(args, int*); @@ -271,7 +271,7 @@ bool XMLParser::vparseIntegerKey(const char *key, int count, va_list args) { key = parseEnd; - while (isspace(*key)) + while (isspace(static_cast(*key))) key++; if (count && *key++ != ',') @@ -463,10 +463,10 @@ bool XMLParser::parse() { } bool XMLParser::skipSpaces() { - if (!isspace(_char)) + if (!isspace(static_cast(_char))) return false; - while (_char && isspace(_char)) + while (_char && isspace(static_cast(_char))) _char = _stream->readByte(); return true; @@ -516,7 +516,7 @@ bool XMLParser::parseToken() { _char = _stream->readByte(); } - return isspace(_char) != 0 || _char == '>' || _char == '=' || _char == '/'; + return isspace(static_cast(_char)) != 0 || _char == '>' || _char == '=' || _char == '/'; } } // End of namespace Common diff --git a/common/xmlparser.h b/common/xmlparser.h index 7923e43a371..d75dc0e4a9e 100644 --- a/common/xmlparser.h +++ b/common/xmlparser.h @@ -31,6 +31,7 @@ #include "common/hashmap.h" #include "common/hash-str.h" #include "common/stack.h" +#include "common/memorypool.h" namespace Common { @@ -294,7 +295,7 @@ protected: * in their name. */ virtual inline bool isValidNameChar(char c) { - return isalnum(c) || c == '_'; + return isalnum(static_cast(c)) || c == '_'; } /** diff --git a/configure b/configure index 30d0f878534..74eb71a7df7 100755 --- a/configure +++ b/configure @@ -65,8 +65,12 @@ get_var() { # Add an engine: id name build subengines add_engine() { _engines="${_engines} ${1}" + if test "${3}" = "no" ; then + set_var _wip_engines "${_wip_engines} ${1}" + fi set_var _engine_${1}_name "${2}" set_var _engine_${1}_build "${3}" + set_var _engine_${1}_build_default "${3}" set_var _engine_${1}_subengines "${4}" for sub in ${4}; do set_var _engine_${sub}_sub "yes" @@ -94,6 +98,8 @@ _fluidsynth=auto _opengl=auto _opengles=no _readline=auto +_taskbar=yes +_libunity=auto # Default option behaviour yes/no _debug_build=auto _release_build=auto @@ -122,6 +128,7 @@ _strip=strip _ar="ar cru" _as="as" _windres=windres +_stagingpath="staging" _win32path="C:/residual" _aos4path="Games:Residual" _staticlibpath=/sw @@ -130,6 +137,7 @@ _sdlpath="$PATH" _nasmpath="$PATH" NASMFLAGS="" NASM="" +_tainted_build=no # The following variables are automatically detected, and should not # be modified otherwise. Consider them read-only. _posix=no @@ -377,6 +385,11 @@ get_engine_build() { get_var _engine_$1_build } +# Was this engine set to be built by default? +get_engine_build_default() { + get_var _engine_$1_build_default +} + # Get the subengines get_engine_subengines() { get_var _engine_$1_subengines @@ -496,12 +509,19 @@ prepare_engine_build_strings() { if test -n "$string" ; then _engines_skipped="${_engines_skipped}#$string@" fi + + string=`get_engine_build_string $1 wip` + if test -n "$string" ; then + _engines_built_wip="${_engines_built_wip}#$string@" + fi + } # Get the string about building an engine get_engine_build_string() { engine_string="" engine_build=`get_engine_build $1` + engine_build_default=`get_engine_build_default $1` show=no # Check if the current engine should be shown for the current status @@ -517,6 +537,14 @@ get_engine_build_string() { fi done fi + # Test for enabled wip sub-engines + if test $2 = wip ; then + for subeng in `get_engine_subengines $1` ; do + if test `get_engine_build $subeng` != no -a `get_engine_build_default $subeng` = no ; then + show=yes + fi + done + fi fi # Convert static/dynamic to yes to ease the check of subengines @@ -524,13 +552,18 @@ get_engine_build_string() { engine_build=yes fi + # Check if it is a wip engine + if test "$2" = "wip" -a "$engine_build" != "no" -a "$engine_build_default" = no; then + show=yes + fi + # The engine should be shown, build the string if test $show = yes ; then build_string_func=get_${1}_build_string if ( type $build_string_func | grep function ) 2> /dev/null > /dev/null ; then - engine_string=`$build_string_func $1 $engine_build` + engine_string=`$build_string_func $1 $engine_build $2` else - engine_string=`get_subengines_build_string $1 $engine_build` + engine_string=`get_subengines_build_string $1 $engine_build "" $2` fi engine_string="`get_engine_name $1` $engine_string" @@ -542,14 +575,29 @@ get_engine_build_string() { # Get the string about building subengines get_subengines_build_string() { all=yes + parent_engine=$1 subengine_string=$3 - for subeng in `get_engine_subengines $1` ; do - if test `get_engine_build $subeng` = $2 ; then + parent_status=$4 + parent_engine_build_default=`get_engine_build_default $parent_engine` + + for subeng in `get_engine_subengines $parent_engine` ; do + subengine_build=`get_engine_build $subeng` + subengine_build_default=`get_engine_build_default $subeng` + if test \( $subengine_build = $2 -a "$parent_status" != wip \) -o \( "$parent_status" = wip -a $subengine_build != no -a "$subengine_build_default" = no \) ; then subengine_string="$subengine_string [`get_engine_name $subeng`]" else all=no fi + + # handle engines that are on by default and have a single subengine that is off by default + if test "$parent_status" = wip ; then + if test $parent_engine_build_default = yes -a subengine ; then + all=no + fi + fi + done + if test $2 != no ; then if test -n "$subengine_string" ; then if test $all = yes ; then @@ -633,6 +681,7 @@ Optional Features: --default-dynamic make plugins dynamic by default --disable-mt32emu don't enable the integrated MT-32 emulator --disable-translation don't build support for translated messages + --disable-taskbar don't build support for taskbar and launcher integration --enable-text-console use text console instead of graphical console --enable-verbose-build enable regular echoing of commands during build process @@ -674,6 +723,9 @@ Optional Libraries: --with-readline-prefix=DIR Prefix where readline is installed (optional) --disable-readline disable readline support in text console [autodetect] + --with-libunity-prefix=DIR Prefix where libunity is installed (optional) + --disable-libunity disable Unity launcher integration [autodetect] + Some influential environment variables: LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory @@ -711,6 +763,10 @@ for ac_option in $@; do --disable-fluidsynth) _fluidsynth=no ;; --enable-readline) _readline=yes ;; --disable-readline) _readline=no ;; + --enable-taskbar) _taskbar=yes ;; + --disable-taskbar) _taskbar=no ;; + --enable-libunity) _libunity=yes ;; + --disable-libunity) _libunity=no ;; --enable-opengl) _opengl=yes ;; --disable-opengl) _opengl=no ;; --enable-verbose-build) _verbose_build=yes ;; @@ -871,15 +927,6 @@ caanoo) _host_os=gph-linux _host_cpu=arm _host_alias=arm-none-linux-gnueabi - if test "$_debug_build" = auto; then - # If you want to debug on the Caanoo use '--disable-release --enable-debug' - _debug_build=no - fi - - if test "$_release_build" = auto; then - # Enable release build by default. - _release_build=yes - fi ;; dingux) _host_os=linux @@ -959,6 +1006,22 @@ ps2) _host_cpu=mips64r5900el _host_alias=ee ;; +ps3) + _host_os=ps3 + _host_cpu=ppc + _host_alias=powerpc64-ps3-elf + + # The prefix is always the same on PS3 so we hardcode the default + # here. It is still possible to define a custom prefix which is + # needed when packaging the app with a user-specific app ID. + test "x$prefix" = xNONE && prefix=/dev_hdd0/game/SCUM12000/USRDIR + # PS3 apps are installed into app-specific directories. The + # default directory structure of ScummVM makes no sense here so we + # hardcode PS3 specific directories here. + datarootdir='${prefix}/data' + datadir='${datarootdir}' + docdir='${prefix}/doc' + ;; psp) _host_os=psp _host_cpu=mipsallegrexel @@ -1100,6 +1163,12 @@ ps2) exit 1 fi ;; +ps3) + if test -z "$PS3DEV"; then + echo "Please set PS3DEV in your environment. export PS3DEV=" + exit 1 + fi + ;; psp) if test -z "$PSPDEV"; then PSPDEV=`psp-config --pspdev-path` @@ -1292,7 +1361,7 @@ if test "$have_gcc" = yes ; then case $_host_os in # newlib-based system include files suppress non-C89 function # declarations under __STRICT_ANSI__ - amigaos* | android | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | wii | wince ) + amigaos* | android | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | wii | wince ) ;; *) CXXFLAGS="$CXXFLAGS -ansi" @@ -1697,7 +1766,8 @@ case $_host_os in mingw*) DEFINES="$DEFINES -DWIN32" DEFINES="$DEFINES -D__USE_MINGW_ANSI_STDIO=0" - LIBS="$LIBS -lmingw32 -lwinmm" + LDFLAGS="$LDFLAGS -static-libgcc -static-libstdc++" + LIBS="$LIBS -lmingw32 -lwinmm -lgdi32" OBJS="$OBJS residualwinres.o" add_line_to_config_mk 'WIN32 = 1' ;; @@ -1721,6 +1791,16 @@ case $_host_os in DEFINES="$DEFINES -D_EE" DEFINES="$DEFINES -D__PLAYSTATION2__" ;; + ps3) + # Force use of SDL from the ps3 toolchain + _sdlpath="$PS3DEV/portlibs/ppu:$PS3DEV/portlibs/ppu/bin" + + DEFINES="$DEFINES -DPLAYSTATION3" + CXXFLAGS="$CXXFLAGS -mcpu=cell -mminimal-toc -I$PS3DEV/psl1ght/ppu/include -I$PS3DEV/portlibs/ppu/include" + LDFLAGS="$LDFLAGS -L$PS3DEV/psl1ght/ppu/lib -L$PS3DEV/portlibs/ppu/lib" + add_line_to_config_mk 'PLAYSTATION3 = 1' + add_line_to_config_h "#define PREFIX \"${prefix}\"" + ;; psp) if test -d "$PSPDEV/psp/lib"; then LDFLAGS="$LDFLAGS -L$PSPDEV/psp/lib" @@ -2078,6 +2158,11 @@ if test -n "$_host"; then CXXFLAGS="$CXXFLAGS -s" fi ;; + ps3) + _mt32emu=no + _timidity=no + _vkeybd=yes + ;; psp) _backend="psp" _build_scalers=no @@ -2120,6 +2205,7 @@ if test -n "$_host"; then LDFLAGS="$LDFLAGS -Wl,--stack,65536" _tremolo=yes _backend="wince" + _detectlang=yes _mt32emu=no _port_mk="backends/platform/wince/wince.mk" ;; @@ -2134,16 +2220,9 @@ fi # case $_backend in android) - # ssp at this point so the cxxtests link - if test "$_debug_build" = yes; then - CXXFLAGS="$CXXFLAGS -fstack-protector" - else - CXXFLAGS="$CXXFLAGS -fno-stack-protector" - fi + DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" CXXFLAGS="$CXXFLAGS -Wa,--noexecstack" LDFLAGS="$LDFLAGS -Wl,-z,noexecstack" - - DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" ;; dc) INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/dc' @@ -2267,6 +2346,15 @@ case $_backend in LIBS="$LIBS `$_sdlconfig --prefix="$_sdlpath" --libs`" DEFINES="$DEFINES -DSDL_BACKEND" add_line_to_config_mk "SDL_BACKEND = 1" + + _sdlversion=`$_sdlconfig --version` + case $_sdlversion in + 1.3.*) + add_line_to_config_mk "USE_SDL13 = 1" + ;; + *) + ;; + esac ;; esac @@ -2298,7 +2386,7 @@ esac # echo_n "Checking if host is POSIX compliant... " case $_host_os in - amigaos* | cygwin* | dreamcast | ds | gamecube | mingw* | n64 | ps2 | psp | wii | wince) + amigaos* | cygwin* | dreamcast | ds | gamecube | mingw* | n64 | ps2 | ps3 | psp | wii | wince) _posix=no ;; android | beos* | bsd* | darwin* | freebsd* | gph-linux | haiku* | hpux* | iphone | irix* | linux* | mint* | netbsd* | openbsd* | solaris* | sunos* | uclinux* | webos) @@ -2824,6 +2912,43 @@ define_in_config_h_if_yes "$_readline" 'USE_READLINE' define_in_config_h_if_yes "$_text_console" 'USE_TEXT_CONSOLE_FOR_DEBUGGER' +# +# Check for Unity if taskbar integration is enabled +# +echocheck "libunity" +if test "$_unix" = no || test "$_taskbar" = no; then + _libunity=no +else +if test "$_libunity" = auto ; then + case $_host_os in + mingw*) + # pkgconfig and unity are not supported on mingw + _libunity=no + ;; + *) + # Unity has a lots of dependencies, update the libs and cflags var with them + LIBUNITY_LIBS="$LIBUNITY_LIBS $(pkg-config --libs unity = 3.8.4 2>> "$TMPLOG")" + LIBUNITY_CFLAGS="$LIBUNITY_CFLAGS $(pkg-config --cflags unity = 3.8.4 2>> "$TMPLOG")" + _libunity=no + cat > $TMPC << EOF +#include +int main(void) { + unity_launcher_entry_get_for_desktop_id("scummvm.desktop"); + return 0; +} +EOF + cc_check $LIBUNITY_CFLAGS $LIBUNITY_LIBS && _libunity=yes + ;; + esac +fi +if test "$_libunity" = yes ; then + LIBS="$LIBS $LIBUNITY_LIBS" + INCLUDES="$INCLUDES $LIBUNITY_CFLAGS" +fi +define_in_config_h_if_yes "$_libunity" 'USE_TASKBAR_UNITY' +fi +echo "$_libunity" + # # Check for OpenGL (ES) # @@ -2977,12 +3102,14 @@ if test "$_translation" = no ; then else echo_n "yes (" - cat > $TMPC << EOF + if test "$_detectlang" != yes ; then + cat > $TMPC << EOF #include int main(void) { setlocale(LC_ALL, ""); return 0; } EOF - _detectlang=no - cc_check $LDFLAGS $CXXFLAGS && _detectlang=yes + _detectlang=no + cc_check $LDFLAGS $CXXFLAGS && _detectlang=yes + fi define_in_config_h_if_yes $_detectlang 'USE_DETECTLANG' if test "$_detectlang" = yes ; then @@ -2992,6 +3119,29 @@ EOF fi fi +# +# Check whether to build taskbar integration support +# +echo_n "Building taskbar integration support... " +define_in_config_if_yes $_taskbar 'USE_TASKBAR' +if test "$_taskbar" = yes; then + case $_host_os in + mingw*) + LIBS="$LIBS -lole32 -luuid" + echo "win32" + ;; + *) + if test "$_libunity" = yes; then + echo "unity" + else + echo "$_taskbar" + fi + ;; + esac +else + echo "$_taskbar" +fi + # # Figure out installation directories # @@ -3028,6 +3178,10 @@ fi echo_n "Backend... " echo_n "$_backend" +if test "$_backend" = "sdl" -a -n "$_sdlversion"; then + echo_n " ($_sdlversion)" +fi + if test "$_nasm" = yes ; then echo_n ", assembly routines" fi @@ -3055,6 +3209,13 @@ fi # case $_backend in android) + # ssp at this point so the cxxtests link + if test "$_debug_build" = yes; then + CXXFLAGS="$CXXFLAGS -fstack-protector" + else + CXXFLAGS="$CXXFLAGS -fno-stack-protector" + fi + static_libs='' system_libs='' for lib in $LIBS; do @@ -3135,6 +3296,23 @@ for engine in $_engines; do fi done +# +# Detection of WIP/unstable engines +# +for engine in $_engines; do + engine_build=`get_engine_build $engine` + engine_build_default=`get_engine_build_default $engine` + engine_wip=false + if test $engine_build != no -a $engine_build_default = no ; then + engine_wip=true + set_var _tainted_build "yes" + fi + engine_wip_defname="ENGINE_WIP_`echo $engine | tr '[a-z]' '[A-Z]'`" + add_line_to_config_h "#define $engine_wip_defname $engine_wip" +done + +add_to_config_h_if_yes `get_var _tainted_build` '#define TAINTED_BUILD' + # # Show which engines ("frontends") are to be built # @@ -3160,6 +3338,12 @@ if test -n "$_engines_skipped" ; then s/#/ /g' fi +if test -n "$_engines_built_wip" ; then + echo "WARNING: This Residual build contains the following UNSTABLE engines:" + echo $_engines_built_wip | sed 's/@/\ +/g +s/#/ /g' +fi echo echo "Creating config.h" @@ -3215,6 +3399,7 @@ AS := $_as ASFLAGS := $ASFLAGS WINDRES := $_windres WINDRESFLAGS := $WINDRESFLAGS +STAGINGPATH=$_stagingpath WIN32PATH=$_win32path AOS4PATH=$_aos4path STATICLIBPATH=$_staticlibpath diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index 9e4988a4cf5..8910ad32894 100644 --- a/devtools/create_project/create_project.cpp +++ b/devtools/create_project/create_project.cpp @@ -364,6 +364,11 @@ int main(int argc, char *argv[]) { provider = new CreateProjectTool::CodeBlocksProvider(globalWarnings, projectWarnings); + + // Those libraries are automatically added by MSVC, but we need to add them manually with mingw + setup.libraries.push_back("ole32"); + setup.libraries.push_back("uuid"); + break; case kProjectMSVC: @@ -774,7 +779,7 @@ const Feature s_features[] = { { "mt32emu", "USE_MT32EMU", "", true, "integrated MT-32 emulator" }, { "nasm", "USE_NASM", "", true, "IA-32 assembly support" }, // This feature is special in the regard, that it needs additional handling. { "opengl", "USE_OPENGL", "opengl32", true, "OpenGL support" }, - { "indeo3", "USE_INDEO3", "", true, "Indeo3 codec support"}, + { "taskbar", "USE_TASKBAR", "", true, "Taskbar integration support" }, { "translation", "USE_TRANSLATION", "", false, "Translation support" }, { "vkeybd", "ENABLE_VKEYBD", "", false, "Virtual keyboard support"}, { "langdetect", "USE_DETECTLANG", "", true, "System language detection support" } // This feature actually depends on "translation", there diff --git a/devtools/create_project/scripts/installer.vbs b/devtools/create_project/scripts/installer.vbs index d752355acdd..867b06cfaf5 100644 --- a/devtools/create_project/scripts/installer.vbs +++ b/devtools/create_project/scripts/installer.vbs @@ -72,10 +72,9 @@ Sub CreateInstaller() ' Build command line Dim commandLine : commandLine = """" & nsisPath & "\makensis.exe"" /V2" & _ " /Dtop_srcdir=""" & rootFolder & """" & _ - " /Dbuild_dir=""" & targetFolder & """" & _ - " /Dtext_dir=""" & rootFolder & """" & _ + " /Dstaging_dir=""" & targetFolder & """" & _ " /DARCH=""" & arch & """" & _ - " """ & rootFolder & "\dists\nsis\scummvm.nsi""" + " """ & rootFolder & "\dists\win32\residual.nsi""" Dim oExec: Set oExec = WshShell.Exec(commandline) If Err.Number <> 0 Then diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp index 7402c3e37de..1c091c1cbeb 100644 --- a/engines/advancedDetector.cpp +++ b/engines/advancedDetector.cpp @@ -32,112 +32,7 @@ #include "common/translation.h" #include "engines/advancedDetector.h" - -/** - * A list of pointers to ADGameDescription structs (or subclasses thereof). - */ -typedef Common::Array ADGameDescList; - - -/** - * Detect games in specified directory. - * Parameters language and platform are used to pass on values - * specified by the user. I.e. this is used to restrict search scope. - * - * @param fslist FSList to scan or NULL for scanning all specified - * default directories. - * @param params a ADParams struct containing various parameters - * @param language restrict results to specified language only - * @param platform restrict results to specified platform only - * @return list of ADGameDescription (or subclass) pointers corresponding to matched games - */ -static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams ¶ms, Common::Language language, Common::Platform platform, const Common::String &extra); - - -/** - * Returns list of targets supported by the engine. - * Distinguishes engines with single ID - */ -static GameList gameIDList(const ADParams ¶ms) { - if (params.singleid != NULL) { - GameList gl; - - const PlainGameDescriptor *g = params.list; - while (g->gameid) { - if (0 == scumm_stricmp(params.singleid, g->gameid)) { - gl.push_back(GameDescriptor(g->gameid, g->description)); - - return gl; - } - g++; - } - error("Engine %s doesn't have its singleid specified in ids list", params.singleid); - } - - return GameList(params.list); -} - -static void upgradeTargetIfNecessary(const ADParams ¶ms) { - if (params.obsoleteList == 0) - return; - - Common::String gameid = ConfMan.get("gameid"); - - for (const ADObsoleteGameID *o = params.obsoleteList; o->from; ++o) { - if (gameid.equalsIgnoreCase(o->from)) { - gameid = o->to; - ConfMan.set("gameid", gameid); - - if (o->platform != Common::kPlatformUnknown) - ConfMan.set("platform", Common::getPlatformCode(o->platform)); - - warning("Target upgraded from %s to %s", o->from, o->to); - - // WORKAROUND: Fix for bug #1719463: "DETECTOR: Launching - // undefined target adds launcher entry" - if (ConfMan.hasKey("id_came_from_command_line")) { - warning("Target came from command line. Skipping save"); - } else { - ConfMan.flushToDisk(); - } - break; - } - } -} - -namespace AdvancedDetector { - -GameDescriptor findGameID( - const char *gameid, - const PlainGameDescriptor *list, - const ADObsoleteGameID *obsoleteList - ) { - // First search the list of supported game IDs for a match. - const PlainGameDescriptor *g = findPlainGameDescriptor(gameid, list); - if (g) - return GameDescriptor(*g); - - // If we didn't find the gameid in the main list, check if it - // is an obsolete game id. - if (obsoleteList != 0) { - const ADObsoleteGameID *o = obsoleteList; - while (o->from) { - if (0 == scumm_stricmp(gameid, o->from)) { - g = findPlainGameDescriptor(o->to, list); - if (g && g->description) - return GameDescriptor(gameid, "Obsolete game ID (" + Common::String(g->description) + ")"); - else - return GameDescriptor(gameid, "Obsolete game ID"); - } - o++; - } - } - - // No match found - return GameDescriptor(); -} - -} // End of namespace AdvancedDetector +#include "engines/obsolete.h" static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGameDescriptor *sg) { const char *title = 0; @@ -156,7 +51,13 @@ static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGa extra = g.extra; } - GameDescriptor gd(g.gameid, title, g.language, g.platform); + GameSupportLevel gsl = kStableGame; + if (g.flags & ADGF_UNSTABLE) + gsl = kUnstableGame; + else if (g.flags & ADGF_TESTING) + gsl = kTestingGame; + + GameDescriptor gd(g.gameid, title, g.language, g.platform, 0, gsl); gd.updateDesc(extra); return gd; } @@ -189,10 +90,10 @@ static Common::String generatePreferredTarget(const Common::String &id, const AD return res; } -static void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc, const ADParams ¶ms) { - if (params.singleid != NULL) { +void AdvancedMetaEngine::updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const { + if (_singleid != NULL) { desc["preferredtarget"] = desc["gameid"]; - desc["gameid"] = params.singleid; + desc["gameid"] = _singleid; } if (!desc.contains("preferredtarget")) @@ -200,10 +101,10 @@ static void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription * desc["preferredtarget"] = generatePreferredTarget(desc["preferredtarget"], realDesc); - if (params.flags & kADFlagUseExtraAsHint) + if (_flags & kADFlagUseExtraAsHint) desc["extra"] = realDesc->extra; - desc.setGUIOptions(realDesc->guioptions | params.guioptions); + desc.setGUIOptions(realDesc->guioptions | _guioptions); desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(realDesc->language)); if (realDesc->flags & ADGF_ADDENGLISH) @@ -211,7 +112,7 @@ static void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription * } bool cleanupPirated(ADGameDescList &matched) { - // OKay, now let's sense presense of pirated games + // OKay, now let's sense presence of pirated games if (!matched.empty()) { for (uint j = 0; j < matched.size();) { if (matched[j]->flags & ADGF_PIRATED) @@ -222,9 +123,7 @@ bool cleanupPirated(ADGameDescList &matched) { // We ruled out all variants and now have nothing if (matched.empty()) { - warning("Illegitimate game copy detected. We give no support in such cases %d", matched.size()); - return true; } } @@ -234,25 +133,33 @@ bool cleanupPirated(ADGameDescList &matched) { GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const { - ADGameDescList matches = detectGame(fslist, params, Common::UNK_LANG, Common::kPlatformUnknown, ""); + ADGameDescList matches; GameList detectedGames; + FileMap allFiles; - if (cleanupPirated(matches)) + if (fslist.empty()) return detectedGames; + // Compose a hashmap of all files in fslist. + composeFileHashMap(allFiles, fslist, (_maxScanDepth == 0 ? 1 : _maxScanDepth)); + + // Run the detector on this + matches = detectGame(fslist.begin()->getParent(), allFiles, Common::UNK_LANG, Common::kPlatformUnknown, ""); + if (matches.empty()) { // Use fallback detector if there were no matches by other means - const ADGameDescription *fallbackDesc = fallbackDetect(fslist); + const ADGameDescription *fallbackDesc = fallbackDetect(allFiles, fslist); if (fallbackDesc != 0) { - GameDescriptor desc(toGameDescriptor(*fallbackDesc, params.list)); - updateGameDescriptor(desc, fallbackDesc, params); + GameDescriptor desc(toGameDescriptor(*fallbackDesc, _gameids)); + updateGameDescriptor(desc, fallbackDesc); detectedGames.push_back(desc); } } else { // Otherwise use the found matches + cleanupPirated(matches); for (uint i = 0; i < matches.size(); i++) { - GameDescriptor desc(toGameDescriptor(*matches[i], params.list)); - updateGameDescriptor(desc, matches[i], params); + GameDescriptor desc(toGameDescriptor(*matches[i], _gameids)); + updateGameDescriptor(desc, matches[i]); detectedGames.push_back(desc); } } @@ -262,7 +169,6 @@ GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const { Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) const { assert(engine); - upgradeTargetIfNecessary(params); const ADGameDescription *agdDesc = 0; Common::Language language = Common::UNK_LANG; @@ -273,9 +179,10 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) language = Common::parseLanguage(ConfMan.get("language")); if (ConfMan.hasKey("platform")) platform = Common::parsePlatform(ConfMan.get("platform")); - if (params.flags & kADFlagUseExtraAsHint) + if (_flags & kADFlagUseExtraAsHint) { if (ConfMan.hasKey("extra")) extra = ConfMan.get("extra"); + } Common::String gameid = ConfMan.get("gameid"); @@ -305,12 +212,21 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) return Common::kNoGameDataFoundError; } - ADGameDescList matches = detectGame(files, params, language, platform, extra); + if (files.empty()) + return Common::kNoGameDataFoundError; + + // Compose a hashmap of all files in fslist. + FileMap allFiles; + composeFileHashMap(allFiles, files, (_maxScanDepth == 0 ? 1 : _maxScanDepth)); + + // Run the detector on this + ADGameDescList matches = detectGame(files.begin()->getParent(), allFiles, language, platform, extra); if (cleanupPirated(matches)) return Common::kNoGameDataFoundError; - if (params.singleid == NULL) { + if (_singleid == NULL) { + // Find the first match with correct gameid. for (uint i = 0; i < matches.size(); i++) { if (matches[i]->gameid == gameid) { agdDesc = matches[i]; @@ -323,11 +239,11 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) if (agdDesc == 0) { // Use fallback detector if there were no matches by other means - agdDesc = fallbackDetect(files); + agdDesc = fallbackDetect(allFiles, files); if (agdDesc != 0) { // Seems we found a fallback match. But first perform a basic // sanity check: the gameid must match. - if (params.singleid == NULL && agdDesc->gameid != gameid) + if (_singleid == NULL && agdDesc->gameid != gameid) agdDesc = 0; } } @@ -341,10 +257,23 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) if (agdDesc->flags & ADGF_ADDENGLISH) lang += " " + getGameGUIOptionsDescriptionLanguage(Common::EN_ANY); - Common::updateGameGUIOptions(agdDesc->guioptions | params.guioptions, lang); + Common::updateGameGUIOptions(agdDesc->guioptions | _guioptions, lang); + GameDescriptor gameDescriptor = toGameDescriptor(*agdDesc, _gameids); - debug(2, "Running %s", toGameDescriptor(*agdDesc, params.list).description().c_str()); + bool showTestingWarning = false; + +#ifdef RELEASE_BUILD + showTestingWarning = true; +#endif + + if (((gameDescriptor.getSupportLevel() == kUnstableGame + || (gameDescriptor.getSupportLevel() == kTestingGame + && showTestingWarning))) + && !Engine::warnUserAboutUnsupportedGame()) + return Common::kUserCanceled; + + debug(2, "Running %s", gameDescriptor.description().c_str()); if (!createInstance(syst, engine, agdDesc)) return Common::kNoGameDataFoundError; else @@ -357,7 +286,6 @@ struct SizeMD5 { }; typedef Common::HashMap SizeMD5Map; -typedef Common::HashMap FileMap; static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSizeMD5) { // TODO: This message should be cleaned up / made more specific. @@ -379,26 +307,22 @@ static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSiz g_system->logMessage(LogMessageType::kInfo, report.c_str()); } -static ADGameDescList detectGameFilebased(const FileMap &allFiles, const ADParams ¶ms); - -static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles, int depth, const char * const *directoryGlobs) { +void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth) const { if (depth <= 0) return; if (fslist.empty()) return; - // First we compose a hashmap of all files in fslist. - // Includes nifty stuff like removing trailing dots and ignoring case. for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { if (file->isDirectory()) { Common::FSList files; - if (!directoryGlobs) + if (!_directoryGlobs) continue; bool matched = false; - for (const char * const *glob = directoryGlobs; *glob; glob++) + for (const char * const *glob = _directoryGlobs; *glob; glob++) if (file->getName().matchString(*glob, true)) { matched = true; break; @@ -410,7 +334,7 @@ static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles, if (!file->getChildren(files, Common::FSNode::kListAll)) continue; - composeFileHashMap(files, allFiles, depth - 1, directoryGlobs); + composeFileHashMap(allFiles, files, depth - 1); } Common::String tstr = file->getName(); @@ -423,26 +347,18 @@ static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles, } } -static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams ¶ms, Common::Language language, Common::Platform platform, const Common::String &extra) { - FileMap allFiles; +ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const { SizeMD5Map filesSizeMD5; const ADGameFileDescription *fileDesc; const ADGameDescription *g; const byte *descPtr; - if (fslist.empty()) - return ADGameDescList(); - Common::FSNode parent = fslist.begin()->getParent(); debug(3, "Starting detection in dir '%s'", parent.getPath().c_str()); - // First we compose a hashmap of all files in fslist. - // Includes nifty stuff like removing trailing dots and ignoring case. - composeFileHashMap(fslist, allFiles, (params.depth == 0 ? 1 : params.depth), params.directoryGlobs); - - // Check which files are included in some ADGameDescription *and* present - // in fslist. Compute MD5s and file sizes for these files. - for (descPtr = params.descs; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += params.descItemSize) { + // Check which files are included in some ADGameDescription *and* are present. + // Compute MD5s and file sizes for these files. + for (descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += _descItemSize) { g = (const ADGameDescription *)descPtr; for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) { @@ -456,16 +372,14 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p // file and as one with resource fork. if (g->flags & ADGF_MACRESFORK) { - Common::MacResManager *macResMan = new Common::MacResManager(); + Common::MacResManager macResMan; - if (macResMan->open(parent, fname)) { - tmp.md5 = macResMan->computeResForkMD5AsString(params.md5Bytes); - tmp.size = macResMan->getResForkDataSize(); + if (macResMan.open(parent, fname)) { + tmp.md5 = macResMan.computeResForkMD5AsString(_md5Bytes); + tmp.size = macResMan.getResForkDataSize(); debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str()); filesSizeMD5[fname] = tmp; } - - delete macResMan; } else { if (allFiles.contains(fname)) { debug(3, "+ %s", fname.c_str()); @@ -474,7 +388,7 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p if (testFile.open(allFiles[fname])) { tmp.size = (int32)testFile.size(); - tmp.md5 = Common::computeStreamMD5AsString(testFile, params.md5Bytes); + tmp.md5 = Common::computeStreamMD5AsString(testFile, _md5Bytes); } else { tmp.size = -1; } @@ -492,7 +406,7 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p // MD5 based matching uint i; - for (i = 0, descPtr = params.descs; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += params.descItemSize, ++i) { + for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += _descItemSize, ++i) { g = (const ADGameDescription *)descPtr; bool fileMissing = false; @@ -504,7 +418,7 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p continue; } - if ((params.flags & kADFlagUseExtraAsHint) && !extra.empty() && g->extra != extra) + if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && g->extra != extra) continue; bool allFilesPresent = true; @@ -577,28 +491,20 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p } // Filename based fallback - if (params.fileBasedFallback != 0) - matched = detectGameFilebased(allFiles, params); } return matched; } -/** - * Check for each ADFileBasedFallback record whether all files listed - * in it are present. If multiple pass this test, we pick the one with - * the maximal number of matching files. In case of a tie, the entry - * coming first in the list is chosen. - */ -static ADGameDescList detectGameFilebased(const FileMap &allFiles, const ADParams ¶ms) { +const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap &allFiles, const ADFileBasedFallback *fileBasedFallback) const { const ADFileBasedFallback *ptr; const char* const* filenames; int maxNumMatchedFiles = 0; const ADGameDescription *matchedDesc = 0; - for (ptr = params.fileBasedFallback; ptr->desc; ++ptr) { - const ADGameDescription *agdesc = (const ADGameDescription *)ptr->desc; + for (ptr = fileBasedFallback; ptr->desc; ++ptr) { + const ADGameDescription *agdesc = ptr->desc; int numMatchedFiles = 0; bool fileMissing = false; @@ -624,28 +530,45 @@ static ADGameDescList detectGameFilebased(const FileMap &allFiles, const ADParam } } - ADGameDescList matched; - - if (matchedDesc) { // We got a match - matched.push_back(matchedDesc); - if (params.flags & kADFlagPrintWarningOnFileBasedFallback) { - Common::String report = Common::String::format(_("Your game version has been detected using " - "filename matching as a variant of %s."), matchedDesc->gameid); - report += "\n"; - report += _("If this is an original and unmodified version, please report any"); - report += "\n"; - report += _("information previously printed by Residual to the team."); - report += "\n"; - g_system->logMessage(LogMessageType::kInfo, report.c_str()); - } - } - - return matched; + return matchedDesc; } GameList AdvancedMetaEngine::getSupportedGames() const { - return gameIDList(params); + if (_singleid != NULL) { + GameList gl; + + const PlainGameDescriptor *g = _gameids; + while (g->gameid) { + if (0 == scumm_stricmp(_singleid, g->gameid)) { + gl.push_back(GameDescriptor(g->gameid, g->description)); + + return gl; + } + g++; + } + error("Engine %s doesn't have its singleid specified in ids list", _singleid); + } + + return GameList(_gameids); } + GameDescriptor AdvancedMetaEngine::findGame(const char *gameid) const { - return AdvancedDetector::findGameID(gameid, params.list, params.obsoleteList); + // First search the list of supported gameids for a match. + const PlainGameDescriptor *g = findPlainGameDescriptor(gameid, _gameids); + if (g) + return GameDescriptor(*g); + + // No match found + return GameDescriptor(); +} + +AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameids) + : _gameDescriptors((const byte *)descs), _descItemSize(descItemSize), _gameids(gameids) { + + _md5Bytes = 5000; + _singleid = NULL; + _flags = 0; + _guioptions = Common::GUIO_NONE; + _maxScanDepth = 1; + _directoryGlobs = NULL; } diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index 408c46556d5..cbdfdf39d88 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -19,38 +19,59 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ + #ifndef ENGINES_ADVANCED_DETECTOR_H #define ENGINES_ADVANCED_DETECTOR_H #include "engines/metaengine.h" +#include "engines/engine.h" namespace Common { class Error; class FSList; } - +/** + * A record describing a file to be matched for detecting a specific game + * variant. A list of such records is used inside every ADGameDescription to + * enable detection. + */ struct ADGameFileDescription { - const char *fileName; - uint16 fileType; // Optional. Not used during detection, only by engines. - const char *md5; // Optional. May be NULL. - int32 fileSize; // Optional. Set to -1 to ignore. + const char *fileName; ///< Name of described file. + uint16 fileType; ///< Optional. Not used during detection, only by engines. + const char *md5; ///< MD5 of (the beginning of) the described file. Optional. Set to NULL to ignore. + int32 fileSize; ///< Size of the described file. Set to -1 to ignore. }; +/** + * A shortcut to produce an empty ADGameFileDescription record. Used to mark + * the end of a list of these. + */ #define AD_LISTEND {NULL, 0, NULL, 0} +/** + * A shortcut to produce a list of ADGameFileDescription records with only one + * record that contains just a filename with an MD5, and no file size. + */ #define AD_ENTRY1(f, x) {{ f, 0, x, -1}, AD_LISTEND} + +/** + * A shortcut to produce a list of ADGameFileDescription records with only one + * record that contains just a filename with an MD5, plus a file size. + */ #define AD_ENTRY1s(f, x, s) {{ f, 0, x, s}, AD_LISTEND} enum ADGameFlags { ADGF_NO_FLAGS = 0, - ADGF_PIRATED = (1 << 23), // flag to designate well known pirated versions with cracks - ADGF_ADDENGLISH = (1 << 24), // always add English as language option - ADGF_MACRESFORK = (1 << 25), // the md5 for this entry will be calculated from the resource fork - ADGF_USEEXTRAASTITLE = (1 << 26), // Extra field value will be used as main game title, not gameid - ADGF_DROPLANGUAGE = (1 << 28), // don't add language to gameid - ADGF_CD = (1 << 29), // add "-cd" to gameid - ADGF_DEMO = (1 << 30) // add "-demo" to gameid + ADGF_UNSTABLE = (1 << 21), // flag to designate not yet officially-supported games that are not fit for public testing + ADGF_TESTING = (1 << 22), // flag to designate not yet officially-supported games that are fit for public testing + ADGF_PIRATED = (1 << 23), ///< flag to designate well known pirated versions with cracks + ADGF_ADDENGLISH = (1 << 24), ///< always add English as language option + ADGF_MACRESFORK = (1 << 25), ///< the md5 for this entry will be calculated from the resource fork + ADGF_USEEXTRAASTITLE = (1 << 26), ///< Extra field value will be used as main game title, not gameid + ADGF_DROPLANGUAGE = (1 << 28), ///< don't add language to gameid + ADGF_CD = (1 << 29), ///< add "-cd" to gameid + ADGF_DEMO = (1 << 30) ///< add "-demo" to gameid }; struct ADGameDescription { @@ -70,6 +91,11 @@ struct ADGameDescription { uint32 guioptions; }; +/** + * A list of pointers to ADGameDescription structs (or subclasses thereof). + */ +typedef Common::Array ADGameDescList; + /** * End marker for a table of ADGameDescription structs. Use this to * terminate a list to be passed to the AdvancedDetector API. @@ -77,19 +103,12 @@ struct ADGameDescription { #define AD_TABLE_END_MARKER \ { NULL, NULL, { { NULL, 0, NULL, 0 } }, Common::UNK_LANG, Common::kPlatformUnknown, ADGF_NO_FLAGS, Common::GUIO_NONE } - -struct ADObsoleteGameID { - const char *from; - const char *to; - Common::Platform platform; -}; - struct ADFileBasedFallback { /** * Pointer to an ADGameDescription or subclass thereof which will get * returned if there's a detection match. */ - const void *desc; + const ADGameDescription *desc; /** * A zero-terminated list of filenames used for matching. All files in @@ -100,39 +119,42 @@ struct ADFileBasedFallback { enum ADFlags { - /** - * Warn user about new variant if his version was detected with fallback - */ - kADFlagPrintWarningOnFileBasedFallback = (1 << 1), /** * Store value of extra field in config file, and use it as a hint * on subsequent runs. Could be used when there is no way to autodetect * game (when more than one game sits in same directory), and user picks * up a variant manually. + * In addition, this is useful if two variants of a game sharing the same + * gameid are contained in a single directory. */ - kADFlagUseExtraAsHint = (1 << 2) + kADFlagUseExtraAsHint = (1 << 0) }; + /** - * A structure containing all parameters for the AdvancedDetector. - * Typically, an engine will have a single instance of this which is - * used by its AdvancedMetaEngine subclass as a parameter to the - * primary AdvancedMetaEngine constructor. + * A MetaEngine implementation based around the advanced detector code. */ -struct ADParams { +class AdvancedMetaEngine : public MetaEngine { +protected: /** * Pointer to an array of objects which are either ADGameDescription * or superset structures (i.e. start with an ADGameDescription member. * The list is terminated by an entry with a gameid equal to 0 * (see AD_TABLE_END_MARKER). */ - const byte *descs; + const byte *_gameDescriptors; /** * The size of a single entry of the above descs array. Always * must be >= sizeof(ADGameDescription). */ - uint descItemSize; + const uint _descItemSize; + + /** + * A list of all gameids (and their corresponding descriptions) supported + * by this engine. + */ + const PlainGameDescriptor *_gameids; /** * The number of bytes to compute MD5 sum for. The AdvancedDetector @@ -140,110 +162,109 @@ struct ADParams { * Since doing that for large files can be slow, it can be restricted * to a subset of all files. * Typically this will be set to something between 5 and 50 kilobyte, - * but arbitrary non-zero values are possible. + * but arbitrary non-zero values are possible. The default is 5000. */ - uint md5Bytes; - - /** - * A list of all gameids (and their corresponding descriptions) supported - * by this engine. - */ - const PlainGameDescriptor *list; - - /** - * Structure for autoupgrading obsolete targets (optional). - * - * @todo Properly explain this. - */ - const ADObsoleteGameID *obsoleteList; + uint _md5Bytes; /** * Name of single gameid (optional). * * @todo Properly explain this -- what does it do? */ - const char *singleid; - - /** - * List of files for file-based fallback detection (optional). - * This is used if the regular MD5 based detection failed to - * detect anything. - * As usual this list is terminated by an all-zero entry. - * - * @todo Properly explain this - */ - const ADFileBasedFallback *fileBasedFallback; + const char *_singleid; /** * A bitmask of flags which can be used to configure the behavior * of the AdvancedDetector. Refer to ADFlags for a list of flags * that can be ORed together and passed here. */ - uint32 flags; + uint32 _flags; /** * A bitmask of game GUI options which will be added to each * entry in addition to per-game options. Refer to GameGUIOption * enum for the list. */ - uint32 guioptions; + uint32 _guioptions; /** - * Maximum depth of directories to look up + * Maximum depth of directories to look up. * If set to 0, the depth is 1 level */ - uint32 depth; + uint32 _maxScanDepth; /** * Case-insensitive list of directory globs which could be used for - * going deeper int directory structure. + * going deeper into the directory structure. * @see String::matchString() method for format description. * * @note Last item must be 0 */ - const char * const *directoryGlobs; -}; + const char * const *_directoryGlobs; - -namespace AdvancedDetector { - -/** - * Scan through the game descriptors specified in params and search for - * 'gameid' in there. If a match is found, returns a GameDescriptor - * with gameid and description set. - */ -GameDescriptor findGameID( - const char *gameid, - const PlainGameDescriptor *list, - const ADObsoleteGameID *obsoleteList = 0 - ); - -} // End of namespace AdvancedDetector - -/** - * A MetaEngine implementation based around the advanced detector code. - */ -class AdvancedMetaEngine : public MetaEngine { - const ADParams ¶ms; public: - AdvancedMetaEngine(const ADParams &dp) : params(dp) {} + AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameids); + /** + * Returns list of targets supported by the engine. + * Distinguishes engines with single ID + */ virtual GameList getSupportedGames() const; + virtual GameDescriptor findGame(const char *gameid) const; + virtual GameList detectGames(const Common::FSList &fslist) const; + virtual Common::Error createInstance(OSystem *syst, Engine **engine) const; - // To be provided by subclasses +protected: + // To be implemented by subclasses virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const = 0; + typedef Common::HashMap FileMap; + /** * An (optional) generic fallback detect function which is invoked - * if both the regular MD5 based detection as well as the file - * based fallback failed to detect anything. + * if the regular MD5 based detection failed to detect anything. */ - virtual const ADGameDescription *fallbackDetect(const Common::FSList &fslist) const { + virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { return 0; } + +protected: + /** + * Detect games in specified directory. + * Parameters language and platform are used to pass on values + * specified by the user. This is used to restrict search scope. + * + * @param allFiles list of all present files, as computed by composeFileHashMap + * @param language restrict results to specified language + * @param platform restrict results to specified platform + * @param extra restrict results to specified extra string (only if kADFlagUseExtraAsHint is set) + * @return list of ADGameDescription pointers corresponding to matched games + */ + ADGameDescList detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const; + + /** + * Iterates over all ADFileBasedFallback records inside fileBasedFallback. + * This then returns the record (or rather, the ADGameDescription + * contained in it) for which all files described by it are present, and + * among those the one with the maximal number of matching files. + * In case of a tie, the entry coming first in the list is chosen. + * + * @param allFiles a map describing all present files + * @param fileBasedFallback a list of ADFileBasedFallback records, zero-terminated + */ + const ADGameDescription *detectGameFilebased(const FileMap &allFiles, const ADFileBasedFallback *fileBasedFallback) const; + + // TODO + void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const; + + /** + * Compose a hashmap of all files in fslist. + * Includes nifty stuff like removing trailing dots and ignoring case. + */ + void composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth) const; }; #endif diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp index 90b2fe95043..8b890be0708 100644 --- a/engines/dialogs.cpp +++ b/engines/dialogs.cpp @@ -143,9 +143,9 @@ void MainMenuDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint3 break; case kHelpCmd: { GUI::MessageDialog dialog( - "Sorry, this engine does not currently provide in-game help. " + _("Sorry, this engine does not currently provide in-game help. " "Please consult the README for basic information, and for " - "instructions on how to obtain further assistance."); + "instructions on how to obtain further assistance.")); dialog.runModal(); } break; @@ -173,7 +173,7 @@ void MainMenuDialog::reflowLayout() { _loadButton->setEnabled(_engine->canLoadGameStateCurrently()); if (_engine->hasFeature(Engine::kSupportsSavingDuringRuntime)) _saveButton->setEnabled(_engine->canSaveGameStateCurrently()); - + // Overlay size might have changed since the construction of the dialog. // Update labels when it might be needed // FIXME: it might be better to declare GUI::StaticTextWidget::setLabel() virtual @@ -246,14 +246,10 @@ void MainMenuDialog::load() { int slot = _loadDialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); - if (slot >= 0) { - // FIXME: For now we just ignore the return - // value, which is quite bad since it could - // be a fatal loading error, which renders - // the engine unusable. - _engine->loadGameState(slot); + _engine->setGameToLoadSlot(slot); + + if (slot >= 0) close(); - } } enum { diff --git a/engines/engine.cpp b/engines/engine.cpp index ec8306d998d..656a8b622ab 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -42,6 +42,7 @@ #include "common/list_intern.h" #include "common/scummsys.h" #include "common/textconsole.h" +#include "common/translation.h" #include "gui/debugger.h" #include "gui/dialog.h" @@ -95,6 +96,7 @@ Engine::Engine(OSystem *syst) _targetName(ConfMan.getActiveDomainName()), _pauseLevel(0), _pauseStartTime(0), + _saveSlotToLoad(-1), _engineStartTime(_system->getMillis()), _mainMenuDialog(NULL) { @@ -112,6 +114,19 @@ Engine::Engine(OSystem *syst) // there still would be problems with many games... if (!_mixer->isReady()) warning("Sound initialization failed. This may cause severe problems in some games"); + + // Setup a dummy cursor and palette, so that all engines can use + // CursorMan.replace without having any headaches about memory leaks. + // + // If an engine only used CursorMan.replaceCursor and no cursor has + // been setup before, then replaceCursor just uses pushCursor. This + // means that that the engine's cursor is never again removed from + // CursorMan. Hence we setup a fake cursor here and remove it again + // in the destructor. + CursorMan.pushCursor(NULL, 0, 0, 0, 0, 0); + // Note: Using this dummy palette will actually disable cursor + // palettes till the user enables it again. + CursorMan.pushCursorPalette(NULL, 0, 0); } Engine::~Engine() { @@ -178,22 +193,22 @@ void Engine::checkCD() { if (GetDriveType(buffer) == DRIVE_CDROM) { GUI::MessageDialog dialog( - "You appear to be playing this game directly\n" + _("You appear to be playing this game directly\n" "from the CD. This is known to cause problems,\n" "and it is therefore recommended that you copy\n" "the data files to your hard disk instead.\n" - "See the README file for details.", "OK"); + "See the README file for details."), _("OK")); dialog.runModal(); } else { // If we reached here, the game has audio tracks, // it's not ran from the CD and the tracks have not // been ripped. GUI::MessageDialog dialog( - "This game has audio tracks in its disk. These\n" + _("This game has audio tracks in its disk. These\n" "tracks need to be ripped from the disk using\n" "an appropriate CD audio extracting tool in\n" "order to listen to the game's music.\n" - "See the README file for details.", "OK"); + "See the README file for details."), _("OK")); dialog.runModal(); } #endif @@ -235,10 +250,36 @@ void Engine::pauseEngineIntern(bool pause) { void Engine::openMainMenuDialog() { if (!_mainMenuDialog) _mainMenuDialog = new MainMenuDialog(this); + + setGameToLoadSlot(-1); + runDialog(*_mainMenuDialog); + + // Load savegame after main menu execution + // (not from inside the menu loop to avoid + // mouse cursor glitches and simliar bugs, + // e.g. #2822778). + // FIXME: For now we just ignore the return + // value, which is quite bad since it could + // be a fatal loading error, which renders + // the engine unusable. + if (_saveSlotToLoad >= 0) + loadGameState(_saveSlotToLoad); + syncSoundSettings(); } +bool Engine::warnUserAboutUnsupportedGame() { + if (ConfMan.getBool("enable_unsupported_game_warning")) { + GUI::MessageDialog alert(_("WARNING: The game you are about to start is" + " not yet fully supported by Residual. As such, it is likely to be" + " unstable, and any saves you make might not work in future" + " versions of Residual."), _("Start anyway"), _("Cancel")); + return alert.runModal() == GUI::kMessageOK; + } + return true; +} + uint32 Engine::getTotalPlayTime() const { if (!_pauseLevel) return _system->getMillis() - _engineStartTime; @@ -265,6 +306,10 @@ int Engine::runDialog(GUI::Dialog &dialog) { return result; } +void Engine::setGameToLoadSlot(int slot) { + _saveSlotToLoad = slot; +} + void Engine::syncSoundSettings() { // Sync the engine with the config manager int soundVolumeMusic = ConfMan.getInt("music_volume"); diff --git a/engines/engine.h b/engines/engine.h index d7d971ad97f..2796df5c4f2 100644 --- a/engines/engine.h +++ b/engines/engine.h @@ -82,6 +82,13 @@ private: */ int32 _engineStartTime; + /** + * Save slot selected via global main menu. + * This slot will be loaded after main menu execution (not from inside + * the menu loop, to avoid bugs like #2822778). + */ + int _saveSlotToLoad; + public: @@ -185,6 +192,15 @@ public: */ virtual Common::Error loadGameState(int slot); + /** + * Sets the game slot for a savegame to be loaded after global + * main menu execution. This is to avoid loading a savegame from + * inside the menu loop which causes bugs like #2822778. + * + * @param slot the slot from which a savestate should be loaded. + */ + void setGameToLoadSlot(int slot); + /** * Indicates whether a game state can be loaded. */ @@ -251,6 +267,13 @@ public: */ void openMainMenuDialog(); + /** + * Display a warning to the user that the game is not fully supported. + * + * @return true if the user chose to start anyway, false otherwise + */ + static bool warnUserAboutUnsupportedGame(); + /** * Get the total play time. * diff --git a/engines/game.cpp b/engines/game.cpp index a14edb8af4d..8ea68bb6811 100644 --- a/engines/game.cpp +++ b/engines/game.cpp @@ -38,20 +38,15 @@ GameDescriptor::GameDescriptor() { setVal("description", ""); } -GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd) { - setVal("gameid", pgd.gameid); - setVal("description", pgd.description); -} - -GameDescriptor::GameDescriptor(const PlainGameDescriptorGUIOpts &pgd) { +GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd, uint32 guioptions) { setVal("gameid", pgd.gameid); setVal("description", pgd.description); - if (pgd.guioptions != 0) - setVal("guioptions", Common::getGameGUIOptionsDescription(pgd.guioptions)); + if (guioptions != 0) + setVal("guioptions", Common::getGameGUIOptionsDescription(guioptions)); } -GameDescriptor::GameDescriptor(const Common::String &g, const Common::String &d, Common::Language l, Common::Platform p, uint32 guioptions) { +GameDescriptor::GameDescriptor(const Common::String &g, const Common::String &d, Common::Language l, Common::Platform p, uint32 guioptions, GameSupportLevel gsl) { setVal("gameid", g); setVal("description", d); if (l != Common::UNK_LANG) @@ -60,6 +55,8 @@ GameDescriptor::GameDescriptor(const Common::String &g, const Common::String &d, setVal("platform", Common::getPlatformCode(p)); if (guioptions != 0) setVal("guioptions", Common::getGameGUIOptionsDescription(guioptions)); + + setSupportLevel(gsl); } void GameDescriptor::setGUIOptions(uint32 guioptions) { @@ -74,9 +71,6 @@ void GameDescriptor::appendGUIOptions(const Common::String &str) { } void GameDescriptor::updateDesc(const char *extra) { - // TODO: The format used here (LANG/PLATFORM/EXTRA) is not set in stone. - // We may want to change the order (PLATFORM/EXTRA/LANG, anybody?), or - // the seperator (instead of '/' use ', ' or ' '). const bool hasCustomLanguage = (language() != Common::UNK_LANG); const bool hasCustomPlatform = (platform() != Common::kPlatformUnknown); const bool hasExtraDesc = (extra && extra[0]); @@ -102,3 +96,30 @@ void GameDescriptor::updateDesc(const char *extra) { setVal("description", descr); } } + +GameSupportLevel GameDescriptor::getSupportLevel() { + GameSupportLevel gsl = kStableGame; + if (contains("gsl")) { + Common::String gslString = getVal("gsl"); + if (gslString.equals("unstable")) + gsl = kUnstableGame; + else if (gslString.equals("testing")) + gsl = kTestingGame; + } + return gsl; +} + +void GameDescriptor::setSupportLevel(GameSupportLevel gsl) { + switch (gsl) { + case kUnstableGame: + setVal("gsl", "unstable"); + break; + case kTestingGame: + setVal("gsl", "testing"); + break; + case kStableGame: + // Fall Through intended + default: + erase("gsl"); + } +} diff --git a/engines/game.h b/engines/game.h index f9988c29650..9082d937931 100644 --- a/engines/game.h +++ b/engines/game.h @@ -39,24 +39,22 @@ struct PlainGameDescriptor { const char *description; }; -/** - * Same as PlainGameDsscriptor except it adds Game GUI options parameter - * This is a plain struct to make it possible to declare NULL-terminated C arrays - * consisting of PlainGameDescriptors. - */ -struct PlainGameDescriptorGUIOpts { - const char *gameid; - const char *description; - uint32 guioptions; -}; - /** * Given a list of PlainGameDescriptors, returns the first PlainGameDescriptor * matching the given gameid. If not match is found return 0. - * The end of the list must marked by a PlainGameDescriptor with gameid equal to 0. + * The end of the list must be marked by an entry with gameid 0. */ const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list); +/** + * Ths is an enum to describe how done a game is. This also indicates what level of support is expected. + */ +enum GameSupportLevel { + kStableGame = 0, // the game is fully supported + kTestingGame, // the game is not supposed to end up in releases yet but is ready for public testing + kUnstableGame // the game is not even ready for public testing yet +}; + /** * A hashmap describing details about a given game. In a sense this is a refined * version of PlainGameDescriptor, as it also contains a gameid and a description string. @@ -67,22 +65,30 @@ const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const Pla class GameDescriptor : public Common::StringMap { public: GameDescriptor(); - GameDescriptor(const PlainGameDescriptor &pgd); - GameDescriptor(const PlainGameDescriptorGUIOpts &pgd); + GameDescriptor(const PlainGameDescriptor &pgd, uint32 guioptions = 0); GameDescriptor(const Common::String &gameid, const Common::String &description, Common::Language language = Common::UNK_LANG, Common::Platform platform = Common::kPlatformUnknown, - uint32 guioptions = 0); + uint32 guioptions = 0, + GameSupportLevel gsl = kStableGame); /** - * Update the description string by appending (LANG/PLATFORM/EXTRA) to it. + * Update the description string by appending (EXTRA/PLATFORM/LANG) to it. + * Values that are missing are omitted, so e.g. (EXTRA/LANG) would be + * added if no platform has been specified but a language and an extra string. */ void updateDesc(const char *extra = 0); void setGUIOptions(uint32 options); void appendGUIOptions(const Common::String &str); + /** + * What level of support is expected of this game + */ + GameSupportLevel getSupportLevel(); + void setSupportLevel(GameSupportLevel gsl); + Common::String &gameid() { return getVal("gameid"); } Common::String &description() { return getVal("description"); } const Common::String &gameid() const { return getVal("gameid"); } @@ -102,7 +108,7 @@ public: GameList(const GameList &list) : Common::Array(list) {} GameList(const PlainGameDescriptor *g) { while (g->gameid) { - push_back(GameDescriptor(g->gameid, g->description)); + push_back(GameDescriptor(*g)); g++; } } diff --git a/engines/grim/detection.cpp b/engines/grim/detection.cpp index b23abf7fc7b..5d7a491a351 100644 --- a/engines/grim/detection.cpp +++ b/engines/grim/detection.cpp @@ -21,6 +21,7 @@ */ #include "engines/advancedDetector.h" +#include "engines/obsolete.h" #include "engines/grim/grim.h" #include "engines/grim/colormap.h" @@ -362,39 +363,21 @@ static const GrimGameDescription gameDescriptions[] = { { AD_TABLE_END_MARKER, GType_GRIM } }; -static const ADObsoleteGameID obsoleteGameIDsTable[] = { +static const Engines::ObsoleteGameID obsoleteGameIDsTable[] = { {"grimdemo", "grim", Common::kPlatformWindows}, {0, 0, Common::kPlatformUnknown} }; -static const ADParams detectionParams = { - // Pointer to ADGameDescription or its superset structure - (const byte *)gameDescriptions, - // Size of that superset structure - sizeof(GrimGameDescription), - // Number of bytes to compute MD5 sum for - 5000, - // List of all engine targets - grimGames, - // Structure for autoupgrading obsolete targets - obsoleteGameIDsTable, - // Name of single gameid (optional) - 0, - // List of files for file-based fallback detection (optional) - 0, - // Flags - 0, - // Additional GUI options (for every game} - Common::GUIO_NOMIDI, - // Maximum directory depth - 1, - // List of directory globs - 0 -}; - class GrimMetaEngine : public AdvancedMetaEngine { public: - GrimMetaEngine() : AdvancedMetaEngine(detectionParams) {} + GrimMetaEngine() : AdvancedMetaEngine(Grim::gameDescriptions, sizeof(Grim::GrimGameDescription), grimGames) { + _singleid = "grim"; + _guioptions = Common::GUIO_NOMIDI; + } + + virtual GameDescriptor findGame(const char *gameid) const { + return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable); + } virtual const char *getName() const { return "Grim Engine"; @@ -404,9 +387,15 @@ public: return "LucasArts GrimE Games (C) LucasArts"; } + virtual Common::Error createInstance(OSystem *syst, Engine **engine) const { + Engines::upgradeTargetIfNecessary(obsoleteGameIDsTable); + return AdvancedMetaEngine::createInstance(syst, engine); + } + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; virtual bool hasFeature(MetaEngineFeature f) const; + virtual SaveStateList listSaves(const char *target) const; }; diff --git a/engines/module.mk b/engines/module.mk index 643310002f2..7849c2ff25f 100644 --- a/engines/module.mk +++ b/engines/module.mk @@ -5,6 +5,7 @@ MODULE_OBJS := \ dialogs.o \ engine.o \ game.o \ + obsolete.o \ savestate.o # Include common rules diff --git a/engines/obsolete.cpp b/engines/obsolete.cpp new file mode 100644 index 00000000000..6733a384be4 --- /dev/null +++ b/engines/obsolete.cpp @@ -0,0 +1,88 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "engines/obsolete.h" + +#include "common/config-manager.h" + + +namespace Engines { + +void upgradeTargetIfNecessary(const ObsoleteGameID *obsoleteList) { + if (obsoleteList == 0) + return; + + Common::String gameid = ConfMan.get("gameid"); + + for (const ObsoleteGameID *o = obsoleteList; o->from; ++o) { + if (gameid.equalsIgnoreCase(o->from)) { + gameid = o->to; + ConfMan.set("gameid", gameid); + + if (o->platform != Common::kPlatformUnknown) + ConfMan.set("platform", Common::getPlatformCode(o->platform)); + + warning("Target upgraded from %s to %s", o->from, o->to); + + // WORKAROUND: Fix for bug #1719463: "DETECTOR: Launching + // undefined target adds launcher entry" + if (ConfMan.hasKey("id_came_from_command_line")) { + warning("Target came from command line. Skipping save"); + } else { + ConfMan.flushToDisk(); + } + break; + } + } +} + +GameDescriptor findGameID( + const char *gameid, + const PlainGameDescriptor *gameids, + const ObsoleteGameID *obsoleteList + ) { + // First search the list of supported gameids for a match. + const PlainGameDescriptor *g = findPlainGameDescriptor(gameid, gameids); + if (g) + return GameDescriptor(*g); + + // If we didn't find the gameid in the main list, check if it + // is an obsolete game id. + if (obsoleteList != 0) { + const ObsoleteGameID *o = obsoleteList; + while (o->from) { + if (0 == scumm_stricmp(gameid, o->from)) { + g = findPlainGameDescriptor(o->to, gameids); + if (g && g->description) + return GameDescriptor(gameid, "Obsolete game ID (" + Common::String(g->description) + ")"); + else + return GameDescriptor(gameid, "Obsolete game ID"); + } + o++; + } + } + + // No match found + return GameDescriptor(); +} + +} // End of namespace Engines diff --git a/engines/obsolete.h b/engines/obsolete.h new file mode 100644 index 00000000000..97bc7524a61 --- /dev/null +++ b/engines/obsolete.h @@ -0,0 +1,78 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ENGINES_OBSOLETE_H +#define ENGINES_OBSOLETE_H + +#include "engines/game.h" + +namespace Engines { + +/** + * Structure for autoupgrading targets using an obsolete gameid + * to the correct new gameid. + */ +struct ObsoleteGameID { + + /** Name of the obsolete gameid. */ + const char *from; + + /** Name of the corresponding new gameid. */ + const char *to; + + /** + * If platform is set to a value different from Common::kPlatformUnknown, + * then upgradeTargetIfNecessary() will use this value to set the platform + * attribute of any target it updates using this ObsoleteGameID record. + * This is useful when the old gameid encoded the target platform (e.g. + * "zakTowns" for FM-TOWNS) while the new gameid does not (e.g. "zak"). + */ + Common::Platform platform; +}; + +/** + * Check if the currently active game target has an obsolete gameid; + * if so, replace it by the correct new gameid. + * This function is typically invoked by a MetaEngine::createInstance + * implementation. + */ +void upgradeTargetIfNecessary(const ObsoleteGameID *obsoleteList); + + +/** + * Scan through the given list of plain game descriptors specified and search + * for 'gameid' in there. If a match is found, returns a GameDescriptor + * with gameid and description set. + * + * Optionally can take a list of obsolete game ids into account in order + * to support obsolete gameids. + */ +GameDescriptor findGameID( + const char *gameid, + const PlainGameDescriptor *gameids, + const ObsoleteGameID *obsoleteList = 0 + ); + + +} // End of namespace Engines + +#endif diff --git a/engines/savestate.cpp b/engines/savestate.cpp index 551c39b8804..0b187ce6304 100644 --- a/engines/savestate.cpp +++ b/engines/savestate.cpp @@ -24,6 +24,17 @@ #include "graphics/surface.h" #include "common/textconsole.h" +SaveStateDescriptor::SaveStateDescriptor() + // FIXME: default to 0 (first slot) or to -1 (invalid slot) ? + : _slot(-1), _description(), _isDeletable(true), _isWriteProtected(false), + _saveDate(), _saveTime(), _playTime(), _thumbnail() { +} + +SaveStateDescriptor::SaveStateDescriptor(int s, const Common::String &d) + : _slot(s), _description(d), _isDeletable(true), _isWriteProtected(false), + _saveDate(), _saveTime(), _playTime(), _thumbnail() { +} + void SaveStateDescriptor::setThumbnail(Graphics::Surface *t) { if (_thumbnail.get() == t) return; @@ -31,42 +42,16 @@ void SaveStateDescriptor::setThumbnail(Graphics::Surface *t) { _thumbnail = Common::SharedPtr(t, Graphics::SharedPtrSurfaceDeleter()); } -bool SaveStateDescriptor::getBool(const Common::String &key) const { - if (contains(key)) { - const Common::String value = getVal(key); - bool valueAsBool; - if (Common::parseBool(value, valueAsBool)) - return valueAsBool; - error("SaveStateDescriptor: %s '%s' has unknown value '%s' for boolean '%s'", - save_slot().c_str(), description().c_str(), value.c_str(), key.c_str()); - } - return false; -} - -void SaveStateDescriptor::setDeletableFlag(bool state) { - setVal("is_deletable", state ? "true" : "false"); -} - -void SaveStateDescriptor::setWriteProtectedFlag(bool state) { - setVal("is_write_protected", state ? "true" : "false"); -} - void SaveStateDescriptor::setSaveDate(int year, int month, int day) { - Common::String buffer; - buffer = Common::String::format("%.2d.%.2d.%.4d", day, month, year); - setVal("save_date", buffer); + _saveDate = Common::String::format("%.2d.%.2d.%.4d", day, month, year); } void SaveStateDescriptor::setSaveTime(int hour, int min) { - Common::String buffer; - buffer = Common::String::format("%.2d:%.2d", hour, min); - setVal("save_time", buffer); + _saveTime = Common::String::format("%.2d:%.2d", hour, min); } void SaveStateDescriptor::setPlayTime(int hours, int minutes) { - Common::String buffer; - buffer = Common::String::format("%.2d:%.2d", hours, minutes); - setVal("play_time", buffer); + _playTime = Common::String::format("%.2d:%.2d", hours, minutes); } void SaveStateDescriptor::setPlayTime(uint32 msecs) { diff --git a/engines/savestate.h b/engines/savestate.h index df017320584..6cbdb22edf1 100644 --- a/engines/savestate.h +++ b/engines/savestate.h @@ -24,7 +24,7 @@ #define ENGINES_SAVESTATE_H #include "common/array.h" -#include "common/hash-str.h" +#include "common/str.h" #include "common/ptr.h" @@ -33,65 +33,60 @@ struct Surface; } /** - * A hashmap describing details about a given save state. - * TODO - * Guaranteed to contain save_slot and description values. - * Additional ideas: Playtime, creation date, thumbnail, ... + * Object describing a save state. + * + * This at least includes the save slot number and a human readable + * description of the save state. + * + * Further possibilites are a thumbnail, play time, creation date, + * creation time, delete protected, write protection. */ -class SaveStateDescriptor : public Common::StringMap { -protected: - Common::SharedPtr _thumbnail; // can be 0 - +class SaveStateDescriptor { public: - SaveStateDescriptor() : _thumbnail() { - setVal("save_slot", "-1"); // FIXME: default to 0 (first slot) or to -1 (invalid slot) ? - setVal("description", ""); - } + SaveStateDescriptor(); + SaveStateDescriptor(int s, const Common::String &d); - SaveStateDescriptor(int s, const Common::String &d) : _thumbnail() { - setVal("save_slot", Common::String::format("%d", s)); - setVal("description", d); - } + /** + * @param slot The saveslot id, as it would be passed to the "-x" command line switch. + */ + void setSaveSlot(int slot) { _slot = slot; } - SaveStateDescriptor(const Common::String &s, const Common::String &d) : _thumbnail() { - setVal("save_slot", s); - setVal("description", d); - } + /** + * @return The saveslot id, as it would be passed to the "-x" command line switch. + */ + int getSaveSlot() const { return _slot; } - /** The saveslot id, as it would be passed to the "-x" command line switch. */ - Common::String &save_slot() { return getVal("save_slot"); } + /** + * @param desc A human readable description of the save state. + */ + void setDescription(const Common::String &desc) { _description = desc; } - /** The saveslot id, as it would be passed to the "-x" command line switch (read-only variant). */ - const Common::String &save_slot() const { return getVal("save_slot"); } - - /** A human readable description of the save state. */ - Common::String &description() { return getVal("description"); } - - /** A human readable description of the save state (read-only variant). */ - const Common::String &description() const { return getVal("description"); } + /** + * @return A human readable description of the save state. + */ + const Common::String &getDescription() const { return _description; } /** Optional entries only included when querying via MetaEngine::querySaveMetaInfo */ /** - * Returns the value of a given key as boolean. - * It accepts 'true', 'yes' and '1' for true and - * 'false', 'no' and '0' for false. - * (FIXME:) On unknown value it errors out ScummVM. - * On unknown key it returns false as default. + * Defines whether the save state is allowed to be deleted. */ - bool getBool(const Common::String &key) const; + void setDeletableFlag(bool state) { _isDeletable = state; } /** - * Sets the 'is_deletable' key, which indicates if the - * given savestate is safe for deletion. + * Queries whether the save state is allowed to be deleted. */ - void setDeletableFlag(bool state); + bool getDeletableFlag() const { return _isDeletable; } /** - * Sets the 'is_write_protected' key, which indicates if the - * given savestate can be overwritten or not + * Defines whether the save state is write protected. */ - void setWriteProtectedFlag(bool state); + void setWriteProtectedFlag(bool state) { _isWriteProtected = state; } + + /** + * Queries whether the save state is write protected. + */ + bool getWriteProtectedFlag() const { return _isWriteProtected; } /** * Return a thumbnail graphics surface representing the savestate visually. @@ -109,24 +104,100 @@ public: void setThumbnail(Graphics::Surface *t); /** - * Sets the 'save_date' key properly, based on the given values. + * Sets the date the save state was created. + * + * @param year Year of creation. + * @param month Month of creation. + * @param day Day of creation. */ void setSaveDate(int year, int month, int day); /** - * Sets the 'save_time' key properly, based on the given values. + * Queries a human readable description of the date the save state was created. + * + * This will return an empty string in case the value is not set. + */ + const Common::String &getSaveDate() const { return _saveDate; } + + /** + * Sets the time the save state was created. + * + * @param hour Hour of creation. + * @param min Minute of creation. */ void setSaveTime(int hour, int min); /** - * Sets the 'play_time' key properly, based on the given values. + * Queries a human readable description of the time the save state was created. + * + * This will return an empty string in case the value is not set. + */ + const Common::String &getSaveTime() const { return _saveTime; } + + /** + * Sets the time the game was played before the save state was created. + * + * @param hours How many hours the user played the game so far. + * @param min How many minutes the user played the game so far. */ void setPlayTime(int hours, int minutes); /** - * Sets the 'play_time' key properly, based on the given value. + * Sets the time the game was played before the save state was created. + * + * @param msecs How many milliseconds the user played the game so far. */ void setPlayTime(uint32 msecs); + + /** + * Queries a human readable description of the time the game was played + * before the save state was created. + * + * This will return an empty string in case the value is not set. + */ + const Common::String &getPlayTime() const { return _playTime; } + +private: + /** + * The saveslot id, as it would be passed to the "-x" command line switch. + */ + int _slot; + + /** + * A human readable description of the save state. + */ + Common::String _description; + + /** + * Whether the save state can be deleted. + */ + bool _isDeletable; + + /** + * Whether the save state is write protected. + */ + bool _isWriteProtected; + + /** + * Human readable description of the date the save state was created. + */ + Common::String _saveDate; + + /** + * Human readable description of the time the save state was created. + */ + Common::String _saveTime; + + /** + * Human readable description of the time the game was played till the + * save state was created. + */ + Common::String _playTime; + + /** + * The thumbnail of the save state. + */ + Common::SharedPtr _thumbnail; }; /** List of savestates. */ diff --git a/graphics/cursorman.cpp b/graphics/cursorman.cpp index 8e10b2f22b4..9395a7e64b0 100644 --- a/graphics/cursorman.cpp +++ b/graphics/cursorman.cpp @@ -24,7 +24,9 @@ #include "common/system.h" #include "common/stack.h" +namespace Common { DECLARE_SINGLETON(Graphics::CursorManager); +} namespace Graphics { diff --git a/graphics/font.cpp b/graphics/font.cpp index cdf90906252..3f7152a95e6 100644 --- a/graphics/font.cpp +++ b/graphics/font.cpp @@ -19,771 +19,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "common/stream.h" -#include "common/file.h" -#include "common/endian.h" -#include "common/array.h" -#include "common/textconsole.h" -#include "common/util.h" #include "graphics/font.h" -#include "graphics/surface.h" + +#include "common/array.h" +#include "common/util.h" namespace Graphics { -void free_font(NewFontData *pf); - -NewFont::~NewFont() { - if (_font) { - free_font(_font); - } -} - -int NewFont::getCharWidth(byte chr) const { - // If no width table is specified, return the maximum width - if (!_desc.width) - return _desc.maxwidth; - // If this character is not included in the font, use the default char. - if (chr < _desc.firstchar || _desc.firstchar + _desc.size < chr) { - chr = _desc.defaultchar; - } - return _desc.width[chr - _desc.firstchar]; -} - - -template -void drawCharIntern(byte *ptr, uint pitch, const bitmap_t *src, int h, int minX, int maxX, const PixelType color) { - const bitmap_t maxXMask = ~((1 << (16-maxX)) - 1); - while (h-- > 0) { - bitmap_t buffer = READ_UINT16(src); - src++; - - buffer &= maxXMask; - buffer <<= minX; - PixelType *tmp = (PixelType *)ptr; - while (buffer != 0) { - if ((buffer & 0x8000) != 0) - *tmp = color; - tmp++; - buffer <<= 1; - } - - ptr += pitch; - } -} - -void NewFont::drawChar(Surface *dst, byte chr, const int tx, const int ty, const uint32 color) const { - assert(dst != 0); - - assert(_desc.bits != 0 && _desc.maxwidth <= 16); - assert(dst->format.bytesPerPixel == 1 || dst->format.bytesPerPixel == 2); - - // If this character is not included in the font, use the default char. - if (chr < _desc.firstchar || chr >= _desc.firstchar + _desc.size) { - chr = _desc.defaultchar; - } - - chr -= _desc.firstchar; - - int bbw, bbh, bbx, bby; - - // Get the bounding box of the character - if (!_desc.bbx) { - bbw = _desc.fbbw; - bbh = _desc.fbbh; - bbx = _desc.fbbx; - bby = _desc.fbby; - } else { - bbw = _desc.bbx[chr].w; - bbh = _desc.bbx[chr].h; - bbx = _desc.bbx[chr].x; - bby = _desc.bbx[chr].y; - } - - byte *ptr = (byte *)dst->getBasePtr(tx + bbx, ty + _desc.ascent - bby - bbh); - - const bitmap_t *tmp = _desc.bits + (_desc.offset ? _desc.offset[chr] : (chr * _desc.fbbh)); - - int y = MIN(bbh, ty + _desc.ascent - bby); - tmp += bbh - y; - y -= MAX(0, ty + _desc.ascent - bby - dst->h); - - if (dst->format.bytesPerPixel == 1) - drawCharIntern(ptr, dst->pitch, tmp, y, MAX(0, -(tx + bbx)), MIN(bbw, dst->w - tx - bbx), color); - else if (dst->format.bytesPerPixel == 2) - drawCharIntern(ptr, dst->pitch, tmp, y, MAX(0, -(tx + bbx)), MIN(bbw, dst->w - tx - bbx), color); -} - - -#pragma mark - - -/* BEGIN font.h*/ -/* bitmap_t helper macros*/ -#define BITMAP_WORDS(x) (((x)+15)/16) /* image size in words*/ -#define BITMAP_BYTES(x) (BITMAP_WORDS(x)*sizeof(bitmap_t)) -#define BITMAP_BITSPERIMAGE (sizeof(bitmap_t) * 8) -#define BITMAP_BITVALUE(n) ((bitmap_t) (((bitmap_t) 1) << (n))) -#define BITMAP_FIRSTBIT (BITMAP_BITVALUE(BITMAP_BITSPERIMAGE - 1)) -#define BITMAP_TESTBIT(m) ((m) & BITMAP_FIRSTBIT) -#define BITMAP_SHIFTBIT(m) ((bitmap_t) ((m) << 1)) - -/* builtin C-based proportional/fixed font structure */ -/* based on The Microwindows Project http://microwindows.org */ -struct NewFontData { - char * name; /* font name*/ - int maxwidth; /* max width in pixels*/ - int height; /* height in pixels*/ - int ascent; /* ascent (baseline) height*/ - int firstchar; /* first character in bitmap*/ - int size; /* font size in glyphs*/ - bitmap_t* bits; /* 16-bit right-padded bitmap data*/ - unsigned long* offset; /* offsets into bitmap data*/ - unsigned char* width; /* character widths or NULL if fixed*/ - BBX* bbx; /* character bounding box or NULL if fixed */ - int defaultchar; /* default char (not glyph index)*/ - long bits_size; /* # words of bitmap_t bits*/ - - /* unused by runtime system, read in by convbdf*/ - char * facename; /* facename of font*/ - char * copyright; /* copyright info for loadable fonts*/ - int pixel_size; - int descent; - int fbbw, fbbh, fbbx, fbby; -}; -/* END font.h*/ - -#define isprefix(buf,str) (!strncmp(buf, str, strlen(str))) -#define strequal(s1,s2) (!strcmp(s1, s2)) - -#define EXTRA 300 - -int start_char = 0; -int limit_char = 255; - -NewFontData* bdf_read_font(Common::SeekableReadStream &fp); -int bdf_read_header(Common::SeekableReadStream &fp, NewFontData* pf); -int bdf_read_bitmaps(Common::SeekableReadStream &fp, NewFontData* pf); -char * bdf_getline(Common::SeekableReadStream &fp, char *buf, int len); -bitmap_t bdf_hexval(unsigned char *buf); - -void free_font(NewFontData* pf) { - if (!pf) - return; - free(pf->name); - free(pf->facename); - free(pf->bits); - free(pf->offset); - free(pf->width); - free(pf->bbx); - free(pf); -} - -/* build incore structure from .bdf file*/ -NewFontData* bdf_read_font(Common::SeekableReadStream &fp) { - NewFontData* pf; - uint32 pos = fp.pos(); - - pf = (NewFontData*)calloc(1, sizeof(NewFontData)); - if (!pf) - goto errout; - - if (!bdf_read_header(fp, pf)) { - warning("Error reading font header"); - goto errout; - } - - fp.seek(pos, SEEK_SET); - - if (!bdf_read_bitmaps(fp, pf)) { - warning("Error reading font bitmaps"); - goto errout; - } - - return pf; - - errout: - free_font(pf); - return NULL; -} - -/* read bdf font header information, return 0 on error*/ -int bdf_read_header(Common::SeekableReadStream &fp, NewFontData* pf) { - int encoding = 0; - int nchars = 0, maxwidth, maxheight; - int firstchar = 65535; - int lastchar = -1; - char buf[256]; - char facename[256]; - char copyright[256]; - - /* set certain values to errors for later error checking*/ - pf->defaultchar = -1; - pf->ascent = -1; - pf->descent = -1; - - for (;;) { - if (!bdf_getline(fp, buf, sizeof(buf))) { - warning("Error: EOF on file"); - return 0; - } - - /* note: the way sscanf is used here ensures that a terminating null - character is automatically added. Refer to: - http://pubs.opengroup.org/onlinepubs/009695399/functions/fscanf.html */ - - if (isprefix(buf, "FONT ")) { /* not required*/ - if (sscanf(buf, "FONT %[^\n]", facename) != 1) { - warning("Error: bad 'FONT'"); - return 0; - } - pf->facename = strdup(facename); - continue; - } - if (isprefix(buf, "COPYRIGHT ")) { /* not required*/ - if (sscanf(buf, "COPYRIGHT \"%[^\"]", copyright) != 1) { - warning("Error: bad 'COPYRIGHT'"); - return 0; - } - pf->copyright = strdup(copyright); - continue; - } - if (isprefix(buf, "DEFAULT_CHAR ")) { /* not required*/ - if (sscanf(buf, "DEFAULT_CHAR %d", &pf->defaultchar) != 1) { - warning("Error: bad 'DEFAULT_CHAR'"); - return 0; - } - } - if (isprefix(buf, "FONT_DESCENT ")) { - if (sscanf(buf, "FONT_DESCENT %d", &pf->descent) != 1) { - warning("Error: bad 'FONT_DESCENT'"); - return 0; - } - continue; - } - if (isprefix(buf, "FONT_ASCENT ")) { - if (sscanf(buf, "FONT_ASCENT %d", &pf->ascent) != 1) { - warning("Error: bad 'FONT_ASCENT'"); - return 0; - } - continue; - } - if (isprefix(buf, "FONTBOUNDINGBOX ")) { - if (sscanf(buf, "FONTBOUNDINGBOX %d %d %d %d", - &pf->fbbw, &pf->fbbh, &pf->fbbx, &pf->fbby) != 4) { - warning("Error: bad 'FONTBOUNDINGBOX'"); - return 0; - } - continue; - } - if (isprefix(buf, "CHARS ")) { - if (sscanf(buf, "CHARS %d", &nchars) != 1) { - warning("Error: bad 'CHARS'"); - return 0; - } - continue; - } - - /* - * Reading ENCODING is necessary to get firstchar/lastchar - * which is needed to pre-calculate our offset and widths - * array sizes. - */ - if (isprefix(buf, "ENCODING ")) { - if (sscanf(buf, "ENCODING %d", &encoding) != 1) { - warning("Error: bad 'ENCODING'"); - return 0; - } - if (encoding >= 0 && - encoding <= limit_char && - encoding >= start_char) { - - if (firstchar > encoding) - firstchar = encoding; - if (lastchar < encoding) - lastchar = encoding; - } - continue; - } - if (strequal(buf, "ENDFONT")) - break; - } - - /* calc font height*/ - if (pf->ascent < 0 || pf->descent < 0 || firstchar < 0) { - warning("Error: Invalid BDF file, requires FONT_ASCENT/FONT_DESCENT/ENCODING"); - return 0; - } - pf->height = pf->ascent + pf->descent; - - /* calc default char*/ - if (pf->defaultchar < 0 || - pf->defaultchar < firstchar || - pf->defaultchar > limit_char ) - pf->defaultchar = firstchar; - - /* calc font size (offset/width entries)*/ - pf->firstchar = firstchar; - pf->size = lastchar - firstchar + 1; - - /* use the font boundingbox to get initial maxwidth*/ - /*maxwidth = pf->fbbw - pf->fbbx;*/ - maxwidth = pf->fbbw; - maxheight = pf->fbbh; - - /* initially use font bounding box for bits allocation*/ - pf->bits_size = nchars * BITMAP_WORDS(maxwidth) * maxheight; - - /* allocate bits, offset, and width arrays*/ - pf->bits = (bitmap_t *)malloc(pf->bits_size * sizeof(bitmap_t) + EXTRA); - pf->offset = (unsigned long *)malloc(pf->size * sizeof(unsigned long)); - pf->width = (unsigned char *)malloc(pf->size * sizeof(unsigned char)); - pf->bbx = (BBX *)malloc(pf->size * sizeof(BBX)); - - if (!pf->bits || !pf->offset || !pf->width) { - warning("Error: no memory for font load"); - return 0; - } - - return 1; -} - -/* read bdf font bitmaps, return 0 on error*/ -int bdf_read_bitmaps(Common::SeekableReadStream &fp, NewFontData* pf) { - long ofs = 0; - int maxwidth = 0; - int i, k, encoding = 0, width = 0; - int bbw = 0, bbh = 0, bbx = 0, bby = 0; - int proportional = 0; - int need_bbx = 0; - int encodetable = 0; - long l; - char buf[256]; - - /* initially mark offsets as not used*/ - for (i = 0; i < pf->size; ++i) - pf->offset[i] = (unsigned long)-1; - - for (;;) { - if (!bdf_getline(fp, buf, sizeof(buf))) { - warning("Error: EOF on file"); - return 0; - } - if (isprefix(buf, "STARTCHAR")) { - encoding = width = bbw = bbh = bbx = bby = -1; - continue; - } - if (isprefix(buf, "ENCODING ")) { - if (sscanf(buf, "ENCODING %d", &encoding) != 1) { - warning("Error: bad 'ENCODING'"); - return 0; - } - if (encoding < start_char || encoding > limit_char) - encoding = -1; - continue; - } - if (isprefix(buf, "DWIDTH ")) { - if (sscanf(buf, "DWIDTH %d", &width) != 1) { - warning("Error: bad 'DWIDTH'"); - return 0; - } - /* use font boundingbox width if DWIDTH <= 0*/ - if (width <= 0) - width = pf->fbbw - pf->fbbx; - continue; - } - if (isprefix(buf, "BBX ")) { - if (sscanf(buf, "BBX %d %d %d %d", &bbw, &bbh, &bbx, &bby) != 4) { - warning("Error: bad 'BBX'"); - return 0; - } - continue; - } - if (strequal(buf, "BITMAP")) { - bitmap_t *ch_bitmap = pf->bits + ofs; - int ch_words; - - if (encoding < 0) - continue; - - /* set bits offset in encode map*/ - if (pf->offset[encoding-pf->firstchar] != (unsigned long)-1) { - warning("Error: duplicate encoding for character %d (0x%02x), ignoring duplicate", - encoding, encoding); - continue; - } - pf->offset[encoding-pf->firstchar] = ofs; - pf->width[encoding-pf->firstchar] = width; - - pf->bbx[encoding-pf->firstchar].w = bbw; - pf->bbx[encoding-pf->firstchar].h = bbh; - pf->bbx[encoding-pf->firstchar].x = bbx; - pf->bbx[encoding-pf->firstchar].y = bby; - - if (width > maxwidth) - maxwidth = width; - - /* clear bitmap*/ - memset(ch_bitmap, 0, BITMAP_BYTES(bbw) * bbh); - - ch_words = BITMAP_WORDS(bbw); - - /* read bitmaps*/ - for (i = 0; i < bbh; ++i) { - if (!bdf_getline(fp, buf, sizeof(buf))) { - warning("Error: EOF reading BITMAP data"); - return 0; - } - if (isprefix(buf, "ENDCHAR")) - break; - - for (k = 0; k < ch_words; ++k) { - bitmap_t value; - - value = bdf_hexval((unsigned char *)buf); - if (bbw > 8) { - WRITE_UINT16(ch_bitmap, value); - } else { - WRITE_UINT16(ch_bitmap, value << 8); - } - ch_bitmap++; - } - } - - ofs += ch_words * bbh; - continue; - } - if (strequal(buf, "ENDFONT")) - break; - } - - /* set max width*/ - pf->maxwidth = maxwidth; - - /* change unused offset/width values to default char values*/ - for (i = 0; i < pf->size; ++i) { - int defchar = pf->defaultchar - pf->firstchar; - - if (pf->offset[i] == (unsigned long)-1) { - pf->offset[i] = pf->offset[defchar]; - pf->width[i] = pf->width[defchar]; - pf->bbx[i].w = pf->bbx[defchar].w; - pf->bbx[i].h = pf->bbx[defchar].h; - pf->bbx[i].x = pf->bbx[defchar].x; - pf->bbx[i].y = pf->bbx[defchar].y; - } - } - - /* determine whether font doesn't require encode table*/ - l = 0; - for (i = 0; i < pf->size; ++i) { - if (pf->offset[i] != (unsigned long)l) { - encodetable = 1; - break; - } - l += BITMAP_WORDS(pf->bbx[i].w) * pf->bbx[i].h; - } - if (!encodetable) { - free(pf->offset); - pf->offset = NULL; - } - - /* determine whether font is fixed-width*/ - for (i = 0; i < pf->size; ++i) { - if (pf->width[i] != maxwidth) { - proportional = 1; - break; - } - } - if (!proportional) { - free(pf->width); - pf->width = NULL; - } - - /* determine if the font needs a bbx table */ - for (i = 0; i < pf->size; ++i) { - if (pf->bbx[i].w != pf->fbbw || pf->bbx[i].h != pf->fbbh || pf->bbx[i].x != pf->fbbx || pf->bbx[i].y != pf->fbby) { - need_bbx = 1; - break; - } - } - if (!need_bbx) { - free(pf->bbx); - pf->bbx = NULL; - } - - /* reallocate bits array to actual bits used*/ - if (ofs < pf->bits_size) { - bitmap_t *tmp = (bitmap_t *)realloc(pf->bits, ofs * sizeof(bitmap_t)); - if (tmp != NULL || ofs == 0) - pf->bits = tmp; - else - error("bdf_read_bitmaps: Error while reallocating memory"); - pf->bits_size = ofs; - } - else { - if (ofs > pf->bits_size) { - warning("Warning: DWIDTH spec > max FONTBOUNDINGBOX"); - if (ofs > pf->bits_size+EXTRA) { - warning("Error: Not enough bits initially allocated"); - return 0; - } - pf->bits_size = ofs; - } - } - - return 1; -} - -/* read the next non-comment line, returns buf or NULL if EOF*/ -// TODO: Can we use SeekableReadStream::readLine instead? -char *bdf_getline(Common::SeekableReadStream &fp, char *buf, int len) { - int c; - char *b; - - for (;;) { - b = buf; - while (!fp.eos()) { - c = fp.readByte(); - if (c == '\r') - continue; - if (c == '\n') - break; - if (b - buf >= (len - 1)) - break; - *b++ = c; - } - *b = '\0'; - if (fp.eos() && b == buf) - return NULL; - if (b != buf && !isprefix(buf, "COMMENT")) - break; - } - return buf; -} - -/* return hex value of buffer */ -bitmap_t bdf_hexval(unsigned char *buf) { - bitmap_t val = 0; - - for (unsigned char *ptr = buf; *ptr; ptr++) { - int c = *ptr; - - if (c >= '0' && c <= '9') - c -= '0'; - else if (c >= 'A' && c <= 'F') - c = c - 'A' + 10; - else if (c >= 'a' && c <= 'f') - c = c - 'a' + 10; - else - c = 0; - val = (val << 4) | c; - } - return val; -} - -NewFont *NewFont::loadFont(Common::SeekableReadStream &stream) { - NewFontData *data = bdf_read_font(stream); - if (!data || stream.err()) { - free_font(data); - return 0; - } - - FontDesc desc; - desc.name = data->name; - desc.maxwidth = data->maxwidth; - desc.height = data->height; - desc.fbbw = data->fbbw; - desc.fbbh = data->fbbh; - desc.fbbx = data->fbbx; - desc.fbby = data->fbby; - desc.ascent = data->ascent; - desc.firstchar = data->firstchar; - desc.size = data->size; - desc.bits = data->bits; - desc.offset = data->offset; - desc.width = data->width; - desc.bbx = data->bbx; - desc.defaultchar = data->defaultchar; - desc.bits_size = data->bits_size; - - return new NewFont(desc, data); -} - -bool NewFont::cacheFontData(const NewFont &font, const Common::String &filename) { - Common::DumpFile cacheFile; - if (!cacheFile.open(filename)) { - warning("Couldn't open file '%s' for writing", filename.c_str()); - return false; - } - - cacheFile.writeUint16BE(font._desc.maxwidth); - cacheFile.writeUint16BE(font._desc.height); - cacheFile.writeUint16BE(font._desc.fbbw); - cacheFile.writeUint16BE(font._desc.fbbh); - cacheFile.writeSint16BE(font._desc.fbbx); - cacheFile.writeSint16BE(font._desc.fbby); - cacheFile.writeUint16BE(font._desc.ascent); - cacheFile.writeUint16BE(font._desc.firstchar); - cacheFile.writeUint16BE(font._desc.size); - cacheFile.writeUint16BE(font._desc.defaultchar); - cacheFile.writeUint32BE(font._desc.bits_size); - - for (long i = 0; i < font._desc.bits_size; ++i) { - cacheFile.writeUint16BE(font._desc.bits[i]); - } - - if (font._desc.offset) { - cacheFile.writeByte(1); - for (int i = 0; i < font._desc.size; ++i) { - cacheFile.writeUint32BE(font._desc.offset[i]); - } - } else { - cacheFile.writeByte(0); - } - - if (font._desc.width) { - cacheFile.writeByte(1); - for (int i = 0; i < font._desc.size; ++i) { - cacheFile.writeByte(font._desc.width[i]); - } - } else { - cacheFile.writeByte(0); - } - - if (font._desc.bbx) { - cacheFile.writeByte(1); - for (int i = 0; i < font._desc.size; ++i) { - cacheFile.writeByte(font._desc.bbx[i].w); - cacheFile.writeByte(font._desc.bbx[i].h); - cacheFile.writeByte(font._desc.bbx[i].x); - cacheFile.writeByte(font._desc.bbx[i].y); - } - } else { - cacheFile.writeByte(0); - } - - return !cacheFile.err(); -} - -NewFont *NewFont::loadFromCache(Common::SeekableReadStream &stream) { - NewFont *font = 0; - - NewFontData *data = (NewFontData *)malloc(sizeof(NewFontData)); - if (!data) - return 0; - - memset(data, 0, sizeof(NewFontData)); - - data->maxwidth = stream.readUint16BE(); - data->height = stream.readUint16BE(); - data->fbbw = stream.readUint16BE(); - data->fbbh = stream.readUint16BE(); - data->fbbx = stream.readSint16BE(); - data->fbby = stream.readSint16BE(); - data->ascent = stream.readUint16BE(); - data->firstchar = stream.readUint16BE(); - data->size = stream.readUint16BE(); - data->defaultchar = stream.readUint16BE(); - data->bits_size = stream.readUint32BE(); - - data->bits = (bitmap_t *)malloc(sizeof(bitmap_t) * data->bits_size); - if (!data->bits) { - free(data); - return 0; - } - - for (long i = 0; i < data->bits_size; ++i) { - data->bits[i] = stream.readUint16BE(); - } - - bool hasOffsetTable = (stream.readByte() != 0); - if (hasOffsetTable) { - data->offset = (unsigned long *)malloc(sizeof(unsigned long) * data->size); - if (!data->offset) { - free(data->bits); - free(data); - return 0; - } - - for (int i = 0; i < data->size; ++i) { - data->offset[i] = stream.readUint32BE(); - } - } - - bool hasWidthTable = (stream.readByte() != 0); - if (hasWidthTable) { - data->width = (unsigned char *)malloc(sizeof(unsigned char) * data->size); - if (!data->width) { - free(data->bits); - free(data->offset); - free(data); - return 0; - } - - for (int i = 0; i < data->size; ++i) { - data->width[i] = stream.readByte(); - } - } - - bool hasBBXTable = (stream.readByte() != 0); - if (hasBBXTable) { - data->bbx = (BBX *)malloc(sizeof(BBX) * data->size); - if (!data->bbx) { - free(data->bits); - free(data->offset); - free(data->width); - free(data); - return 0; - } - - for (int i = 0; i < data->size; ++i) { - data->bbx[i].w = (int8)stream.readByte(); - data->bbx[i].h = (int8)stream.readByte(); - data->bbx[i].x = (int8)stream.readByte(); - data->bbx[i].y = (int8)stream.readByte(); - } - } - - 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; - desc.height = data->height; - desc.fbbw = data->fbbw; - desc.fbbh = data->fbbh; - desc.fbbx = data->fbbx; - desc.fbby = data->fbby; - desc.ascent = data->ascent; - desc.firstchar = data->firstchar; - desc.size = data->size; - desc.bits = data->bits; - desc.offset = data->offset; - desc.width = data->width; - desc.bbx = data->bbx; - desc.defaultchar = data->defaultchar; - desc.bits_size = data->bits_size; - - font = new NewFont(desc, data); - if (!font) { - free(data->bits); - free(data->offset); - free(data->width); - free(data); - return 0; - } - - return font; -} - -#pragma mark - - - int Font::getStringWidth(const Common::String &str) const { int space = 0; @@ -944,7 +186,7 @@ int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array 0) { wrapper.add(line, lineWidth); // Trim left side - while (tmpStr.size() && isspace(tmpStr[0])) { + while (tmpStr.size() && isspace(static_cast(tmpStr[0]))) { tmpWidth -= getCharWidth(tmpStr[0]); tmpStr.deleteChar(0); } diff --git a/graphics/font.h b/graphics/font.h index dc75f86e1f3..9c0b4affc10 100644 --- a/graphics/font.h +++ b/graphics/font.h @@ -25,7 +25,6 @@ #include "common/str.h" namespace Common { -class SeekableReadStream; template class Array; } @@ -114,68 +113,6 @@ public: int wordWrapText(const Common::String &str, int maxWidth, Common::Array &lines) const; }; -typedef uint16 bitmap_t; /* bitmap image unit size*/ - -struct BBX { - int8 w; - int8 h; - int8 x; - int8 y; -}; - -/* builtin C-based proportional/fixed font structure */ -/* based on The Microwindows Project http://microwindows.org */ -struct FontDesc { - const char * name; /* font name*/ - int maxwidth; /* max width in pixels*/ - int height; /* height in pixels*/ - int fbbw, fbbh, fbbx, fbby; /* max bounding box */ - int ascent; /* ascent (baseline) height*/ - int firstchar; /* first character in bitmap*/ - int size; /* font size in glyphs*/ - const bitmap_t* bits; /* 16-bit right-padded bitmap data*/ - const unsigned long* offset; /* offsets into bitmap data*/ - const unsigned char* width; /* character widths or NULL if fixed*/ - const BBX* bbx; /* character bounding box or NULL if fixed */ - int defaultchar; /* default char (not glyph index)*/ - long bits_size; /* # words of bitmap_t bits*/ -}; - -struct NewFontData; - -class NewFont : public Font { -protected: - FontDesc _desc; - NewFontData *_font; - -public: - NewFont(const FontDesc &desc, NewFontData *font = 0) : _desc(desc), _font(font) {} - ~NewFont(); - - virtual int getFontHeight() const { return _desc.height; } - virtual int getMaxCharWidth() const { return _desc.maxwidth; } - - virtual int getCharWidth(byte chr) const; - virtual void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const; - - static NewFont *loadFont(Common::SeekableReadStream &stream); - static bool cacheFontData(const NewFont &font, const Common::String &filename); - static NewFont *loadFromCache(Common::SeekableReadStream &stream); -}; - -#define DEFINE_FONT(n) \ - const NewFont *n = 0; \ - void create_##n() { \ - n = new NewFont(desc); \ - } - -#define FORWARD_DECLARE_FONT(n) \ - extern const NewFont *n; \ - extern void create_##n() - -#define INIT_FONT(n) \ - create_##n() - } // End of namespace Graphics #endif diff --git a/graphics/fontman.cpp b/graphics/fontman.cpp index f40cf976029..e59e5a33c5c 100644 --- a/graphics/fontman.cpp +++ b/graphics/fontman.cpp @@ -19,11 +19,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "graphics/font.h" #include "graphics/fontman.h" +#include "graphics/font.h" +#include "graphics/fonts/bdf.h" + #include "common/translation.h" +namespace Common { DECLARE_SINGLETON(Graphics::FontManager); +} namespace Graphics { diff --git a/graphics/fonts/bdf.cpp b/graphics/fonts/bdf.cpp new file mode 100644 index 00000000000..b7d0f7749ee --- /dev/null +++ b/graphics/fonts/bdf.cpp @@ -0,0 +1,796 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "graphics/fonts/bdf.h" + +#include "common/file.h" +#include "common/endian.h" +#include "common/textconsole.h" + +#include "graphics/surface.h" + +namespace Graphics { + +void free_font(BdfFontData *pf); + +BdfFont::~BdfFont() { + if (_font) { + free_font(_font); + } +} + +int BdfFont::getFontHeight() const { + return _desc.height; +} + +int BdfFont::getMaxCharWidth() const { + return _desc.maxwidth; +} + +int BdfFont::getCharWidth(byte chr) const { + // If no width table is specified, return the maximum width + if (!_desc.width) + return _desc.maxwidth; + // If this character is not included in the font, use the default char. + if (chr < _desc.firstchar || _desc.firstchar + _desc.size < chr) { + chr = _desc.defaultchar; + } + return _desc.width[chr - _desc.firstchar]; +} + + +template +void drawCharIntern(byte *ptr, uint pitch, const bitmap_t *src, int h, int minX, int maxX, const PixelType color) { + const bitmap_t maxXMask = ~((1 << (16 - maxX)) - 1); + while (h-- > 0) { + bitmap_t buffer = READ_UINT16(src); + src++; + + buffer &= maxXMask; + buffer <<= minX; + PixelType *tmp = (PixelType *)ptr; + while (buffer != 0) { + if ((buffer & 0x8000) != 0) + *tmp = color; + tmp++; + buffer <<= 1; + } + + ptr += pitch; + } +} + +void BdfFont::drawChar(Surface *dst, byte chr, const int tx, const int ty, const uint32 color) const { + assert(dst != 0); + + assert(_desc.bits != 0 && _desc.maxwidth <= 16); + assert(dst->format.bytesPerPixel == 1 || dst->format.bytesPerPixel == 2); + + // If this character is not included in the font, use the default char. + if (chr < _desc.firstchar || chr >= _desc.firstchar + _desc.size) { + chr = _desc.defaultchar; + } + + chr -= _desc.firstchar; + + int bbw, bbh, bbx, bby; + + // Get the bounding box of the character + if (!_desc.bbx) { + bbw = _desc.fbbw; + bbh = _desc.fbbh; + bbx = _desc.fbbx; + bby = _desc.fbby; + } else { + bbw = _desc.bbx[chr].w; + bbh = _desc.bbx[chr].h; + bbx = _desc.bbx[chr].x; + bby = _desc.bbx[chr].y; + } + + byte *ptr = (byte *)dst->getBasePtr(tx + bbx, ty + _desc.ascent - bby - bbh); + + const bitmap_t *tmp = _desc.bits + (_desc.offset ? _desc.offset[chr] : (chr * _desc.fbbh)); + + int y = MIN(bbh, ty + _desc.ascent - bby); + tmp += bbh - y; + y -= MAX(0, ty + _desc.ascent - bby - dst->h); + + if (dst->format.bytesPerPixel == 1) + drawCharIntern(ptr, dst->pitch, tmp, y, MAX(0, -(tx + bbx)), MIN(bbw, dst->w - tx - bbx), color); + else if (dst->format.bytesPerPixel == 2) + drawCharIntern(ptr, dst->pitch, tmp, y, MAX(0, -(tx + bbx)), MIN(bbw, dst->w - tx - bbx), color); +} + + +#pragma mark - + +/* BEGIN font.h*/ +/* bitmap_t helper macros*/ +#define BITMAP_WORDS(x) (((x)+15)/16) /* image size in words*/ +#define BITMAP_BYTES(x) (BITMAP_WORDS(x)*sizeof(bitmap_t)) +#define BITMAP_BITSPERIMAGE (sizeof(bitmap_t) * 8) +#define BITMAP_BITVALUE(n) ((bitmap_t) (((bitmap_t) 1) << (n))) +#define BITMAP_FIRSTBIT (BITMAP_BITVALUE(BITMAP_BITSPERIMAGE - 1)) +#define BITMAP_TESTBIT(m) ((m) & BITMAP_FIRSTBIT) +#define BITMAP_SHIFTBIT(m) ((bitmap_t) ((m) << 1)) + +/* builtin C-based proportional/fixed font structure */ +/* based on The Microwindows Project http://microwindows.org */ +struct BdfFontData { + char *name; /* font name */ + int maxwidth; /* max width in pixels */ + int height; /* height in pixels */ + int ascent; /* ascent (baseline) height */ + int firstchar; /* first character in bitmap */ + int size; /* font size in glyphs */ + bitmap_t *bits; /* 16-bit right-padded bitmap data */ + unsigned long *offset; /* offsets into bitmap data */ + unsigned char *width; /* character widths or NULL if fixed */ + BBX *bbx; /* character bounding box or NULL if fixed */ + int defaultchar; /* default char (not glyph index) */ + long bits_size; /* # words of bitmap_t bits */ + + /* unused by runtime system, read in by convbdf */ + char *facename; /* facename of font */ + char *copyright; /* copyright info for loadable fonts */ + int pixel_size; + int descent; + int fbbw, fbbh, fbbx, fbby; +}; +/* END font.h */ + +#define isprefix(buf,str) (!strncmp(buf, str, strlen(str))) +#define strequal(s1,s2) (!strcmp(s1, s2)) + +#define EXTRA 300 + +int start_char = 0; +int limit_char = 255; + +BdfFontData *bdf_read_font(Common::SeekableReadStream &fp); +int bdf_read_header(Common::SeekableReadStream &fp, BdfFontData *pf); +int bdf_read_bitmaps(Common::SeekableReadStream &fp, BdfFontData *pf); +char *bdf_getline(Common::SeekableReadStream &fp, char *buf, int len); +bitmap_t bdf_hexval(unsigned char *buf); + +void free_font(BdfFontData *pf) { + if (!pf) + return; + free(pf->name); + free(pf->facename); + free(pf->copyright); + free(pf->bits); + free(pf->offset); + free(pf->width); + free(pf->bbx); + free(pf); +} + +/* build incore structure from .bdf file*/ +BdfFontData *bdf_read_font(Common::SeekableReadStream &fp) { + BdfFontData *pf; + uint32 pos = fp.pos(); + + pf = (BdfFontData *)calloc(1, sizeof(BdfFontData)); + if (!pf) + goto errout; + + if (!bdf_read_header(fp, pf)) { + warning("Error reading font header"); + goto errout; + } + + fp.seek(pos, SEEK_SET); + + if (!bdf_read_bitmaps(fp, pf)) { + warning("Error reading font bitmaps"); + goto errout; + } + + return pf; + +errout: + free_font(pf); + return NULL; +} + +/* read bdf font header information, return 0 on error*/ +int bdf_read_header(Common::SeekableReadStream &fp, BdfFontData *pf) { + int encoding = 0; + int nchars = 0, maxwidth, maxheight; + int firstchar = 65535; + int lastchar = -1; + char buf[256]; + char facename[256]; + char copyright[256]; + memset(facename, 0, sizeof(facename)); + memset(copyright, 0, sizeof(copyright)); + + /* set certain values to errors for later error checking*/ + pf->defaultchar = -1; + pf->ascent = -1; + pf->descent = -1; + + for (;;) { + if (!bdf_getline(fp, buf, sizeof(buf))) { + warning("Error: EOF on file"); + return 0; + } + + /* note: the way sscanf is used here ensures that a terminating null + character is automatically added. Refer to: + http://pubs.opengroup.org/onlinepubs/009695399/functions/fscanf.html */ + + if (isprefix(buf, "FONT ")) { /* not required*/ + if (sscanf(buf, "FONT %[^\n]", facename) != 1) { + warning("Error: bad 'FONT'"); + return 0; + } + + pf->facename = strdup(facename); + continue; + } + if (isprefix(buf, "COPYRIGHT ")) { /* not required*/ + if (sscanf(buf, "COPYRIGHT \"%[^\"]", copyright) != 1) { + warning("Error: bad 'COPYRIGHT'"); + return 0; + } + + pf->copyright = strdup(copyright); + continue; + } + if (isprefix(buf, "DEFAULT_CHAR ")) { /* not required*/ + if (sscanf(buf, "DEFAULT_CHAR %d", &pf->defaultchar) != 1) { + warning("Error: bad 'DEFAULT_CHAR'"); + return 0; + } + } + if (isprefix(buf, "FONT_DESCENT ")) { + if (sscanf(buf, "FONT_DESCENT %d", &pf->descent) != 1) { + warning("Error: bad 'FONT_DESCENT'"); + return 0; + } + continue; + } + if (isprefix(buf, "FONT_ASCENT ")) { + if (sscanf(buf, "FONT_ASCENT %d", &pf->ascent) != 1) { + warning("Error: bad 'FONT_ASCENT'"); + return 0; + } + continue; + } + if (isprefix(buf, "FONTBOUNDINGBOX ")) { + if (sscanf(buf, "FONTBOUNDINGBOX %d %d %d %d", + &pf->fbbw, &pf->fbbh, &pf->fbbx, &pf->fbby) != 4) { + warning("Error: bad 'FONTBOUNDINGBOX'"); + return 0; + } + continue; + } + if (isprefix(buf, "CHARS ")) { + if (sscanf(buf, "CHARS %d", &nchars) != 1) { + warning("Error: bad 'CHARS'"); + return 0; + } + continue; + } + + /* + * Reading ENCODING is necessary to get firstchar/lastchar + * which is needed to pre-calculate our offset and widths + * array sizes. + */ + if (isprefix(buf, "ENCODING ")) { + if (sscanf(buf, "ENCODING %d", &encoding) != 1) { + warning("Error: bad 'ENCODING'"); + return 0; + } + if (encoding >= 0 && + encoding <= limit_char && + encoding >= start_char) { + + if (firstchar > encoding) + firstchar = encoding; + if (lastchar < encoding) + lastchar = encoding; + } + continue; + } + if (strequal(buf, "ENDFONT")) + break; + } + + /* calc font height*/ + if (pf->ascent < 0 || pf->descent < 0 || firstchar < 0) { + warning("Error: Invalid BDF file, requires FONT_ASCENT/FONT_DESCENT/ENCODING"); + return 0; + } + pf->height = pf->ascent + pf->descent; + + /* calc default char*/ + if (pf->defaultchar < 0 || + pf->defaultchar < firstchar || + pf->defaultchar > limit_char) + pf->defaultchar = firstchar; + + /* calc font size (offset/width entries)*/ + pf->firstchar = firstchar; + pf->size = lastchar - firstchar + 1; + + /* use the font boundingbox to get initial maxwidth*/ + /*maxwidth = pf->fbbw - pf->fbbx;*/ + maxwidth = pf->fbbw; + maxheight = pf->fbbh; + + /* initially use font bounding box for bits allocation*/ + pf->bits_size = nchars * BITMAP_WORDS(maxwidth) * maxheight; + + /* allocate bits, offset, and width arrays*/ + pf->bits = (bitmap_t *)malloc(pf->bits_size * sizeof(bitmap_t) + EXTRA); + pf->offset = (unsigned long *)malloc(pf->size * sizeof(unsigned long)); + pf->width = (unsigned char *)malloc(pf->size * sizeof(unsigned char)); + pf->bbx = (BBX *)malloc(pf->size * sizeof(BBX)); + + if (!pf->bits || !pf->offset || !pf->width) { + warning("Error: no memory for font load"); + return 0; + } + + return 1; +} + +/* read bdf font bitmaps, return 0 on error*/ +int bdf_read_bitmaps(Common::SeekableReadStream &fp, BdfFontData *pf) { + long ofs = 0; + int maxwidth = 0; + int i, k, encoding = 0, width = 0; + int bbw = 0, bbh = 0, bbx = 0, bby = 0; + int proportional = 0; + int need_bbx = 0; + int encodetable = 0; + long l; + char buf[256]; + + /* initially mark offsets as not used*/ + for (i = 0; i < pf->size; ++i) + pf->offset[i] = (unsigned long)-1; + + for (;;) { + if (!bdf_getline(fp, buf, sizeof(buf))) { + warning("Error: EOF on file"); + return 0; + } + if (isprefix(buf, "STARTCHAR")) { + encoding = width = bbw = bbh = bbx = bby = -1; + continue; + } + if (isprefix(buf, "ENCODING ")) { + if (sscanf(buf, "ENCODING %d", &encoding) != 1) { + warning("Error: bad 'ENCODING'"); + return 0; + } + if (encoding < start_char || encoding > limit_char) + encoding = -1; + continue; + } + if (isprefix(buf, "DWIDTH ")) { + if (sscanf(buf, "DWIDTH %d", &width) != 1) { + warning("Error: bad 'DWIDTH'"); + return 0; + } + /* use font boundingbox width if DWIDTH <= 0*/ + if (width <= 0) + width = pf->fbbw - pf->fbbx; + continue; + } + if (isprefix(buf, "BBX ")) { + if (sscanf(buf, "BBX %d %d %d %d", &bbw, &bbh, &bbx, &bby) != 4) { + warning("Error: bad 'BBX'"); + return 0; + } + continue; + } + if (strequal(buf, "BITMAP")) { + bitmap_t *ch_bitmap = pf->bits + ofs; + int ch_words; + + if (encoding < 0) + continue; + + /* set bits offset in encode map*/ + if (pf->offset[encoding - pf->firstchar] != (unsigned long)-1) { + warning("Error: duplicate encoding for character %d (0x%02x), ignoring duplicate", + encoding, encoding); + continue; + } + pf->offset[encoding - pf->firstchar] = ofs; + pf->width[encoding - pf->firstchar] = width; + + pf->bbx[encoding - pf->firstchar].w = bbw; + pf->bbx[encoding - pf->firstchar].h = bbh; + pf->bbx[encoding - pf->firstchar].x = bbx; + pf->bbx[encoding - pf->firstchar].y = bby; + + if (width > maxwidth) + maxwidth = width; + + /* clear bitmap*/ + memset(ch_bitmap, 0, BITMAP_BYTES(bbw) * bbh); + + ch_words = BITMAP_WORDS(bbw); + + /* read bitmaps*/ + for (i = 0; i < bbh; ++i) { + if (!bdf_getline(fp, buf, sizeof(buf))) { + warning("Error: EOF reading BITMAP data"); + return 0; + } + if (isprefix(buf, "ENDCHAR")) + break; + + for (k = 0; k < ch_words; ++k) { + bitmap_t value; + + value = bdf_hexval((unsigned char *)buf); + if (bbw > 8) { + WRITE_UINT16(ch_bitmap, value); + } else { + WRITE_UINT16(ch_bitmap, value << 8); + } + ch_bitmap++; + } + } + + ofs += ch_words * bbh; + continue; + } + if (strequal(buf, "ENDFONT")) + break; + } + + /* set max width*/ + pf->maxwidth = maxwidth; + + /* change unused offset/width values to default char values*/ + for (i = 0; i < pf->size; ++i) { + int defchar = pf->defaultchar - pf->firstchar; + + if (pf->offset[i] == (unsigned long)-1) { + pf->offset[i] = pf->offset[defchar]; + pf->width[i] = pf->width[defchar]; + pf->bbx[i].w = pf->bbx[defchar].w; + pf->bbx[i].h = pf->bbx[defchar].h; + pf->bbx[i].x = pf->bbx[defchar].x; + pf->bbx[i].y = pf->bbx[defchar].y; + } + } + + /* determine whether font doesn't require encode table*/ + l = 0; + for (i = 0; i < pf->size; ++i) { + if (pf->offset[i] != (unsigned long)l) { + encodetable = 1; + break; + } + l += BITMAP_WORDS(pf->bbx[i].w) * pf->bbx[i].h; + } + if (!encodetable) { + free(pf->offset); + pf->offset = NULL; + } + + /* determine whether font is fixed-width*/ + for (i = 0; i < pf->size; ++i) { + if (pf->width[i] != maxwidth) { + proportional = 1; + break; + } + } + if (!proportional) { + free(pf->width); + pf->width = NULL; + } + + /* determine if the font needs a bbx table */ + for (i = 0; i < pf->size; ++i) { + if (pf->bbx[i].w != pf->fbbw || pf->bbx[i].h != pf->fbbh || pf->bbx[i].x != pf->fbbx || pf->bbx[i].y != pf->fbby) { + need_bbx = 1; + break; + } + } + if (!need_bbx) { + free(pf->bbx); + pf->bbx = NULL; + } + + /* reallocate bits array to actual bits used*/ + if (ofs < pf->bits_size) { + bitmap_t *tmp = (bitmap_t *)realloc(pf->bits, ofs * sizeof(bitmap_t)); + if (tmp != NULL || ofs == 0) + pf->bits = tmp; + else + error("bdf_read_bitmaps: Error while reallocating memory"); + pf->bits_size = ofs; + } else { + if (ofs > pf->bits_size) { + warning("Warning: DWIDTH spec > max FONTBOUNDINGBOX"); + if (ofs > pf->bits_size + EXTRA) { + warning("Error: Not enough bits initially allocated"); + return 0; + } + pf->bits_size = ofs; + } + } + + return 1; +} + +/* read the next non-comment line, returns buf or NULL if EOF*/ +// TODO: Can we use SeekableReadStream::readLine instead? +char *bdf_getline(Common::SeekableReadStream &fp, char *buf, int len) { + int c; + char *b; + + for (;;) { + b = buf; + while (!fp.eos()) { + c = fp.readByte(); + if (c == '\r') + continue; + if (c == '\n') + break; + if (b - buf >= (len - 1)) + break; + *b++ = c; + } + *b = '\0'; + if (fp.eos() && b == buf) + return NULL; + if (b != buf && !isprefix(buf, "COMMENT")) + break; + } + return buf; +} + +/* return hex value of buffer */ +bitmap_t bdf_hexval(unsigned char *buf) { + bitmap_t val = 0; + + for (unsigned char *ptr = buf; *ptr; ptr++) { + int c = *ptr; + + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'F') + c = c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + c = c - 'a' + 10; + else + c = 0; + val = (val << 4) | c; + } + return val; +} + +BdfFont *BdfFont::loadFont(Common::SeekableReadStream &stream) { + BdfFontData *data = bdf_read_font(stream); + if (!data || stream.err()) { + free_font(data); + return 0; + } + + BdfFontDesc desc; + desc.name = data->name; + desc.maxwidth = data->maxwidth; + desc.height = data->height; + desc.fbbw = data->fbbw; + desc.fbbh = data->fbbh; + desc.fbbx = data->fbbx; + desc.fbby = data->fbby; + desc.ascent = data->ascent; + desc.firstchar = data->firstchar; + desc.size = data->size; + desc.bits = data->bits; + desc.offset = data->offset; + desc.width = data->width; + desc.bbx = data->bbx; + desc.defaultchar = data->defaultchar; + desc.bits_size = data->bits_size; + + return new BdfFont(desc, data); +} + +bool BdfFont::cacheFontData(const BdfFont &font, const Common::String &filename) { + Common::DumpFile cacheFile; + if (!cacheFile.open(filename)) { + warning("Couldn't open file '%s' for writing", filename.c_str()); + return false; + } + + cacheFile.writeUint16BE(font._desc.maxwidth); + cacheFile.writeUint16BE(font._desc.height); + cacheFile.writeUint16BE(font._desc.fbbw); + cacheFile.writeUint16BE(font._desc.fbbh); + cacheFile.writeSint16BE(font._desc.fbbx); + cacheFile.writeSint16BE(font._desc.fbby); + cacheFile.writeUint16BE(font._desc.ascent); + cacheFile.writeUint16BE(font._desc.firstchar); + cacheFile.writeUint16BE(font._desc.size); + cacheFile.writeUint16BE(font._desc.defaultchar); + cacheFile.writeUint32BE(font._desc.bits_size); + + for (long i = 0; i < font._desc.bits_size; ++i) { + cacheFile.writeUint16BE(font._desc.bits[i]); + } + + if (font._desc.offset) { + cacheFile.writeByte(1); + for (int i = 0; i < font._desc.size; ++i) { + cacheFile.writeUint32BE(font._desc.offset[i]); + } + } else { + cacheFile.writeByte(0); + } + + if (font._desc.width) { + cacheFile.writeByte(1); + for (int i = 0; i < font._desc.size; ++i) { + cacheFile.writeByte(font._desc.width[i]); + } + } else { + cacheFile.writeByte(0); + } + + if (font._desc.bbx) { + cacheFile.writeByte(1); + for (int i = 0; i < font._desc.size; ++i) { + cacheFile.writeByte(font._desc.bbx[i].w); + cacheFile.writeByte(font._desc.bbx[i].h); + cacheFile.writeByte(font._desc.bbx[i].x); + cacheFile.writeByte(font._desc.bbx[i].y); + } + } else { + cacheFile.writeByte(0); + } + + return !cacheFile.err(); +} + +BdfFont *BdfFont::loadFromCache(Common::SeekableReadStream &stream) { + BdfFont *font = 0; + + BdfFontData *data = (BdfFontData *)malloc(sizeof(BdfFontData)); + if (!data) + return 0; + + memset(data, 0, sizeof(BdfFontData)); + + data->maxwidth = stream.readUint16BE(); + data->height = stream.readUint16BE(); + data->fbbw = stream.readUint16BE(); + data->fbbh = stream.readUint16BE(); + data->fbbx = stream.readSint16BE(); + data->fbby = stream.readSint16BE(); + data->ascent = stream.readUint16BE(); + data->firstchar = stream.readUint16BE(); + data->size = stream.readUint16BE(); + data->defaultchar = stream.readUint16BE(); + data->bits_size = stream.readUint32BE(); + + data->bits = (bitmap_t *)malloc(sizeof(bitmap_t) * data->bits_size); + if (!data->bits) { + free(data); + return 0; + } + + for (long i = 0; i < data->bits_size; ++i) { + data->bits[i] = stream.readUint16BE(); + } + + bool hasOffsetTable = (stream.readByte() != 0); + if (hasOffsetTable) { + data->offset = (unsigned long *)malloc(sizeof(unsigned long) * data->size); + if (!data->offset) { + free(data->bits); + free(data); + return 0; + } + + for (int i = 0; i < data->size; ++i) { + data->offset[i] = stream.readUint32BE(); + } + } + + bool hasWidthTable = (stream.readByte() != 0); + if (hasWidthTable) { + data->width = (unsigned char *)malloc(sizeof(unsigned char) * data->size); + if (!data->width) { + free(data->bits); + free(data->offset); + free(data); + return 0; + } + + for (int i = 0; i < data->size; ++i) { + data->width[i] = stream.readByte(); + } + } + + bool hasBBXTable = (stream.readByte() != 0); + if (hasBBXTable) { + data->bbx = (BBX *)malloc(sizeof(BBX) * data->size); + if (!data->bbx) { + free(data->bits); + free(data->offset); + free(data->width); + free(data); + return 0; + } + + for (int i = 0; i < data->size; ++i) { + data->bbx[i].w = (int8)stream.readByte(); + data->bbx[i].h = (int8)stream.readByte(); + data->bbx[i].x = (int8)stream.readByte(); + data->bbx[i].y = (int8)stream.readByte(); + } + } + + if (stream.err() || stream.eos()) { + free(data->bits); + free(data->offset); + free(data->width); + free(data); + return 0; + } + + BdfFontDesc desc; + desc.name = data->name; + desc.maxwidth = data->maxwidth; + desc.height = data->height; + desc.fbbw = data->fbbw; + desc.fbbh = data->fbbh; + desc.fbbx = data->fbbx; + desc.fbby = data->fbby; + desc.ascent = data->ascent; + desc.firstchar = data->firstchar; + desc.size = data->size; + desc.bits = data->bits; + desc.offset = data->offset; + desc.width = data->width; + desc.bbx = data->bbx; + desc.defaultchar = data->defaultchar; + desc.bits_size = data->bits_size; + + font = new BdfFont(desc, data); + if (!font) { + free(data->bits); + free(data->offset); + free(data->width); + free(data); + return 0; + } + + return font; +} + +} // End of namespace Graphics + diff --git a/graphics/fonts/bdf.h b/graphics/fonts/bdf.h new file mode 100644 index 00000000000..31c009eb271 --- /dev/null +++ b/graphics/fonts/bdf.h @@ -0,0 +1,100 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GRAPHICS_FONTS_BDF_H +#define GRAPHICS_FONTS_BDF_H + +#include "common/system.h" + +#include "graphics/font.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Graphics { + +typedef uint16 bitmap_t; /* bitmap image unit size*/ + +struct BBX { + int8 w; + int8 h; + int8 x; + int8 y; +}; + +/* builtin C-based proportional/fixed font structure */ +/* based on The Microwindows Project http://microwindows.org */ +struct BdfFontDesc { + const char *name; /* font name */ + int maxwidth; /* max width in pixels */ + int height; /* height in pixels */ + int fbbw, fbbh, fbbx, fbby; /* max bounding box */ + int ascent; /* ascent (baseline) height */ + int firstchar; /* first character in bitmap */ + int size; /* font size in glyphs */ + const bitmap_t *bits; /* 16-bit right-padded bitmap data */ + const unsigned long *offset; /* offsets into bitmap data */ + const unsigned char *width; /* character widths or NULL if fixed */ + const BBX *bbx; /* character bounding box or NULL if fixed */ + int defaultchar; /* default char (not glyph index) */ + long bits_size; /* # words of bitmap_t bits */ +}; + +struct BdfFontData; + +class BdfFont : public Font { +protected: + BdfFontDesc _desc; + BdfFontData *_font; + +public: + BdfFont(const BdfFontDesc &desc, BdfFontData *font = 0) : _desc(desc), _font(font) {} + ~BdfFont(); + + virtual int getFontHeight() const; + virtual int getMaxCharWidth() const; + + virtual int getCharWidth(byte chr) const; + virtual void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const; + + static BdfFont *loadFont(Common::SeekableReadStream &stream); + static bool cacheFontData(const BdfFont &font, const Common::String &filename); + static BdfFont *loadFromCache(Common::SeekableReadStream &stream); +}; + +#define DEFINE_FONT(n) \ + const BdfFont *n = 0; \ + void create_##n() { \ + n = new BdfFont(desc); \ + } + +#define FORWARD_DECLARE_FONT(n) \ + extern const BdfFont *n; \ + extern void create_##n() + +#define INIT_FONT(n) \ + create_##n() + +} // End of namespace Graphics + +#endif + diff --git a/graphics/fonts/consolefont.cpp b/graphics/fonts/consolefont.cpp index 65ccd3ec704..5b4768327aa 100644 --- a/graphics/fonts/consolefont.cpp +++ b/graphics/fonts/consolefont.cpp @@ -1,5 +1,5 @@ /* Generated by convbdf on Sat Jun 17 01:37:46 2006. */ -#include "graphics/font.h" +#include "graphics/fonts/bdf.h" /* Font information: name: 5x8-L1 @@ -5635,7 +5635,7 @@ static const unsigned long _sysfont_offset[] = { }; /* Exported structure definition. */ -static const FontDesc desc = { +static const BdfFontDesc desc = { "5x8-L1", 5, 8, diff --git a/graphics/fonts/newfont.cpp b/graphics/fonts/newfont.cpp index 0327c0997a9..f02baba2de7 100644 --- a/graphics/fonts/newfont.cpp +++ b/graphics/fonts/newfont.cpp @@ -1,5 +1,5 @@ /* Generated by convbdf on Sat Jun 17 01:34:15 2006. */ -#include "graphics/font.h" +#include "graphics/fonts/bdf.h" /* Font information: name: clR6x12-L1 @@ -7419,7 +7419,7 @@ static const unsigned long _sysfont_offset[] = { }; /* Exported structure definition. */ -static const FontDesc desc = { +static const BdfFontDesc desc = { "clR6x12-L1", 6, 12, diff --git a/graphics/fonts/newfont_big.cpp b/graphics/fonts/newfont_big.cpp index 7b15a6ab389..59c54a4551a 100644 --- a/graphics/fonts/newfont_big.cpp +++ b/graphics/fonts/newfont_big.cpp @@ -1,5 +1,5 @@ /* Generated by convbdf on Tue Jun 13 00:00:22 2006. */ -#include "graphics/font.h" +#include "graphics/fonts/bdf.h" /* Font information: name: helvB12-L1 @@ -5523,7 +5523,7 @@ static const BBX _sysfont_bbx[] = { }; /* Exported structure definition. */ -static const FontDesc desc = { +static const BdfFontDesc desc = { "helvB12-L1", 13, 14, diff --git a/graphics/surface.cpp b/graphics/surface.cpp index cee8e614381..0fad25734c7 100644 --- a/graphics/surface.cpp +++ b/graphics/surface.cpp @@ -127,7 +127,7 @@ void Surface::vLine(int x, int y, int y2, uint32 color) { *ptr = (uint16)color; ptr += pitch / 2; } - + } else if (format.bytesPerPixel == 4) { uint32 *ptr = (uint32 *)getBasePtr(x, y); while (y++ <= y2) { diff --git a/graphics/surface.h b/graphics/surface.h index 2a0f71955ad..018a283aad8 100644 --- a/graphics/surface.h +++ b/graphics/surface.h @@ -189,7 +189,7 @@ struct Surface { /** * A deleter for Surface objects which can be used with SharedPtr. - * + * * This deleter assures Surface::free is called on deletion. */ struct SharedPtrSurfaceDeleter { diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index ef9c75eb9cd..5ed56abdb57 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -33,6 +33,7 @@ #include "graphics/imagedec.h" #include "graphics/surface.h" #include "graphics/VectorRenderer.h" +#include "graphics/fonts/bdf.h" #include "gui/widget.h" #include "gui/ThemeEngine.h" @@ -1393,7 +1394,7 @@ const Graphics::Font *ThemeEngine::loadFontFromArchive(const Common::String &fil if (_themeArchive) stream = _themeArchive->createReadStreamForMember(filename); if (stream) { - font = Graphics::NewFont::loadFont(*stream); + font = Graphics::BdfFont::loadFont(*stream); delete stream; } @@ -1407,7 +1408,7 @@ const Graphics::Font *ThemeEngine::loadCachedFontFromArchive(const Common::Strin if (_themeArchive) stream = _themeArchive->createReadStreamForMember(filename); if (stream) { - font = Graphics::NewFont::loadFromCache(*stream); + font = Graphics::BdfFont::loadFromCache(*stream); delete stream; } @@ -1421,7 +1422,7 @@ const Graphics::Font *ThemeEngine::loadFont(const Common::String &filename) { if (!cacheFilename.empty()) { if (fontFile.open(cacheFilename)) { - font = Graphics::NewFont::loadFromCache(fontFile); + font = Graphics::BdfFont::loadFromCache(fontFile); } if (font) @@ -1433,7 +1434,7 @@ const Graphics::Font *ThemeEngine::loadFont(const Common::String &filename) { // normal open if (fontFile.open(filename)) { - font = Graphics::NewFont::loadFont(fontFile); + font = Graphics::BdfFont::loadFont(fontFile); } if (!font) { @@ -1442,7 +1443,7 @@ const Graphics::Font *ThemeEngine::loadFont(const Common::String &filename) { if (font) { if (!cacheFilename.empty()) { - if (!Graphics::NewFont::cacheFontData(*(const Graphics::NewFont *)font, cacheFilename)) { + if (!Graphics::BdfFont::cacheFontData(*(const Graphics::BdfFont *)font, cacheFilename)) { warning("Couldn't create cache file for font '%s'", filename.c_str()); } } diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp index 5b1faa40750..db45b5a9953 100644 --- a/gui/ThemeParser.cpp +++ b/gui/ThemeParser.cpp @@ -352,11 +352,8 @@ bool ThemeParser::parserCallback_drawdata(ParserNode *node) { } if (node->values.contains("cache")) { - if (node->values["cache"] == "true") - cached = true; - else if (node->values["cache"] == "false") - cached = false; - else return parserError("'Parsed' value must be either true or false."); + if (!Common::parseBool(node->values["cache"], cached)) + return parserError("'Parsed' value must be either true or false."); } if (_theme->addDrawData(node->values["id"], cached) == false) @@ -595,9 +592,7 @@ bool ThemeParser::parserCallback_widget(ParserNode *node) { bool enabled = true; if (node->values.contains("enabled")) { - if (node->values["enabled"] == "false") - enabled = false; - else if (node->values["enabled"] != "true") + if (!Common::parseBool(node->values["enabled"], enabled)) return parserError("Invalid value for Widget enabling (expecting true/false)"); } @@ -641,9 +636,7 @@ bool ThemeParser::parserCallback_dialog(ParserNode *node) { } if (node->values.contains("enabled")) { - if (node->values["enabled"] == "false") - enabled = false; - else if (node->values["enabled"] != "true") + if (!Common::parseBool(node->values["enabled"], enabled)) return parserError("Invalid value for Dialog enabling (expecting true/false)"); } @@ -677,16 +670,19 @@ bool ThemeParser::parserCallback_import(ParserNode *node) { bool ThemeParser::parserCallback_layout(ParserNode *node) { int spacing = -1; + bool center = false; if (node->values.contains("spacing")) { if (!parseIntegerKey(node->values["spacing"], 1, &spacing)) return false; } + Common::parseBool(node->values["center"], center); + if (node->values["type"] == "vertical") - _theme->getEvaluator()->addLayout(GUI::ThemeLayout::kLayoutVertical, spacing, node->values["center"] == "true"); + _theme->getEvaluator()->addLayout(GUI::ThemeLayout::kLayoutVertical, spacing, center); else if (node->values["type"] == "horizontal") - _theme->getEvaluator()->addLayout(GUI::ThemeLayout::kLayoutHorizontal, spacing, node->values["center"] == "true"); + _theme->getEvaluator()->addLayout(GUI::ThemeLayout::kLayoutHorizontal, spacing, center); else return parserError("Invalid layout type. Only 'horizontal' and 'vertical' layouts allowed."); diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp index 5d87f13d959..d278010eaf5 100644 --- a/gui/gui-manager.cpp +++ b/gui/gui-manager.cpp @@ -39,7 +39,9 @@ #include "graphics/cursorman.h" +namespace Common { DECLARE_SINGLETON(GUI::GuiManager); +} namespace GUI { diff --git a/gui/massadd.cpp b/gui/massadd.cpp index b0adce3f478..70580e8b9c2 100644 --- a/gui/massadd.cpp +++ b/gui/massadd.cpp @@ -24,6 +24,7 @@ #include "common/config-manager.h" #include "common/debug.h" #include "common/system.h" +#include "common/taskbar.h" #include "common/translation.h" #include "gui/launcher.h" // For addGameToConf() @@ -60,6 +61,7 @@ MassAddDialog::MassAddDialog(const Common::FSNode &startDir) : Dialog("MassAdd"), _dirsScanned(0), _oldGamesCount(0), + _dirTotal(0), _okButton(0), _dirProgressText(0), _gameProgressText(0) { @@ -69,14 +71,14 @@ MassAddDialog::MassAddDialog(const Common::FSNode &startDir) // The dir we start our scan at _scanStack.push(startDir); -// Removed for now... Why would you put a title on mass add dialog called "Mass Add Dialog"? -// new StaticTextWidget(this, "massadddialog_caption", "Mass Add Dialog"); + // Removed for now... Why would you put a title on mass add dialog called "Mass Add Dialog"? + // new StaticTextWidget(this, "massadddialog_caption", "Mass Add Dialog"); _dirProgressText = new StaticTextWidget(this, "MassAdd.DirProgressText", - _("... progress ...")); + _("... progress ...")); _gameProgressText = new StaticTextWidget(this, "MassAdd.GameProgressText", - _("... progress ...")); + _("... progress ...")); _dirProgressText->setAlign(Graphics::kTextAlignCenter); _gameProgressText->setAlign(Graphics::kTextAlignCenter); @@ -130,6 +132,12 @@ struct GameDescLess { void MassAddDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { +#if defined(USE_TASKBAR) + // Remove progress bar and count from taskbar + g_system->getTaskbarManager()->setProgressState(Common::TaskbarManager::kTaskbarNoProgress); + g_system->getTaskbarManager()->setCount(0); +#endif + // FIXME: It's a really bad thing that we use two arbitrary constants if (cmd == kOkCmd) { // Sort the detected games. This is not strictly necessary, but nice for @@ -226,10 +234,17 @@ void MassAddDialog::handleTickle() { for (Common::FSList::const_iterator file = files.begin(); file != files.end(); ++file) { if (file->isDirectory()) { _scanStack.push(*file); + + _dirTotal++; } } _dirsScanned++; + +#if defined(USE_TASKBAR) + g_system->getTaskbarManager()->setProgressValue(_dirsScanned, _dirTotal); + g_system->getTaskbarManager()->setCount(_games.size()); +#endif } diff --git a/gui/massadd.h b/gui/massadd.h index 15cef7ba681..73502138359 100644 --- a/gui/massadd.h +++ b/gui/massadd.h @@ -60,6 +60,7 @@ private: int _dirsScanned; int _oldGamesCount; + int _dirTotal; Widget *_okButton; StaticTextWidget *_dirProgressText; diff --git a/gui/options.cpp b/gui/options.cpp index fd8d8fa52f4..32a8af86ce4 100644 --- a/gui/options.cpp +++ b/gui/options.cpp @@ -555,7 +555,7 @@ void OptionsDialog::addMIDIControls(GuiObject *boss, const Common::String &prefi _gmDevicePopUp->appendEntry(d->getCompleteName(), d->getHandle()); } else if (d->getMusicDriverId() == "auto") { _gmDevicePopUp->appendEntry(_("Use first available device"), d->getHandle()); - } + } } } @@ -602,7 +602,7 @@ void OptionsDialog::addMT32Controls(GuiObject *boss, const Common::String &prefi // Make sure the null device is the first one in the list to avoid undesired // auto detection for users who don't have a saved setting yet. for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) { - MusicDevices i = (**m)->getDevices(); + MusicDevices i = (**m)->getDevices(); for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) { if (d->getMusicDriverId() == "null") _mt32DevicePopUp->appendEntry(_("Don't use Roland MT-32 music"), d->getHandle()); diff --git a/gui/saveload.cpp b/gui/saveload.cpp index 460246e5fcd..02ddf814dc2 100644 --- a/gui/saveload.cpp +++ b/gui/saveload.cpp @@ -131,7 +131,7 @@ void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 da if (_list->isEditable() || !_list->getSelectedString().empty()) { _list->endEditMode(); if (!_saveList.empty()) { - setResult(atoi(_saveList[selItem].save_slot().c_str())); + setResult(_saveList[selItem].getSaveSlot()); _resultString = _list->getSelectedString(); } close(); @@ -141,7 +141,7 @@ void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 da case kChooseCmd: _list->endEditMode(); if (!_saveList.empty()) { - setResult(atoi(_saveList[selItem].save_slot().c_str())); + setResult(_saveList[selItem].getSaveSlot()); _resultString = _list->getSelectedString(); } close(); @@ -154,7 +154,7 @@ void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 da MessageDialog alert(_("Do you really want to delete this savegame?"), _("Delete"), _("Cancel")); if (alert.runModal() == GUI::kMessageOK) { - (*_plugin)->removeSaveState(_target.c_str(), atoi(_saveList[selItem].save_slot().c_str())); + (*_plugin)->removeSaveState(_target.c_str(), _saveList[selItem].getSaveSlot()); setResult(-1); _list->setSelected(-1); @@ -241,10 +241,10 @@ void SaveLoadChooser::updateSelection(bool redraw) { _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())); + SaveStateDescriptor desc = (*_plugin)->querySaveMetaInfos(_target.c_str(), _saveList[selItem].getSaveSlot()); - isDeletable = desc.getBool("is_deletable") && _delSupport; - isWriteProtected = desc.getBool("is_write_protected"); + isDeletable = desc.getDeletableFlag() && _delSupport; + isWriteProtected = desc.getWriteProtectedFlag(); // Don't allow the user to change the description of write protected games if (isWriteProtected) @@ -259,16 +259,19 @@ void SaveLoadChooser::updateSelection(bool redraw) { } if (_saveDateSupport) { - if (desc.contains("save_date")) - _date->setLabel(_("Date: ") + desc.getVal("save_date")); + const Common::String &saveDate = desc.getSaveDate(); + if (!saveDate.empty()) + _date->setLabel(_("Date: ") + saveDate); - if (desc.contains("save_time")) - _time->setLabel(_("Time: ") + desc.getVal("save_time")); + const Common::String &saveTime = desc.getSaveTime(); + if (!saveTime.empty()) + _time->setLabel(_("Time: ") + saveTime); } if (_playTimeSupport) { - if (desc.contains("play_time")) - _playtime->setLabel(_("Playtime: ") + desc.getVal("play_time")); + const Common::String &playTime = desc.getPlayTime(); + if (!playTime.empty()) + _playtime->setLabel(_("Playtime: ") + playTime); } } @@ -326,25 +329,25 @@ void SaveLoadChooser::updateSaveList() { 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()); + saveSlot = x->getSaveSlot(); if (curSlot < saveSlot) { while (curSlot < saveSlot) { SaveStateDescriptor dummySave(curSlot, ""); _saveList.insert_at(curSlot, dummySave); - saveNames.push_back(dummySave.description()); + saveNames.push_back(dummySave.getDescription()); colors.push_back(ThemeEngine::kFontColorNormal); curSlot++; } // Sync the save list iterator for (x = _saveList.begin(); x != _saveList.end(); ++x) { - if (atoi(x->save_slot().c_str()) == saveSlot) + if (x->getSaveSlot() == saveSlot) break; } } // Show "Untitled savestate" for empty/whitespace savegame descriptions - Common::String description = x->description(); + Common::String description = x->getDescription(); Common::String trimmedDescription = description; trimmedDescription.trim(); if (trimmedDescription.empty()) { diff --git a/po/POTFILES b/po/POTFILES index 1a49907d35d..63c51a79d29 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -21,6 +21,7 @@ common/util.cpp engines/advancedDetector.cpp engines/dialogs.cpp +engines/engine.cpp audio/fmopl.cpp audio/mididrv.cpp @@ -35,4 +36,5 @@ audio/softsynth/pcspk.cpp backends/keymapper/remap-dialog.cpp backends/midi/windows.cpp -backends/platform/sdl/graphics.cpp +backends/graphics/surfacesdl/surfacesdl-graphics.cpp +backends/events/default/default-events.cpp diff --git a/ports.mk b/ports.mk index e5a31196b39..210fb296f87 100644 --- a/ports.mk +++ b/ports.mk @@ -175,6 +175,7 @@ residualwinres.o: $(srcdir)/icons/residual.ico $(DIST_FILES_THEMES) $(DIST_FILES # Special target to create a win32 snapshot binary win32dist: $(EXECUTABLE) mkdir -p $(WIN32PATH) + mkdir -p $(WIN32PATH)/graphics $(STRIP) $(EXECUTABLE) -o $(WIN32PATH)/$(EXECUTABLE) cp $(DIST_FILES_THEMES) $(WIN32PATH) ifdef DIST_FILES_ENGINEDATA @@ -227,6 +228,29 @@ ifdef DIST_FILES_ENGINEDATA endif cp $(DIST_FILES_DOCS) $(AOS4PATH) -# Mark special targets as phony -.PHONY: deb bundle osxsnap win32dist install uninstall +# +# PlayStation 3 specific +# +ps3pkg: $(EXECUTABLE) + $(STRIP) $(EXECUTABLE) + sprxlinker $(EXECUTABLE) + mkdir -p ps3pkg/USRDIR/data/ + mkdir -p ps3pkg/USRDIR/doc/ + mkdir -p ps3pkg/USRDIR/saves/ + make_self_npdrm "$(EXECUTABLE)" ps3pkg/USRDIR/EBOOT.BIN UP0001-SCUM12000_00-0000000000000000 + cp $(DIST_FILES_THEMES) ps3pkg/USRDIR/data/ +ifdef DIST_FILES_ENGINEDATA + cp $(DIST_FILES_ENGINEDATA) ps3pkg/USRDIR/data/ +endif + cp $(DIST_FILES_DOCS) ps3pkg/USRDIR/doc/ + cp $(srcdir)/dists/ps3/readme-ps3.md ps3pkg/USRDIR/doc/ + cp $(srcdir)/backends/vkeybd/packs/vkeybd_default.zip ps3pkg/USRDIR/data/ + cp $(srcdir)/dists/ps3/ICON0.PNG ps3pkg/ + cp $(srcdir)/dists/ps3/PIC1.PNG ps3pkg/ + sfo.py -f $(srcdir)/dists/ps3/sfo.xml ps3pkg/PARAM.SFO + pkg.py --contentid UP0001-SCUM12000_00-0000000000000000 ps3pkg/ scummvm-ps3.pkg + package_finalize residual-ps3.pkg + +# Mark special targets as phony +.PHONY: deb bundle osxsnap win32dist install uninstall ps3pkg