diff --git a/Makefile.common b/Makefile.common index 34ca0f68523..674dcc3a42d 100644 --- a/Makefile.common +++ b/Makefile.common @@ -29,6 +29,9 @@ MODULES += \ backends \ common \ +ifdef USE_MT32EMU +MODULES += sound/softsynth/mt32 +endif ###################################################################### # The build rules follow - normally you should have no need to @@ -68,7 +71,7 @@ endif # The build rule for the Residual executable $(EXECUTABLE): $(OBJS) - $(QUIET_LINK)$(CXX) $(LDFLAGS) $(PRE_OBJS_FLAGS) $+ $(POST_OBJS_FLAGS) $(LIBS) -o $@ + $(QUIET_LINK)$(LD) $(LDFLAGS) $(PRE_OBJS_FLAGS) $+ $(POST_OBJS_FLAGS) $(LIBS) -o $@ distclean: clean $(RM) config.h config.mk config.log @@ -90,7 +93,12 @@ ifdef CXX_UPDATE_DEP_FLAG $(QUIET)$(MKDIR) $(*D)/$(DEPDIR) $(QUIET_CXX)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o -# Build rule for Objective-C files. Strictly speaking, this is for OS X only. + +# Build rules for Objective-C and Objective-C++ files. Strictly speaking, this is for OS X only. +%.o: %.mm + $(QUIET)$(MKDIR) $(*D)/$(DEPDIR) + $(QUIET_CXX)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o + %.o: %.m $(QUIET)$(MKDIR) $(*D)/$(DEPDIR) $(QUIET_CXX)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(OBJCFLAGS) -c $(<) -o $*.o @@ -104,18 +112,31 @@ else endif - +# Build rule for assembler files %.o: %.s + $(QUIET)$(MKDIR) $(*D) $(QUIET_AS)$(AS) $(ASFLAGS) $(<) -o $*.o +# Build rule for assembler files with preprocessing +%.o: %.S + $(QUIET)$(MKDIR) $(*D)/$(DEPDIR) + $(QUIET_AS)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(ASFLAGS) -c $(<) -o $*.o + ifdef HAVE_NASM +# Build rule for NASM assembler files %.o: %.asm + $(QUIET)$(MKDIR) $(*D) $(QUIET_NASM)$(NASM) -O1 $(NASMFLAGS) -g -o $*.o $(<) endif # Include the dependency tracking files. -include $(wildcard $(addsuffix /*.d,$(DEPDIRS))) +# Mark *.d files and most *.mk files as PHONY. This stops make from trying to +# recreate them (which it can't), and in particular from looking for potential +# source files. This can save quite a bit of disk access time. +.PHONY: $(wildcard $(addsuffix /*.d,$(DEPDIRS))) $(addprefix $(srcdir)/, $(addsuffix /module.mk,$(MODULES))) \ + $(srcdir)/$(port_mk) $(srcdir)/rules.mk $(srcdir)/engines/engines.mk ###################################################################### # Get the current version information diff --git a/backends/base-backend.cpp b/backends/base-backend.cpp index 661767da3c2..c9217704c0c 100644 --- a/backends/base-backend.cpp +++ b/backends/base-backend.cpp @@ -46,13 +46,15 @@ Common::EventManager *BaseBackend::getEventManager() { } #if defined(UNIX) -#ifdef MACOSX -#define DEFAULT_CONFIG_FILE "Library/Preferences/Residual Preferences" +#if defined(SAMSUNGTV) +#define DEFAULT_CONFIG_FILE "/dtv/usb/sda1/.scummvmrc" #else #define DEFAULT_CONFIG_FILE ".residualrc" #endif -#else -#define DEFAULT_CONFIG_FILE "residual.ini" +#endif + +#if !defined(UNIX) +#define DEFAULT_CONFIG_FILE "scummvm.ini" #endif Common::SeekableReadStream *BaseBackend::createConfigReadStream() { diff --git a/backends/fs/amigaos4/amigaos4-fs.cpp b/backends/fs/amigaos4/amigaos4-fs.cpp index 9e4943faf25..c0a38817a6c 100644 --- a/backends/fs/amigaos4/amigaos4-fs.cpp +++ b/backends/fs/amigaos4/amigaos4-fs.cpp @@ -172,7 +172,7 @@ AmigaOSFilesystemNode::AmigaOSFilesystemNode(const Common::String &p) { _nProt = pExd->Protection; if (EXD_IS_DIRECTORY(pExd)) { _bIsDirectory = true; - _pFileLock = IDOS->Lock((CONST_STRPTR)_sPath.c_str(), SHARED_LOCK);; + _pFileLock = IDOS->Lock((CONST_STRPTR)_sPath.c_str(), SHARED_LOCK); _bIsValid = (_pFileLock != 0); // Add a trailing slash if it is needed diff --git a/backends/fs/stdiostream.cpp b/backends/fs/stdiostream.cpp index 1c7dcf2fea1..be7ee0a7b76 100644 --- a/backends/fs/stdiostream.cpp +++ b/backends/fs/stdiostream.cpp @@ -20,77 +20,11 @@ * * $URL$ * $Id$ + * */ #include "backends/fs/stdiostream.h" -#include - -#if defined(MACOSX) || defined(IPHONE) -#include "CoreFoundation/CoreFoundation.h" -#endif - - -#ifdef __PLAYSTATION2__ - // for those replaced fopen/fread/etc functions - typedef unsigned long uint64; - typedef signed long int64; - #include "backends/platform/ps2/fileio.h" - - #define fopen(a, b) ps2_fopen(a, b) - #define fclose(a) ps2_fclose(a) - #define fseek(a, b, c) ps2_fseek(a, b, c) - #define ftell(a) ps2_ftell(a) - #define feof(a) ps2_feof(a) - #define fread(a, b, c, d) ps2_fread(a, b, c, d) - #define fwrite(a, b, c, d) ps2_fwrite(a, b, c, d) - - #define fprintf ps2_fprintf // used in common/util.cpp - #define fflush(a) ps2_fflush(a) // used in common/util.cpp - #define ferror(a) ps2_ferror(a) - #define clearerr(a) ps2_clearerr(a) - - //#define fgetc(a) ps2_fgetc(a) // not used - //#define fgets(a, b, c) ps2_fgets(a, b, c) // not used - //#define fputc(a, b) ps2_fputc(a, b) // not used - //#define fputs(a, b) ps2_fputs(a, b) // not used - - //#define fsize(a) ps2_fsize(a) // not used -- and it is not a standard function either -#endif - -#ifdef __DS__ - - // These functions replace the standard library functions of the same name. - // As this header is included after the standard one, I have the chance to #define - // all of these to my own code. - // - // A #define is the only way, as redefinig the functions would cause linker errors. - - // These functions need to be #undef'ed, as their original definition - // in devkitarm is done with #includes (ugh!) - #undef feof - #undef clearerr - //#undef getc - //#undef ferror - - #include "backends/fs/ds/ds-fs.h" - - - // Only functions used in the ScummVM source have been defined here! - #define fopen(name, mode) DS::std_fopen(name, mode) - #define fclose(handle) DS::std_fclose(handle) - #define fread(ptr, size, items, file) DS::std_fread(ptr, size, items, file) - #define fwrite(ptr, size, items, file) DS::std_fwrite(ptr, size, items, file) - #define feof(handle) DS::std_feof(handle) - #define ftell(handle) DS::std_ftell(handle) - #define fseek(handle, offset, whence) DS::std_fseek(handle, offset, whence) - #define clearerr(handle) DS::std_clearerr(handle) - #define fflush(file) DS::std_fflush(file) - #undef ferror - #define ferror(handle) DS::std_ferror(handle) - -#endif - StdioStream::StdioStream(void *handle) : _handle(handle) { assert(handle); } diff --git a/backends/keymapper/action.cpp b/backends/keymapper/action.cpp index d5fb559c099..dd9ec998a28 100644 --- a/backends/keymapper/action.cpp +++ b/backends/keymapper/action.cpp @@ -57,6 +57,6 @@ const HardwareKey *Action::getMappedKey() const { return _hwKey; } -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_KEYMAPPER diff --git a/backends/keymapper/action.h b/backends/keymapper/action.h index e041f003eda..3313ba84b2c 100644 --- a/backends/keymapper/action.h +++ b/backends/keymapper/action.h @@ -119,7 +119,7 @@ struct ActionPriorityComp : public BinaryFunction { } }; -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_KEYMAPPER diff --git a/backends/keymapper/hardware-key.h b/backends/keymapper/hardware-key.h index 94b2b0935c6..846a4e5c825 100644 --- a/backends/keymapper/hardware-key.h +++ b/backends/keymapper/hardware-key.h @@ -131,7 +131,7 @@ private: }; -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_KEYMAPPER diff --git a/backends/keymapper/keymap.cpp b/backends/keymapper/keymap.cpp index 04020defb24..01e92bcce4f 100644 --- a/backends/keymapper/keymap.cpp +++ b/backends/keymapper/keymap.cpp @@ -36,7 +36,7 @@ namespace Common { Keymap::Keymap(const Keymap& km) : _actions(km._actions), _keymap(), _configDomain(0) { List::iterator it; - for (it = _actions.begin(); it != _actions.end(); it++) { + for (it = _actions.begin(); it != _actions.end(); ++it) { const HardwareKey *hwKey = (*it)->getMappedKey(); if (hwKey) { @@ -48,7 +48,7 @@ Keymap::Keymap(const Keymap& km) : _actions(km._actions), _keymap(), _configDoma Keymap::~Keymap() { List::iterator it; - for (it = _actions.begin(); it != _actions.end(); it++) + for (it = _actions.begin(); it != _actions.end(); ++it) delete *it; } @@ -87,7 +87,7 @@ Action *Keymap::getAction(const char *id) { Action *Keymap::findAction(const char *id) { List::iterator it; - for (it = _actions.begin(); it != _actions.end(); it++) { + for (it = _actions.begin(); it != _actions.end(); ++it) { if (strncmp((*it)->id, id, ACTION_ID_SIZE) == 0) return *it; } @@ -97,7 +97,7 @@ Action *Keymap::findAction(const char *id) { const Action *Keymap::findAction(const char *id) const { List::const_iterator it; - for (it = _actions.begin(); it != _actions.end(); it++) { + for (it = _actions.begin(); it != _actions.end(); ++it) { if (strncmp((*it)->id, id, ACTION_ID_SIZE) == 0) return *it; } @@ -127,7 +127,7 @@ void Keymap::loadMappings(const HardwareKeySet *hwKeys) { ConfigManager::Domain::iterator it; String prefix = KEYMAP_KEY_PREFIX + _name + "_"; - for (it = _configDomain->begin(); it != _configDomain->end(); it++) { + for (it = _configDomain->begin(); it != _configDomain->end(); ++it) { const String& key = it->_key; if (!key.hasPrefix(prefix.c_str())) @@ -164,7 +164,7 @@ void Keymap::saveMappings() { List::const_iterator it; String prefix = KEYMAP_KEY_PREFIX + _name + "_"; - for (it = _actions.begin(); it != _actions.end(); it++) { + for (it = _actions.begin(); it != _actions.end(); ++it) { uint actIdLen = strlen((*it)->id); actIdLen = (actIdLen > ACTION_ID_SIZE) ? ACTION_ID_SIZE : actIdLen; @@ -186,7 +186,7 @@ bool Keymap::isComplete(const HardwareKeySet *hwKeys) { bool allMapped = true; uint numberMapped = 0; - for (it = _actions.begin(); it != _actions.end(); it++) { + for (it = _actions.begin(); it != _actions.end(); ++it) { if ((*it)->getMappedKey()) { numberMapped++; } else { @@ -336,6 +336,6 @@ Action *Keymap::getParentMappedAction(KeyState key) { } } -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_KEYMAPPER diff --git a/backends/keymapper/keymap.h b/backends/keymapper/keymap.h index 818bb6787b9..7fdcb21e593 100644 --- a/backends/keymapper/keymap.h +++ b/backends/keymapper/keymap.h @@ -147,7 +147,7 @@ private: }; -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_KEYMAPPER diff --git a/backends/keymapper/keymapper.cpp b/backends/keymapper/keymapper.cpp index 240749a3797..bc7cbb819f3 100644 --- a/backends/keymapper/keymapper.cpp +++ b/backends/keymapper/keymapper.cpp @@ -272,6 +272,6 @@ const HardwareKey *Keymapper::findHardwareKey(const KeyState& key) { return (_hardwareKeys) ? _hardwareKeys->findHardwareKey(key) : 0; } -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_KEYMAPPER diff --git a/backends/keymapper/keymapper.h b/backends/keymapper/keymapper.h index 3f23c8d7ba0..a6475b5c9f3 100644 --- a/backends/keymapper/keymapper.h +++ b/backends/keymapper/keymapper.h @@ -197,7 +197,7 @@ private: }; -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_KEYMAPPER diff --git a/backends/keymapper/remap-dialog.cpp b/backends/keymapper/remap-dialog.cpp index 6aa7e62cde3..1c8b8ed9207 100644 --- a/backends/keymapper/remap-dialog.cpp +++ b/backends/keymapper/remap-dialog.cpp @@ -94,7 +94,7 @@ void RemapDialog::open() { if (_globalKeymaps) { if (divider) _kmPopUp->appendEntry(""); - for (it = _globalKeymaps->begin(); it != _globalKeymaps->end(); it++) { + for (it = _globalKeymaps->begin(); it != _globalKeymaps->end(); ++it) { _kmPopUp->appendEntry(it->_value->getName() + " (Global)", idx); _keymapTable[idx++] = it->_value; } @@ -104,7 +104,7 @@ void RemapDialog::open() { if (_gameKeymaps) { if (divider) _kmPopUp->appendEntry(""); - for (it = _gameKeymaps->begin(); it != _gameKeymaps->end(); it++) { + for (it = _gameKeymaps->begin(); it != _gameKeymaps->end(); ++it) { _kmPopUp->appendEntry(it->_value->getName() + " (Game)", idx); _keymapTable[idx++] = it->_value; } @@ -317,7 +317,7 @@ void RemapDialog::loadKeymap() { List::iterator it; - for (it = km->getActions().begin(); it != km->getActions().end(); it++) { + for (it = km->getActions().begin(); it != km->getActions().end(); ++it) { ActionInfo info = {*it, false, (*it)->description}; _currentActions.push_back(info); @@ -380,6 +380,6 @@ void RemapDialog::refreshKeymap() { } -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_KEYMAPPER diff --git a/backends/keymapper/remap-dialog.h b/backends/keymapper/remap-dialog.h index 81472197b3f..7216476f501 100644 --- a/backends/keymapper/remap-dialog.h +++ b/backends/keymapper/remap-dialog.h @@ -92,7 +92,7 @@ protected: }; -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_KEYMAPPER diff --git a/backends/keymapper/types.h b/backends/keymapper/types.h index b7cfbaffc95..1b7a2b95cb9 100644 --- a/backends/keymapper/types.h +++ b/backends/keymapper/types.h @@ -23,8 +23,8 @@ * */ -#ifndef COMMON_TYPES_H -#define COMMON_TYPES_H +#ifndef KEYMAPPER_TYPES_H +#define KEYMAPPER_TYPES_H #include "common/scummsys.h" @@ -70,8 +70,8 @@ enum ActionType { kActionTypeMax }; -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_KEYMAPPER -#endif // #ifndef COMMON_TYPES_H +#endif // #ifndef KEYMAPPER_TYPES_H diff --git a/backends/platform/sdl/events.cpp b/backends/platform/sdl/events.cpp index 9c5fa740e12..043f9293af5 100644 --- a/backends/platform/sdl/events.cpp +++ b/backends/platform/sdl/events.cpp @@ -174,8 +174,6 @@ static byte SDLModToOSystemKeyFlags(SDLMod mod) { bool OSystem_SDL::pollEvent(Common::Event &event) { SDL_Event ev; - int axis; - byte b = 0; handleKbdMouse(); /* Residual doesn't support this @@ -188,279 +186,308 @@ bool OSystem_SDL::pollEvent(Common::Event &event) { */ while (SDL_PollEvent(&ev)) { preprocessEvents(&ev); - - switch (ev.type) { - case SDL_KEYDOWN:{ - b = event.kbd.flags = SDLModToOSystemKeyFlags(SDL_GetModState()); - -/* Residual doesn't support this - // Alt-Return and Alt-Enter toggle full screen mode - if (b == Common::KBD_ALT && (ev.key.keysym.sym == SDLK_RETURN - || ev.key.keysym.sym == SDLK_KP_ENTER)) { - beginGFXTransaction(); - setFullscreenMode(!_videoMode.fullscreen); - endGFXTransaction(); -#ifdef USE_OSD - if (_videoMode.fullscreen) - displayMessageOnOSD("Fullscreen mode"); - else - displayMessageOnOSD("Windowed mode"); -#endif - - break; - } - - // Alt-S: Create a screenshot - if (b == Common::KBD_ALT && ev.key.keysym.sym == 's') { - char filename[20]; - - for (int n = 0;; n++) { - SDL_RWops *file; - - sprintf(filename, "scummvm%05d.bmp", n); - file = SDL_RWFromFile(filename, "r"); - if (!file) - break; - SDL_RWclose(file); - } - if (saveScreenshot(filename)) - printf("Saved '%s'\n", filename); - else - printf("Could not save screenshot!\n"); - break; - } -*/ - // Ctrl-m toggles mouse capture - if (b == Common::KBD_CTRL && ev.key.keysym.sym == 'm') { - toggleMouseGrab(); - break; - } - -#if defined(MACOSX) - // On Macintosh', Cmd-Q quits - if ((ev.key.keysym.mod & KMOD_META) && ev.key.keysym.sym == 'q') { - event.type = Common::EVENT_QUIT; - return true; - } -#elif defined(UNIX) - // On other unices, Control-Q quits - if ((ev.key.keysym.mod & KMOD_CTRL) && ev.key.keysym.sym == 'q') { - event.type = Common::EVENT_QUIT; - return true; - } -#else - // Ctrl-z and Alt-X quit - if ((b == Common::KBD_CTRL && ev.key.keysym.sym == 'z') || (b == Common::KBD_ALT && ev.key.keysym.sym == 'x')) { - event.type = Common::EVENT_QUIT; - return true; - } -#endif - - if ((ev.key.keysym.mod & KMOD_CTRL) && ev.key.keysym.sym == 'u') { - event.type = Common::EVENT_MUTE; - return true; - } -/* Residual doesn't support this - // Ctrl-Alt- will change the GFX mode - if ((b & (Common::KBD_CTRL|Common::KBD_ALT)) == (Common::KBD_CTRL|Common::KBD_ALT)) { - - handleScalerHotkeys(ev.key); - break; - }*/ - const bool event_complete = remapKey(ev, event); - - if (event_complete) - return true; - - 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); - + if (dispatchSDLEvent(ev, event)) return true; - } - case SDL_KEYUP: - { - const bool event_complete = remapKey(ev,event); - - if (event_complete) - return true; - - 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); - b = event.kbd.flags = SDLModToOSystemKeyFlags(SDL_GetModState()); - - // Ctrl-Alt- will change the GFX mode - if ((b & (Common::KBD_CTRL|Common::KBD_ALT)) == (Common::KBD_CTRL|Common::KBD_ALT)) { - // Swallow these key up events - break; - } - - return true; - } - case SDL_MOUSEMOTION: - event.type = Common::EVENT_MOUSEMOVE; - fillMouseEvent(event, ev.motion.x, ev.motion.y); - - //setMousePos(event.mouse.x, event.mouse.y); - return true; - - case SDL_MOUSEBUTTONDOWN: - if (ev.button.button == SDL_BUTTON_LEFT) - event.type = Common::EVENT_LBUTTONDOWN; - else if (ev.button.button == SDL_BUTTON_RIGHT) - event.type = Common::EVENT_RBUTTONDOWN; -#if defined(SDL_BUTTON_WHEELUP) && defined(SDL_BUTTON_WHEELDOWN) - else if (ev.button.button == SDL_BUTTON_WHEELUP) - event.type = Common::EVENT_WHEELUP; - else if (ev.button.button == SDL_BUTTON_WHEELDOWN) - event.type = Common::EVENT_WHEELDOWN; -#endif -#if defined(SDL_BUTTON_MIDDLE) - else if (ev.button.button == SDL_BUTTON_MIDDLE) - event.type = Common::EVENT_MBUTTONDOWN; -#endif - else - break; - - fillMouseEvent(event, ev.button.x, ev.button.y); - - return true; - - case SDL_MOUSEBUTTONUP: - if (ev.button.button == SDL_BUTTON_LEFT) - event.type = Common::EVENT_LBUTTONUP; - else if (ev.button.button == SDL_BUTTON_RIGHT) - event.type = Common::EVENT_RBUTTONUP; -#if defined(SDL_BUTTON_MIDDLE) - else if (ev.button.button == SDL_BUTTON_MIDDLE) - event.type = Common::EVENT_MBUTTONUP; -#endif - else - break; - fillMouseEvent(event, ev.button.x, ev.button.y); - - return true; - - case SDL_JOYBUTTONDOWN: - if (ev.jbutton.button == JOY_BUT_LMOUSE) { - event.type = Common::EVENT_LBUTTONDOWN; - fillMouseEvent(event, _km.x, _km.y); - } else if (ev.jbutton.button == JOY_BUT_RMOUSE) { - event.type = Common::EVENT_RBUTTONDOWN; - fillMouseEvent(event, _km.x, _km.y); - } else { - event.type = Common::EVENT_KEYDOWN; - 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); - break; - case JOY_BUT_PERIOD: - event.kbd.keycode = Common::KEYCODE_PERIOD; - event.kbd.ascii = mapKey(SDLK_PERIOD, 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); - break; - case JOY_BUT_F5: - event.kbd.keycode = Common::KEYCODE_F5; - event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0); - break; - } - } - return true; - - case SDL_JOYBUTTONUP: - if (ev.jbutton.button == JOY_BUT_LMOUSE) { - event.type = Common::EVENT_LBUTTONUP; - fillMouseEvent(event, _km.x, _km.y); - } else if (ev.jbutton.button == JOY_BUT_RMOUSE) { - event.type = Common::EVENT_RBUTTONUP; - fillMouseEvent(event, _km.x, _km.y); - } else { - event.type = Common::EVENT_KEYUP; - 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); - break; - case JOY_BUT_PERIOD: - event.kbd.keycode = Common::KEYCODE_PERIOD; - event.kbd.ascii = mapKey(SDLK_PERIOD, 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); - break; - case JOY_BUT_F5: - event.kbd.keycode = Common::KEYCODE_F5; - event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0); - break; - } - } - return true; - - case SDL_JOYAXISMOTION: - axis = ev.jaxis.value; - if ( axis > JOY_DEADZONE) { - axis -= JOY_DEADZONE; - event.type = Common::EVENT_MOUSEMOVE; - } else if ( axis < -JOY_DEADZONE ) { - axis += JOY_DEADZONE; - event.type = Common::EVENT_MOUSEMOVE; - } else - axis = 0; - - if ( ev.jaxis.axis == JOY_XAXIS) { -#ifdef JOY_ANALOG - _km.x_vel = axis/2000; - _km.x_down_count = 0; -#else - if (axis != 0) { - _km.x_vel = (axis > 0) ? 1:-1; - _km.x_down_count = 1; - } else { - _km.x_vel = 0; - _km.x_down_count = 0; - } -#endif - - } else if (ev.jaxis.axis == JOY_YAXIS) { -#ifndef JOY_INVERT_Y - axis = -axis; -#endif -#ifdef JOY_ANALOG - _km.y_vel = -axis / 2000; - _km.y_down_count = 0; -#else - if (axis != 0) { - _km.y_vel = (-axis > 0) ? 1: -1; - _km.y_down_count = 1; - } else { - _km.y_vel = 0; - _km.y_down_count = 0; - } -#endif - } - - fillMouseEvent(event, _km.x, _km.y); - - return true; - - case SDL_VIDEOEXPOSE: - event.type = Common::EVENT_SCREEN_CHANGED; // this two lines are Residual change - return true; - - case SDL_QUIT: - event.type = Common::EVENT_QUIT; - return true; - } } return false; } +bool OSystem_SDL::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { + switch (ev.type) { + case SDL_KEYDOWN: + return handleKeyDown(ev, event); + case SDL_KEYUP: + return handleKeyUp(ev, event); + case SDL_MOUSEMOTION: + return handleMouseMotion(ev, event); + case SDL_MOUSEBUTTONDOWN: + return handleMouseButtonDown(ev, event); + case SDL_MOUSEBUTTONUP: + return handleMouseButtonUp(ev, event); + case SDL_JOYBUTTONDOWN: + return handleJoyButtonDown(ev, event); + case SDL_JOYBUTTONUP: + return handleJoyButtonUp(ev, event); + case SDL_JOYAXISMOTION: + return handleJoyAxisMotion(ev, event); + + case SDL_VIDEOEXPOSE: + //_forceFull = true; + break; + + case SDL_QUIT: + event.type = Common::EVENT_QUIT; + return true; + + } + + return false; +} + + +bool OSystem_SDL::handleKeyDown(SDL_Event &ev, Common::Event &event) { + byte b = 0; + b = event.kbd.flags = SDLModToOSystemKeyFlags(SDL_GetModState()); + +/* Residual doesn't support this + // Alt-Return and Alt-Enter toggle full screen mode + if (b == Common::KBD_ALT && (ev.key.keysym.sym == SDLK_RETURN + || ev.key.keysym.sym == SDLK_KP_ENTER)) { + beginGFXTransaction(); + setFullscreenMode(!_videoMode.fullscreen); + endGFXTransaction(); +#ifdef USE_OSD + if (_videoMode.fullscreen) + displayMessageOnOSD("Fullscreen mode"); + else + displayMessageOnOSD("Windowed mode"); +#endif + + return false; + } + + // Alt-S: Create a screenshot + if (b == Common::KBD_ALT && ev.key.keysym.sym == 's') { + char filename[20]; + + for (int n = 0;; n++) { + SDL_RWops *file; + + sprintf(filename, "residual%05d.bmp", n); + file = SDL_RWFromFile(filename, "r"); + if (!file) + break; + SDL_RWclose(file); + } + if (saveScreenshot(filename)) + printf("Saved '%s'\n", filename); + else + printf("Could not save screenshot!\n"); + return false; + } +*/ + // Ctrl-m toggles mouse capture + if (b == Common::KBD_CTRL && ev.key.keysym.sym == 'm') { + toggleMouseGrab(); + return false; + } + +#if defined(MACOSX) + // On Macintosh', Cmd-Q quits + if ((ev.key.keysym.mod & KMOD_META) && ev.key.keysym.sym == 'q') { + event.type = Common::EVENT_QUIT; + return true; + } +#elif defined(UNIX) + // On other unices, Control-Q quits + if ((ev.key.keysym.mod & KMOD_CTRL) && ev.key.keysym.sym == 'q') { + event.type = Common::EVENT_QUIT; + return true; + } +#else + // Ctrl-z and Alt-X quit + if ((b == Common::KBD_CTRL && ev.key.keysym.sym == 'z') || (b == Common::KBD_ALT && ev.key.keysym.sym == 'x')) { + event.type = Common::EVENT_QUIT; + return true; + } +#endif + + if ((ev.key.keysym.mod & KMOD_CTRL) && ev.key.keysym.sym == 'u') { + event.type = Common::EVENT_MUTE; + return true; + } +/* Residual doesn't support this + // Ctrl-Alt- will change the GFX mode + if ((b & (Common::KBD_CTRL|Common::KBD_ALT)) == (Common::KBD_CTRL|Common::KBD_ALT)) { + if (handleScalerHotkeys(ev.key)) + return false; + }*/ + + if (remapKey(ev, event)) + return true; + + 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); + + return true; +} + +bool OSystem_SDL::handleKeyUp(SDL_Event &ev, Common::Event &event) { + if (remapKey(ev, event)) + return true; + + 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); + + // Ctrl-Alt- will change the GFX mode + byte b = event.kbd.flags = SDLModToOSystemKeyFlags(SDL_GetModState()); + if ((b & (Common::KBD_CTRL|Common::KBD_ALT)) == (Common::KBD_CTRL|Common::KBD_ALT)) { + // Swallow these key up events + return false; + } + + return true; +} + +bool OSystem_SDL::handleMouseMotion(SDL_Event &ev, Common::Event &event) { + event.type = Common::EVENT_MOUSEMOVE; + fillMouseEvent(event, ev.motion.x, ev.motion.y); + + //setMousePos(event.mouse.x, event.mouse.y); + return true; +} + +bool OSystem_SDL::handleMouseButtonDown(SDL_Event &ev, Common::Event &event) { + if (ev.button.button == SDL_BUTTON_LEFT) + event.type = Common::EVENT_LBUTTONDOWN; + else if (ev.button.button == SDL_BUTTON_RIGHT) + event.type = Common::EVENT_RBUTTONDOWN; +#if defined(SDL_BUTTON_WHEELUP) && defined(SDL_BUTTON_WHEELDOWN) + else if (ev.button.button == SDL_BUTTON_WHEELUP) + event.type = Common::EVENT_WHEELUP; + else if (ev.button.button == SDL_BUTTON_WHEELDOWN) + event.type = Common::EVENT_WHEELDOWN; +#endif +#if defined(SDL_BUTTON_MIDDLE) + else if (ev.button.button == SDL_BUTTON_MIDDLE) + event.type = Common::EVENT_MBUTTONDOWN; +#endif + else + return false; + + fillMouseEvent(event, ev.button.x, ev.button.y); + + return true; +} + +bool OSystem_SDL::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) { + if (ev.button.button == SDL_BUTTON_LEFT) + event.type = Common::EVENT_LBUTTONUP; + else if (ev.button.button == SDL_BUTTON_RIGHT) + event.type = Common::EVENT_RBUTTONUP; +#if defined(SDL_BUTTON_MIDDLE) + else if (ev.button.button == SDL_BUTTON_MIDDLE) + event.type = Common::EVENT_MBUTTONUP; +#endif + else + return false; + fillMouseEvent(event, ev.button.x, ev.button.y); + + return true; +} + +bool OSystem_SDL::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) { + if (ev.jbutton.button == JOY_BUT_LMOUSE) { + event.type = Common::EVENT_LBUTTONDOWN; + fillMouseEvent(event, _km.x, _km.y); + } else if (ev.jbutton.button == JOY_BUT_RMOUSE) { + event.type = Common::EVENT_RBUTTONDOWN; + fillMouseEvent(event, _km.x, _km.y); + } else { + event.type = Common::EVENT_KEYDOWN; + 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); + break; + case JOY_BUT_PERIOD: + event.kbd.keycode = Common::KEYCODE_PERIOD; + event.kbd.ascii = mapKey(SDLK_PERIOD, 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); + break; + case JOY_BUT_F5: + event.kbd.keycode = Common::KEYCODE_F5; + event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0); + break; + } + } + return true; +} + +bool OSystem_SDL::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) { + if (ev.jbutton.button == JOY_BUT_LMOUSE) { + event.type = Common::EVENT_LBUTTONUP; + fillMouseEvent(event, _km.x, _km.y); + } else if (ev.jbutton.button == JOY_BUT_RMOUSE) { + event.type = Common::EVENT_RBUTTONUP; + fillMouseEvent(event, _km.x, _km.y); + } else { + event.type = Common::EVENT_KEYUP; + 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); + break; + case JOY_BUT_PERIOD: + event.kbd.keycode = Common::KEYCODE_PERIOD; + event.kbd.ascii = mapKey(SDLK_PERIOD, 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); + break; + case JOY_BUT_F5: + event.kbd.keycode = Common::KEYCODE_F5; + event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0); + break; + } + } + return true; +} + +bool OSystem_SDL::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) { + int axis = ev.jaxis.value; + if ( axis > JOY_DEADZONE) { + axis -= JOY_DEADZONE; + event.type = Common::EVENT_MOUSEMOVE; + } else if ( axis < -JOY_DEADZONE ) { + axis += JOY_DEADZONE; + event.type = Common::EVENT_MOUSEMOVE; + } else + axis = 0; + + if ( ev.jaxis.axis == JOY_XAXIS) { +#ifdef JOY_ANALOG + _km.x_vel = axis/2000; + _km.x_down_count = 0; +#else + if (axis != 0) { + _km.x_vel = (axis > 0) ? 1:-1; + _km.x_down_count = 1; + } else { + _km.x_vel = 0; + _km.x_down_count = 0; + } +#endif + + } else if (ev.jaxis.axis == JOY_YAXIS) { +#ifndef JOY_INVERT_Y + axis = -axis; +#endif +#ifdef JOY_ANALOG + _km.y_vel = -axis / 2000; + _km.y_down_count = 0; +#else + if (axis != 0) { + _km.y_vel = (-axis > 0) ? 1: -1; + _km.y_down_count = 1; + } else { + _km.y_vel = 0; + _km.y_down_count = 0; + } +#endif + } + + fillMouseEvent(event, _km.x, _km.y); + + return true; +} + bool OSystem_SDL::remapKey(SDL_Event &ev, Common::Event &event) { #ifdef LINUPY // On Yopy map the End button to quit diff --git a/backends/platform/sdl/graphics.cpp b/backends/platform/sdl/graphics.cpp index 227474db768..986357429f3 100644 --- a/backends/platform/sdl/graphics.cpp +++ b/backends/platform/sdl/graphics.cpp @@ -424,13 +424,18 @@ void OSystem_SDL::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, _cursorFormat = Graphics::PixelFormat::createFormatCLUT8(); else if (format->bytesPerPixel <= _screenFormat.bytesPerPixel) _cursorFormat = *format; - keycolor &= (1 << (_cursorFormat.bytesPerPixel << 3)) - 1; + + if (_cursorFormat.bytesPerPixel < 4) + assert(keycolor < (uint)(1 << (_cursorFormat.bytesPerPixel << 3))); #else - keycolor &= 0xFF; + assert(keycolor <= 0xFF); #endif if (w == 0 || h == 0) return; + + + /* Residual doesn't support this _mouseCurState.hotX = hotspot_x; _mouseCurState.hotY = hotspot_y; @@ -439,6 +444,22 @@ void OSystem_SDL::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, _cursorTargetScale = cursorTargetScale; + _mouseTextureId = new GLuint[1]; + glGenTextures(1, _mouseTextureId); + glBindTexture(GL_TEXTURE_2D, _overlayTexIds[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, BITMAP_TEXTURE_SIZE, BITMAP_TEXTURE_SIZE, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 2); + glPixelStorei(GL_UNPACK_ROW_LENGTH, _overlayWidth); + glBindTexture(GL_TEXTURE_2D, _overlayTexIds[curTexIdx]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _mouseWidth, _mouseHeight, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (byte *)buf + (y * 2 * _mouseWidth) + (2 * x)); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + if (_mouseCurState.w != (int)w || _mouseCurState.h != (int)h) { _mouseCurState.w = w; _mouseCurState.h = h; @@ -446,10 +467,9 @@ void OSystem_SDL::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, if (_mouseOrigSurface) SDL_FreeSurface(_mouseOrigSurface); - // Allocate bigger surface because AdvMame2x adds black pixel at [0,0] _mouseOrigSurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_RLEACCEL | SDL_SRCCOLORKEY | SDL_SRCALPHA, - _mouseCurState.w + 2, - _mouseCurState.h + 2, + _mouseCurState.w, + _mouseCurState.h, 16, _hwscreen->format->Rmask, _hwscreen->format->Gmask, diff --git a/backends/platform/sdl/main.cpp b/backends/platform/sdl/main.cpp index d81f813964f..85ca9ca6637 100644 --- a/backends/platform/sdl/main.cpp +++ b/backends/platform/sdl/main.cpp @@ -23,23 +23,28 @@ * */ +// Fix for bug #2895217 "MSVC compilation broken with r47595": +// We need to keep this on top of the "common/scummsys.h" include, +// otherwise we will get errors about the windows headers redefining +// "ARRAYSIZE" for example. #if defined(WIN32) && !defined(__SYMBIAN32__) #include // winnt.h defines ARRAYSIZE, but we want our own one... #undef ARRAYSIZE #endif +#include "common/sys.h" + +// Several SDL based ports use a custom main, and hence do not want to compile +// of this file. The following "#if" ensures that. +#if !defined(__MAEMO__) && !defined(_WIN32_WCE) && !defined(GP2XWIZ)&& !defined(LINUXMOTO) && !defined(__SYMBIAN32__) + + #include "backends/platform/sdl/sdl.h" #include "backends/plugins/sdl/sdl-provider.h" #include "base/main.h" -#if defined(__SYMBIAN32__) -#include "SymbianOs.h" -#endif - -#if !defined(__MAEMO__) && !defined(_WIN32_WCE) && !defined(GP2XWIZ)&& !defined(LINUXMOTO) - -#if defined (WIN32) +#ifdef WIN32 int __stdcall WinMain(HINSTANCE /*hInst*/, HINSTANCE /*hPrevInst*/, LPSTR /*lpCmdLine*/, int /*iShowCmd*/) { SDL_SetModuleHandle(GetModuleHandle(NULL)); return main(__argc, __argv); @@ -48,56 +53,8 @@ int __stdcall WinMain(HINSTANCE /*hInst*/, HINSTANCE /*hPrevInst*/, LPSTR /*lpC int main(int argc, char *argv[]) { -#if defined(__SYMBIAN32__) - // - // Set up redirects for stdout/stderr under Windows and Symbian. - // Code copied from SDL_main. - // - - // Symbian does not like any output to the console through any *print* function - char STDOUT_FILE[256], STDERR_FILE[256]; // shhh, don't tell anybody :) - strcpy(STDOUT_FILE, Symbian::GetExecutablePath()); - strcpy(STDERR_FILE, Symbian::GetExecutablePath()); - strcat(STDOUT_FILE, "residual.stdout.txt"); - strcat(STDERR_FILE, "residual.stderr.txt"); - - /* Flush the output in case anything is queued */ - fclose(stdout); - fclose(stderr); - - /* Redirect standard input and standard output */ - FILE *newfp = freopen(STDOUT_FILE, "w", stdout); - if (newfp == NULL) { /* This happens on NT */ -#if !defined(stdout) - stdout = fopen(STDOUT_FILE, "w"); -#else - newfp = fopen(STDOUT_FILE, "w"); - if (newfp) { - *stdout = *newfp; - } -#endif - } - newfp = freopen(STDERR_FILE, "w", stderr); - if (newfp == NULL) { /* This happens on NT */ -#if !defined(stderr) - stderr = fopen(STDERR_FILE, "w"); -#else - newfp = fopen(STDERR_FILE, "w"); - if (newfp) { - *stderr = *newfp; - } -#endif - } - setbuf(stderr, NULL); /* No buffering */ - -#endif // defined(__SYMBIAN32__) - // Create our OSystem instance -#if defined(__SYMBIAN32__) - g_system = new OSystem_SDL_Symbian(); -#else g_system = new OSystem_SDL(); -#endif assert(g_system); #ifdef DYNAMIC_MODULES diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index 575c5cb5e69..7097642ac47 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -68,6 +68,8 @@ #if defined(UNIX) #ifdef MACOSX #define DEFAULT_CONFIG_FILE "Library/Preferences/Residual Preferences" +#elif defined(SAMSUNGTV) +#define DEFAULT_CONFIG_FILE "/dtv/usb/sda1/.residualrc" #else #define DEFAULT_CONFIG_FILE ".residualrc" #endif @@ -177,7 +179,7 @@ OSystem_SDL::OSystem_SDL() _cdrom(0), _joystick(0), -#ifdef MIXER_DOUBLE_BUFFERING +#if MIXER_DOUBLE_BUFFERING _soundMutex(0), _soundCond(0), _soundThread(0), _soundThreadIsRunning(false), _soundThreadShouldQuit(false), #endif @@ -223,9 +225,15 @@ void OSystem_SDL::delayMillis(uint msecs) { SDL_Delay(msecs); } -void OSystem_SDL::getTimeAndDate(struct tm &t) const { +void OSystem_SDL::getTimeAndDate(TimeDate &td) const { time_t curTime = time(0); - t = *localtime(&curTime); + struct tm t = *localtime(&curTime); + td.tm_sec = t.tm_sec; + td.tm_min = t.tm_min; + td.tm_hour = t.tm_hour; + td.tm_mday = t.tm_mday; + td.tm_mon = t.tm_mon; + td.tm_year = t.tm_year; } Common::TimerManager *OSystem_SDL::getTimerManager() { @@ -398,6 +406,7 @@ void OSystem_SDL::quit() { } if (_joystick) SDL_JoystickClose(_joystick); + SDL_ShowCursor(SDL_ENABLE); SDL_RemoveTimer(_timerID); @@ -412,6 +421,10 @@ void OSystem_SDL::quit() { // recorded events delete getEventManager(); delete _savefile; + +#if !defined(SAMSUNGTV) + exit(0); +#endif } void OSystem_SDL::setupIcon() { @@ -466,7 +479,7 @@ void OSystem_SDL::setupIcon() { free(icon); } -OSystem::MutexRef OSystem_SDL::createMutex(void) { +OSystem::MutexRef OSystem_SDL::createMutex() { return (MutexRef) SDL_CreateMutex(); } @@ -486,7 +499,7 @@ void OSystem_SDL::deleteMutex(MutexRef mutex) { #pragma mark --- Audio --- #pragma mark - -#ifdef MIXER_DOUBLE_BUFFERING +#if MIXER_DOUBLE_BUFFERING void OSystem_SDL::mixerProducerThread() { byte nextSoundBuffer; @@ -636,7 +649,7 @@ void OSystem_SDL::setupMixer() { _mixer->setOutputRate(_samplesPerSec); _mixer->setReady(true); -#ifdef MIXER_DOUBLE_BUFFERING +#if MIXER_DOUBLE_BUFFERING initThreadedMixer(_mixer, _obtainedRate.samples * 4); #endif @@ -654,7 +667,7 @@ void OSystem_SDL::closeMixer() { delete _mixer; _mixer = 0; -#ifdef MIXER_DOUBLE_BUFFERING +#if MIXER_DOUBLE_BUFFERING deinitThreadedMixer(); #endif diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h index db5d27f1e23..59de898430a 100644 --- a/backends/platform/sdl/sdl.h +++ b/backends/platform/sdl/sdl.h @@ -89,6 +89,25 @@ public: // Returns true if an event was retrieved. virtual bool pollEvent(Common::Event &event); // overloaded by CE backend +protected: + virtual bool dispatchSDLEvent(SDL_Event &ev, Common::Event &event); + + // Handlers for specific SDL events, called by pollEvent. + // This way, if a backend inherits fromt the SDL backend, it can + // change the behavior of only a single event, without having to override all + // of pollEvent. + virtual bool handleKeyDown(SDL_Event &ev, Common::Event &event); + virtual bool handleKeyUp(SDL_Event &ev, Common::Event &event); + virtual bool handleMouseMotion(SDL_Event &ev, Common::Event &event); + virtual bool handleMouseButtonDown(SDL_Event &ev, Common::Event &event); + virtual bool handleMouseButtonUp(SDL_Event &ev, Common::Event &event); + virtual bool handleJoyButtonDown(SDL_Event &ev, Common::Event &event); + virtual bool handleJoyButtonUp(SDL_Event &ev, Common::Event &event); + virtual bool handleJoyAxisMotion(SDL_Event &ev, Common::Event &event); + +public: + + // Define all hardware keys for keymapper virtual Common::HardwareKeySet *getHardwareKeySet(); @@ -116,7 +135,7 @@ public: // Quit virtual void quit(); // overloaded by CE backend - virtual void getTimeAndDate(struct tm &t) const; + virtual void getTimeAndDate(TimeDate &t) const; virtual Common::TimerManager *getTimerManager(); // Mutex handling @@ -232,7 +251,7 @@ protected: virtual bool remapKey(SDL_Event &ev, Common::Event &event); - void handleScalerHotkeys(const SDL_KeyboardEvent &key); + bool handleScalerHotkeys(const SDL_KeyboardEvent &key); }; #endif diff --git a/backends/saves/posix/posix-saves.cpp b/backends/saves/posix/posix-saves.cpp index eefb9a8523d..3fe32180201 100644 --- a/backends/saves/posix/posix-saves.cpp +++ b/backends/saves/posix/posix-saves.cpp @@ -44,6 +44,9 @@ POSIXSaveFileManager::POSIXSaveFileManager() { // Register default savepath based on HOME +#if defined(SAMSUNGTV) + ConfMan.registerDefault("savepath", "/dtv/usb/sda1/.residual"); +#else Common::String savePath; const char *home = getenv("HOME"); if (home && *home && strlen(home) < MAXPATHLEN) { @@ -51,6 +54,7 @@ POSIXSaveFileManager::POSIXSaveFileManager() { savePath += "/" DEFAULT_SAVE_PATH; ConfMan.registerDefault("savepath", savePath); } +#endif } /* POSIXSaveFileManager::POSIXSaveFileManager(const Common::String &defaultSavepath) diff --git a/backends/vkeybd/polygon.cpp b/backends/vkeybd/polygon.cpp index 7afe91cd4c4..2b099edc10e 100644 --- a/backends/vkeybd/polygon.cpp +++ b/backends/vkeybd/polygon.cpp @@ -54,6 +54,6 @@ bool Polygon::contains(int16 x, int16 y) const { return inside_flag; } -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_VKEYBD diff --git a/backends/vkeybd/polygon.h b/backends/vkeybd/polygon.h index da122d9c823..3916852d5fa 100644 --- a/backends/vkeybd/polygon.h +++ b/backends/vkeybd/polygon.h @@ -107,7 +107,7 @@ private: Rect _bound; }; -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_VKEYBD diff --git a/backends/vkeybd/virtual-keyboard-gui.cpp b/backends/vkeybd/virtual-keyboard-gui.cpp index 8d378374b3e..694e2dcdc0c 100644 --- a/backends/vkeybd/virtual-keyboard-gui.cpp +++ b/backends/vkeybd/virtual-keyboard-gui.cpp @@ -443,7 +443,7 @@ void VirtualKeyboardGUI::setupCursor() { }; CursorMan.pushCursorPalette(palette, 0, 4); - CursorMan.pushCursor(NULL, 0, 0, 0, 0); + CursorMan.pushCursor(NULL, 0, 0, 0, 0, 0); CursorMan.showMouse(true); } @@ -457,7 +457,7 @@ void VirtualKeyboardGUI::animateCursor() { } } - CursorMan.replaceCursor(_cursor, 16, 16, 7, 7); + CursorMan.replaceCursor(_cursor, 16, 16, 7, 7, 255); _cursorAnimateTimer = time; _cursorAnimateCounter = (_cursorAnimateCounter + 1) % 4; @@ -469,6 +469,6 @@ void VirtualKeyboardGUI::removeCursor() { CursorMan.popCursorPalette(); } -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_VKEYBD diff --git a/backends/vkeybd/virtual-keyboard-gui.h b/backends/vkeybd/virtual-keyboard-gui.h index 46574be759a..4435a496108 100644 --- a/backends/vkeybd/virtual-keyboard-gui.h +++ b/backends/vkeybd/virtual-keyboard-gui.h @@ -151,7 +151,7 @@ private: }; -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_VKEYBD diff --git a/backends/vkeybd/virtual-keyboard-parser.cpp b/backends/vkeybd/virtual-keyboard-parser.cpp index cabf98818cb..5d2c48f8ea3 100644 --- a/backends/vkeybd/virtual-keyboard-parser.cpp +++ b/backends/vkeybd/virtual-keyboard-parser.cpp @@ -154,8 +154,11 @@ bool VirtualKeyboardParser::parserCallback_mode(ParserNode *node) { } else { // remove data relating to old resolution _mode->bitmapName.clear(); - delete _mode->image; - _mode->image = 0; + if (_mode->image) { + _mode->image->free(); + delete _mode->image; + _mode->image = 0; + } _mode->imageMap.removeAllAreas(); _mode->displayArea = Rect(); } @@ -371,6 +374,6 @@ bool VirtualKeyboardParser::parseRectAsPolygon(Polygon &poly, const String& coor return true; } -} // end of namespace GUI +} // End of namespace GUI #endif // #ifdef ENABLE_VKEYBD diff --git a/backends/vkeybd/virtual-keyboard-parser.h b/backends/vkeybd/virtual-keyboard-parser.h index acd2a01f4b8..f8ab14806b6 100644 --- a/backends/vkeybd/virtual-keyboard-parser.h +++ b/backends/vkeybd/virtual-keyboard-parser.h @@ -265,7 +265,7 @@ protected: bool parseRectAsPolygon(Polygon &poly, const String& coords); }; -} // end of namespace GUI +} // End of namespace GUI #endif // #ifdef ENABLE_VKEYBD diff --git a/backends/vkeybd/virtual-keyboard.cpp b/backends/vkeybd/virtual-keyboard.cpp index 095e8d650f6..6ddc357d20c 100644 --- a/backends/vkeybd/virtual-keyboard.cpp +++ b/backends/vkeybd/virtual-keyboard.cpp @@ -416,7 +416,7 @@ bool VirtualKeyboard::KeyPressQueue::hasStringChanged() { return ret; } -} // end of namespace Common +} // End of namespace Common #endif // #ifdef ENABLE_VKEYBD diff --git a/backends/vkeybd/virtual-keyboard.h b/backends/vkeybd/virtual-keyboard.h index fea6f76cf40..0cd17d9a83b 100644 --- a/backends/vkeybd/virtual-keyboard.h +++ b/backends/vkeybd/virtual-keyboard.h @@ -121,7 +121,13 @@ protected: OverlayColor displayFontColor; Mode() : image(0) {} - ~Mode() { delete image; } + ~Mode() { + if (image) { + image->free(); + delete image; + image = 0; + } + } }; typedef HashMap ModeMap; diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 7f60fd2dc0f..85aa8c9674b 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -32,6 +32,8 @@ #include "common/system.h" #include "common/fs.h" +#include "sound/mididrv.h" + #include "gui/ThemeEngine.h" #define DETECTOR_TESTING_HACK @@ -69,6 +71,7 @@ static const char HELP_STRING[] = " --gui-theme=THEME Select GUI theme\n" " --themepath=PATH Path to where GUI themes are stored\n" " --list-themes Display list of all usable GUI themes\n" + " -e, --music-driver=MODE Select music driver\n" " -m, --music-volume=NUM Set the music volume, 0-127 (default: 127)\n" " -s, --sfx-volume=NUM Set the sfx volume, 0-127 (default: 127)\n" " -r, --speech-volume=NUM Set the speech volume, 0-127 (default: 127)\n" @@ -78,8 +81,15 @@ static const char HELP_STRING[] = " --debugflags=FLAGS Enables engine specific debug flags\n" " --savepath=PATH Path to where savegames are stored\n" " --extrapath=PATH Extra path to additional game data\n" + " --soundfont=FILE Select the SoundFont for MIDI playback (only\n" + " supported by some MIDI drivers)\n" + " --multi-midi Enable combination AdLib and native MIDI\n" + " --native-mt32 True Roland MT-32 (disable GM emulation)\n" + " --enable-gs Enable Roland GS mode for MIDI playback\n" " --output-rate=RATE Select output sample rate in Hz (e.g. 22050)\n" + " --opl-driver=DRIVER Select AdLib (OPL) emulator (db, mame)\n" " --show-fps=BOOL Set the turn on/off display FPS info: true/false\n" + "\n" ; #endif @@ -115,6 +125,11 @@ void registerDefaults() { ConfMan.registerDefault("speech_volume", 127); ConfMan.registerDefault("speech_mode", "3"); + ConfMan.registerDefault("multi_midi", false); + ConfMan.registerDefault("native_mt32", false); + ConfMan.registerDefault("enable_gs", false); + ConfMan.registerDefault("midi_gain", 100); + ConfMan.registerDefault("path", "."); ConfMan.registerDefault("soft_renderer", "true"); @@ -260,12 +275,20 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha DO_LONG_OPTION("debugflags") END_OPTION + DO_OPTION('e', "music-driver") + if (MidiDriver::findMusicDriver(option) == 0) + usage("Unrecognized music driver '%s'", option); + END_OPTION + DO_LONG_OPTION_INT("output-rate") END_OPTION DO_OPTION_BOOL('f', "fullscreen") END_OPTION + DO_LONG_OPTION("opl-driver") + END_OPTION + DO_OPTION_INT('m', "music-volume") END_OPTION @@ -289,6 +312,9 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha DO_OPTION_INT('r', "speech-volume") END_OPTION + DO_LONG_OPTION_INT("midi-gain") + END_OPTION + DO_LONG_OPTION_INT("cdrom") END_OPTION @@ -303,12 +329,33 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha DO_LONG_OPTION("soft-renderer") END_OPTION + DO_LONG_OPTION("soundfont") + Common::FSNode path(option); + if (!path.exists()) { + usage("Non-existent soundfont path '%s'", option); + } else if (!path.isReadable()) { + usage("Non-readable soundfont path '%s'", option); + } + END_OPTION + DO_LONG_OPTION_BOOL("disable-sdl-parachute") END_OPTION DO_LONG_OPTION("engine-speed") END_OPTION + DO_LONG_OPTION_BOOL("multi-midi") + END_OPTION + + DO_LONG_OPTION_BOOL("native-mt32") + END_OPTION + + DO_LONG_OPTION_BOOL("enable-gs") + END_OPTION + + DO_LONG_OPTION_BOOL("aspect-ratio") + END_OPTION + DO_LONG_OPTION("gamma") END_OPTION diff --git a/base/plugins.cpp b/base/plugins.cpp index 93f5e5a85f3..3b2a461943e 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -37,6 +37,7 @@ int pluginTypeVersions[PLUGIN_TYPE_MAX] = { PLUGIN_TYPE_ENGINE_VERSION, + PLUGIN_TYPE_MUSIC_VERSION, }; @@ -297,3 +298,14 @@ GameList EngineManager::detectGames(const Common::FSList &fslist) const { const EnginePlugin::List &EngineManager::getPlugins() const { return (const EnginePlugin::List &)PluginManager::instance().getPlugins(PLUGIN_TYPE_ENGINE); } + + +// Music plugins + +#include "sound/musicplugin.h" + +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 4bb1009574a..b6d978fd2e4 100644 --- a/base/plugins.h +++ b/base/plugins.h @@ -64,6 +64,7 @@ namespace Common { enum PluginType { PLUGIN_TYPE_ENGINE = 0, + PLUGIN_TYPE_MUSIC, PLUGIN_TYPE_MAX }; @@ -71,6 +72,7 @@ enum PluginType { // TODO: Make the engine API version depend on ScummVM's version // because of the backlinking (posibly from the SVN revision) #define PLUGIN_TYPE_ENGINE_VERSION 1 +#define PLUGIN_TYPE_MUSIC_VERSION 1 extern int pluginTypeVersions[PLUGIN_TYPE_MAX]; diff --git a/base/version.cpp b/base/version.cpp index 2166222b8b2..ee25ef1bf98 100644 --- a/base/version.cpp +++ b/base/version.cpp @@ -93,5 +93,9 @@ const char *gResidualFeatures = "" #ifdef USE_ZLIB "zLib " #endif + +#ifdef USE_FLUIDSYNTH + "FluidSynth " +#endif ; diff --git a/common/EventDispatcher.cpp b/common/EventDispatcher.cpp index 3b2ec252903..8adb4304daa 100644 --- a/common/EventDispatcher.cpp +++ b/common/EventDispatcher.cpp @@ -72,8 +72,7 @@ void EventDispatcher::dispatch() { } void EventDispatcher::registerMapper(EventMapper *mapper) { - if (_mapper) - delete _mapper; + delete _mapper; _mapper = mapper; } @@ -134,5 +133,5 @@ void EventDispatcher::dispatchEvent(const Event &event) { } } -} // end of namespace Common +} // End of namespace Common diff --git a/common/EventRecorder.cpp b/common/EventRecorder.cpp index c0fc35e6733..ca73143c09a 100644 --- a/common/EventRecorder.cpp +++ b/common/EventRecorder.cpp @@ -203,12 +203,8 @@ void EventRecorder::deinit() { g_system->unlockMutex(_timeMutex); g_system->unlockMutex(_recorderMutex); - if (_playbackFile != NULL) { - delete _playbackFile; - } - if (_playbackTimeFile != NULL) { - delete _playbackTimeFile; - } + delete _playbackFile; + delete _playbackTimeFile; if (_recordFile != NULL) { _recordFile->finalize(); @@ -363,5 +359,5 @@ bool EventRecorder::pollEvent(Common::Event &ev) { return false; } -} // end of namespace Common +} // End of namespace Common diff --git a/common/algorithm.h b/common/algorithm.h index 1aac073f21c..08a7bfb2428 100644 --- a/common/algorithm.h +++ b/common/algorithm.h @@ -26,6 +26,7 @@ #define COMMON_ALGORITHM_H #include "common/sys.h" +#include "common/func.h" namespace Common { @@ -145,57 +146,81 @@ Op for_each(In first, In last, Op f) { return f; } -/** - * Simple sort function, modeled after std::sort. - * Use it like this: sort(container.begin(), container.end()). - * Also works on plain old i.e. int arrays etc. For comperance - * operator < is used. - */ -template -void sort(T first, T last) { - if (first == last) - return; +template +unsigned int distance(T *first, T *last) { + return last - first; +} - // Simple selection sort - T i(first); - for (; i != last; ++i) { - T minElem(i); - T j(i); - ++j; - for (; j != last; ++j) - if (*j < *minElem) - minElem = j; - if (minElem != i) - SWAP(*minElem, *i); +template +unsigned int distance(T first, T last) { + unsigned int n = 0; + while (first != last) { + ++n; + ++first; } + return n; +} + +template +T *sortChoosePivot(T *first, T *last) { + return first + distance(first, last) / 2; +} + +template +T sortChoosePivot(T first, T last) { + unsigned int n = distance(first, last); + n /= 2; + while (n--) + ++first; + return first; +} + +template +T sortPartition(T first, T last, T pivot, StrictWeakOrdering &comp) { + --last; + SWAP(*pivot, *last); + + T sorted; + for (sorted = first; first != last; ++first) { + if (!comp(*last, *first)) { + if (first != sorted) + SWAP(*first, *sorted); + ++sorted; + } + } + + SWAP(*last, *sorted); + return sorted; } /** * Simple sort function, modeled after std::sort. * It compares data with the given comparator object comp. - * - * Note: Using this with: Common::Less from common/func.h - * will give the same results as the plain sort function. */ -template +template void sort(T first, T last, StrictWeakOrdering comp) { if (first == last) return; - // Simple selection sort - T i(first); - for (; i != last; ++i) { - T minElem(i); - T j(i); - ++j; - for (; j != last; ++j) - if (comp(*j, *minElem)) - minElem = j; - if (minElem != i) - SWAP(*minElem, *i); - } + T pivot = sortChoosePivot(first, last); + pivot = sortPartition(first, last, pivot, comp); + sort(first, pivot, comp); + sort(++pivot, last, comp); } -} // end of namespace Common +/** + * Simple sort function, modeled after std::sort. + */ +template +void sort(T *first, T *last) { + sort(first, last, Common::Less()); +} + +template +void sort(T first, T last) { + sort(first, last, Common::Less()); +} + +} // End of namespace Common #endif diff --git a/common/archive.cpp b/common/archive.cpp index a69d3ea54d1..0acb3db96fe 100644 --- a/common/archive.cpp +++ b/common/archive.cpp @@ -50,13 +50,11 @@ int Archive::listMatchingMembers(ArchiveMemberList &list, const String &pattern) int matches = 0; - // need to match lowercase key - String lowercasePattern = pattern; - lowercasePattern.toLowercase(); - ArchiveMemberList::iterator it = allNames.begin(); for ( ; it != allNames.end(); ++it) { - if ((*it)->getName().matchString(lowercasePattern, false, true)) { + // TODO: We match case-insenstivie for now, our API does not define whether that's ok or not though... + // For our use case case-insensitive is probably what we want to have though. + if ((*it)->getName().matchString(pattern, true, true)) { list.push_back(*it); matches++; } diff --git a/common/array.h b/common/array.h index fbdd6ea4da9..eb2d3c42087 100644 --- a/common/array.h +++ b/common/array.h @@ -199,6 +199,21 @@ public: return (_size == 0); } + bool operator==(const Array &other) const { + if (this == &other) + return true; + if (_size != other._size) + return false; + for (uint i = 0; i < _size; ++i) { + if (_storage[i] != other._storage[i]) + return false; + } + return true; + } + bool operator!=(const Array &other) const { + return !(*this == other); + } + iterator begin() { return _storage; diff --git a/common/config-file.cpp b/common/config-file.cpp index cb6109cdd70..4c707e74cbb 100644 --- a/common/config-file.cpp +++ b/common/config-file.cpp @@ -98,7 +98,8 @@ bool ConfigFile::loadFromStream(SeekableReadStream &stream) { } else if (line[0] == '#') { // Accumulate comments here. Once we encounter either the start // of a new section, or a key-value-pair, we associate the value - // of the 'comment' variable with that entity. + // of the 'comment' variable with that entity. The semicolon + // comment is used for Living Books games in Mohawk. comment += line; #ifdef _WIN32 comment += "\r\n"; @@ -124,8 +125,9 @@ bool ConfigFile::loadFromStream(SeekableReadStream &stream) { const char *p = line.c_str() + 1; // Get the section 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 == '_')) + // periods, dashes and underscores). Mohawk Living Books games + // can have periods in their section names. + while (*p && (isalnum(*p) || *p == '-' || *p == '_' || *p == '.')) p++; if (*p == '\0') diff --git a/common/config-manager.cpp b/common/config-manager.cpp index 4b432f2dbf3..93c39008e51 100644 --- a/common/config-manager.cpp +++ b/common/config-manager.cpp @@ -40,17 +40,6 @@ static bool isValidDomainName(const Common::String &domName) { namespace Common { -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) - -const String ConfigManager::kApplicationDomain("residual"); -const String ConfigManager::kTransientDomain("__TRANSIENT"); - -#ifdef ENABLE_KEYMAPPER -const String ConfigManager::kKeymapperDomain("keymapper"); -#endif - -#else - const char *ConfigManager::kApplicationDomain = "residual"; const char *ConfigManager::kTransientDomain = "__TRANSIENT"; @@ -58,13 +47,10 @@ const char *ConfigManager::kTransientDomain = "__TRANSIENT"; const char *ConfigManager::kKeymapperDomain = "keymapper"; #endif -#endif - #pragma mark - -ConfigManager::ConfigManager() - : _activeDomain(0) { +ConfigManager::ConfigManager() : _activeDomain(0) { } @@ -426,11 +412,7 @@ const String & ConfigManager::get(const String &key) const { else if (_defaultsDomain.contains(key)) return _defaultsDomain[key]; -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) - return String::emptyString; -#else - return ConfMan._emptyString; -#endif + return _emptyString; } const String & ConfigManager::get(const String &key, const String &domName) const { @@ -453,11 +435,7 @@ const String & ConfigManager::get(const String &key, const String &domName) cons if (!domain->contains(key)) { #if 1 -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) - return String::emptyString; -#else - return ConfMan._emptyString; -#endif + return _emptyString; #else error("ConfigManager::get(%s,%s) called on non-existent key", key.c_str(), domName.c_str()); @@ -661,11 +639,7 @@ const String &ConfigManager::Domain::get(const String &key) const { if (iter != end()) return iter->_value; -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) - return String::emptyString; -#else return ConfMan._emptyString; -#endif } void ConfigManager::Domain::setDomainComment(const String &comment) { diff --git a/common/config-manager.h b/common/config-manager.h index d2bad54b9ef..06c081ce9cd 100644 --- a/common/config-manager.h +++ b/common/config-manager.h @@ -68,29 +68,15 @@ public: typedef HashMap DomainMap; -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) /** The name of the application domain (normally 'scummvm'). */ - static const String kApplicationDomain; + static const char *kApplicationDomain; /** The transient (pseudo) domain. */ - static const String kTransientDomain; - -#ifdef ENABLE_KEYMAPPER - /** The name of keymapper domain used to store the key maps */ - static const String kKeymapperDomain; -#endif - -#else - static const char *kApplicationDomain; static const char *kTransientDomain; - const String _emptyString; - #ifdef ENABLE_KEYMAPPER /** The name of keymapper domain used to store the key maps */ static const char *kKeymapperDomain; -#endif - #endif void loadDefaultConfigFile(); @@ -179,6 +165,8 @@ private: Domain * _activeDomain; String _filename; + + const String _emptyString; }; } // End of namespace Common diff --git a/common/debug.cpp b/common/debug.cpp index b067c4919a7..35fbcc9e195 100644 --- a/common/debug.cpp +++ b/common/debug.cpp @@ -25,6 +25,7 @@ #include "common/debug.h" #include "common/util.h" #include "common/hashmap.h" +#include "common/hash-str.h" #include "engines/engine.h" @@ -37,7 +38,6 @@ typedef signed long int64; #include "backends/platform/ps2/fileio.h" - #define fprintf ps2_fprintf #define fputs(str, file) ps2_fputs(str, file) #define fflush(a) ps2_fflush(a) #endif @@ -45,14 +45,14 @@ #ifdef __DS__ #include "backends/fs/ds/ds-fs.h" - void std_fprintf(FILE* handle, const char* fmt, ...); - void std_fflush(FILE* handle); - - #define fprintf(file, fmt, ...) do { char str[128]; sprintf(str, fmt, ##__VA_ARGS__); DS::std_fwrite(str, strlen(str), 1, file); } while(0) - #define fputs(str, file) DS::std_fwrite(str, strlen(str), 1, file) - #define fflush(file) DS::std_fflush(file) + #define fputs(str, file) DS::std_fwrite(str, strlen(str), 1, file) + #define fflush(file) DS::std_fflush(file) #endif +// TODO: Move gDebugLevel into namespace Common. +int gDebugLevel = -1; + + namespace Common { @@ -142,11 +142,16 @@ bool isDebugChannelEnabled(const String &name) { } + +static OutputFormatter s_debugOutputFormatter = 0; + +void setDebugOutputFormatter(OutputFormatter f) { + s_debugOutputFormatter = f; +} + } // End of namespace Common -int gDebugLevel = -1; - #ifndef DISABLE_TEXT_CONSOLE static void debugHelper(const char *s, va_list va, bool caret = true) { @@ -156,8 +161,8 @@ static void debugHelper(const char *s, va_list va, bool caret = true) { // Next, give the active engine (if any) a chance to augment the message, // but only if not used from debugN. - if (g_engine && caret) { - g_engine->errorString(in_buf, buf, STRINGBUFLEN); + if (Common::s_debugOutputFormatter) { + (*Common::s_debugOutputFormatter)(buf, in_buf, STRINGBUFLEN); } else { strncpy(buf, in_buf, STRINGBUFLEN); } diff --git a/common/debug.h b/common/debug.h index 373915530c8..ff54382f457 100644 --- a/common/debug.h +++ b/common/debug.h @@ -26,6 +26,7 @@ #define COMMON_DEBUG_H #include "common/sys.h" +#include "common/textconsole.h" #include "common/list.h" #include "common/str.h" @@ -96,6 +97,12 @@ bool isDebugChannelEnabled(uint32 level); bool isDebugChannelEnabled(const String &name); +/** + * Set the output formatter used by debug() and related functions. + */ +void setDebugOutputFormatter(OutputFormatter f); + + } // End of namespace Common diff --git a/common/error.h b/common/error.h index 89fe097c3cf..4bdd1ae3f7c 100644 --- a/common/error.h +++ b/common/error.h @@ -44,26 +44,26 @@ namespace Common { * kPathInvalid, kPathIsInvalid, kInvalidPathError */ enum Error { - kNoError = 0, //!< No error occured - kInvalidPathError, //!< Engine initialization: Invalid game path was passed - kNoGameDataFoundError, //!< Engine initialization: No game data was found in the specified location - kUnsupportedGameidError, //!< Engine initialization: Gameid not supported by this (Meta)Engine - kUnsupportedColorMode, //!< Engine initialization: Engine does not support backend's color mode + kNoError = 0, ///< No error occured + kInvalidPathError, ///< Engine initialization: Invalid game path was passed + kNoGameDataFoundError, ///< Engine initialization: No game data was found in the specified location + kUnsupportedGameidError, ///< Engine initialization: Gameid not supported by this (Meta)Engine + kUnsupportedColorMode, ///< Engine initialization: Engine does not support backend's color mode - kReadPermissionDenied, //!< Unable to read data due to missing read permission - kWritePermissionDenied, //!< Unable to write data due to missing write permission + kReadPermissionDenied, ///< Unable to read data due to missing read permission + kWritePermissionDenied, ///< Unable to write data due to missing write permission // The following three overlap a bit with kInvalidPathError and each other. Which to keep? - kPathDoesNotExist, //!< The specified path does not exist - kPathNotDirectory, //!< The specified path does not point to a directory - kPathNotFile, //!< The specified path does not point to a file + kPathDoesNotExist, ///< The specified path does not exist + kPathNotDirectory, ///< The specified path does not point to a directory + kPathNotFile, ///< The specified path does not point to a file kCreatingFileFailed, - kReadingFailed, //!< Failed creating a (savestate) file - kWritingFailed, //!< Failure to write data -- disk full? + kReadingFailed, ///< Failed creating a (savestate) file + kWritingFailed, ///< Failure to write data -- disk full? - kUnknownError //!< Catch-all error, used if no other error code matches + kUnknownError ///< Catch-all error, used if no other error code matches }; } // End of namespace Common diff --git a/common/events.h b/common/events.h index 2f33c3df2d1..414e1b76cf2 100644 --- a/common/events.h +++ b/common/events.h @@ -198,9 +198,9 @@ public: virtual ~EventObserver() {} /** - * Notifies the source of an incoming event. + * Notifies the observer of an incoming event. * - * An obeser is supposed to eat the event, with returning true, when + * An observer is supposed to eat the event, with returning true, when * it might want prevent other observers from preventing to receive * the event. An usage example here is the keymapper: * If it processes an Event, it should 'eat' it and create a new diff --git a/common/fs.cpp b/common/fs.cpp index 0ec7a02d92d..6b2f0946763 100644 --- a/common/fs.cpp +++ b/common/fs.cpp @@ -314,6 +314,8 @@ int FSDirectory::listMatchingMembers(ArchiveMemberList &list, const String &patt // Cache dir data ensureCached(); + // need to match lowercase key, since all entries in our file cache are + // stored as lowercase. String lowercasePattern(pattern); lowercasePattern.toLowercase(); diff --git a/common/func.h b/common/func.h index 0bfb9dcb8d6..3f11671abd6 100644 --- a/common/func.h +++ b/common/func.h @@ -423,7 +423,7 @@ private: * warning("Unimplemented opcode %d", opcodeNum); * * If you want to see an real world example check the kyra engine. - * Files: engines/kyra/script.cpp and .h and engine/kyra/script_*.cpp + * Files: engines/kyra/script.cpp and .h and engines/kyra/script_*.cpp * are interesting for that matter. */ template diff --git a/common/list_intern.h b/common/list_intern.h index b1000fc1c4d..94c90dd7516 100644 --- a/common/list_intern.h +++ b/common/list_intern.h @@ -53,6 +53,7 @@ namespace ListInternal { typedef Node * NodePtr; typedef T & ValueRef; typedef T * ValuePtr; + typedef T ValueType; NodeBase *_node; diff --git a/common/md5.cpp b/common/md5.cpp index a6e9fbfd85a..958107eafa6 100644 --- a/common/md5.cpp +++ b/common/md5.cpp @@ -31,20 +31,19 @@ #include "common/file.h" #include "common/fs.h" #include "common/md5.h" -#include "common/util.h" #include "common/endian.h" namespace Common { -typedef struct { +struct md5_context { uint32 total[2]; uint32 state[4]; uint8 buffer[64]; -} md5_context; +}; -void md5_starts(md5_context *ctx); -void md5_update(md5_context *ctx, const uint8 *input, uint32 length); -void md5_finish(md5_context *ctx, uint8 digest[16]); +static void md5_starts(md5_context *ctx); +static void md5_update(md5_context *ctx, const uint8 *input, uint32 length); +static void md5_finish(md5_context *ctx, uint8 digest[16]); #define GET_UINT32(n, b, i) (n) = READ_LE_UINT32(b + i) @@ -246,41 +245,6 @@ void md5_finish(md5_context *ctx, uint8 digest[16]) { PUT_UINT32(ctx->state[3], digest, 12); } -bool md5_file(const FSNode &file, uint8 digest[16], uint32 length) { - if (!file.exists()) { - warning("md5_file: using an inexistent FSNode"); - return false; - } else if (!file.isReadable()) { - warning("md5_file: using an unreadable FSNode"); - return false; - } else if (file.isDirectory()) { - warning("md5_file: using a directory FSNode"); - return false; - } - - ReadStream *stream = file.createReadStream(); - if (!stream) { - warning("md5_file: failed to open '%s'", file.getPath().c_str()); - return false; - } - - bool result = md5_file(*stream, digest, length); - delete stream; - return result; -} - -bool md5_file(const char *name, uint8 digest[16], uint32 length) { - File f; - - f.open(name); - if (!f.isOpen()) { - warning("md5_file couldn't open '%s'", name); - return false; - } - - return md5_file(f, digest, length); -} - bool md5_file(ReadStream &stream, uint8 digest[16], uint32 length) { @@ -316,30 +280,6 @@ bool md5_file(ReadStream &stream, uint8 digest[16], uint32 length) { return true; } -bool md5_file_string(const FSNode &file, char *md5str, uint32 length) { - uint8 digest[16]; - if (!md5_file(file, digest, length)) - return false; - - for (int i = 0; i < 16; i++) { - snprintf(md5str + i*2, 3, "%02x", (int)digest[i]); - } - - return true; -} - -bool md5_file_string(const char *name, char *md5str, uint32 length) { - uint8 digest[16]; - if (!md5_file(name, digest, length)) - return false; - - for (int i = 0; i < 16; i++) { - snprintf(md5str + i*2, 3, "%02x", (int)digest[i]); - } - - return true; -} - bool md5_file_string(ReadStream &stream, char *md5str, uint32 length) { uint8 digest[16]; if (!md5_file(stream, digest, length)) @@ -353,78 +293,3 @@ bool md5_file_string(ReadStream &stream, char *md5str, uint32 length) { } } // End of namespace Common - -#ifdef TEST - -#include -#include - -/* - * those are the standard RFC 1321 test vectors - */ - -static const char *msg[] = { - "", - "a", - "abc", - "message digest", - "abcdefghijklmnopqrstuvwxyz", - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", - "12345678901234567890123456789012345678901234567890123456789012" \ - "345678901234567890" -}; - -static const char *val[] = { - "d41d8cd98f00b204e9800998ecf8427e", - "0cc175b9c0f1b6a831c399e269772661", - "900150983cd24fb0d6963f7d28e17f72", - "f96b697d7cb7938d525a2f31aaf161d0", - "c3fcd3d76192e4007dfb496cca67e13b", - "d174ab98d277d9f5a5611c2c9f419d9f", - "57edf4a22be3c955ac49da2e2107b67a" -}; - -int main(int argc, char *argv[]) { - int i, j; - char output[33]; - md5_context ctx; - unsigned char md5sum[16]; - - if (argc < 2) { - printf("\n MD5 Validation Tests:\n\n"); - - for (i = 0; i < 7; i++) { - printf(" Test %d ", i + 1); - - md5_starts(&ctx); - md5_update(&ctx, (const uint8 *)msg[i], strlen(msg[i])); - md5_finish(&ctx, md5sum); - - for (j = 0; j < 16; j++) { - sprintf(output + j * 2, "%02x", md5sum[j]); - } - - if (memcmp(output, val[i], 32)) { - printf("failed!\n"); - return 1; - } - - printf("passed.\n"); - } - - printf("\n"); - } else { - for (i = 1; i < argc; i++) { - md5_file(argv[i], md5sum); - for (j = 0; j < 16; j++) { - printf("%02x", md5sum[j]); - } - - printf(" %s\n", argv[i]); - } - } - - return 0; -} - -#endif diff --git a/common/md5.h b/common/md5.h index 66068889d20..afa7f39658e 100644 --- a/common/md5.h +++ b/common/md5.h @@ -29,22 +29,16 @@ namespace Common { -class FSNode; class ReadStream; -bool md5_file(const char *name, uint8 digest[16], uint32 length = 0); -bool md5_file(const FSNode &file, uint8 digest[16], uint32 length = 0); bool md5_file(ReadStream &stream, uint8 digest[16], uint32 length = 0); -// The following two methods work similar to the above two, but -// instead of computing the binary MD5 digest, they produce +// The following method work similar to the above one, but +// instead of computing the binary MD5 digest, it produces // a human readable lowercase hexstring representing the digest. // The md5str parameter must point to a buffer of 32+1 chars. -bool md5_file_string(const char *name, char *md5str, uint32 length = 0); -bool md5_file_string(const FSNode &file, char *md5str, uint32 length = 0); bool md5_file_string(ReadStream &stream, char *md5str, uint32 length = 0); - } // End of namespace Common #endif diff --git a/common/memorypool.cpp b/common/memorypool.cpp index 9d8ffaf2451..572cbb48411 100644 --- a/common/memorypool.cpp +++ b/common/memorypool.cpp @@ -32,13 +32,19 @@ enum { INITIAL_CHUNKS_PER_PAGE = 8 }; - -MemoryPool::MemoryPool(size_t chunkSize) { +static size_t adjustChunkSize(size_t chunkSize) { // You must at least fit the pointer in the node (technically unneeded considering the next rounding statement) - _chunkSize = MAX(chunkSize, sizeof(void*)); + chunkSize = MAX(chunkSize, sizeof(void*)); // There might be an alignment problem on some platforms when trying to load a void* on a non natural boundary // so we round to the next sizeof(void*) - _chunkSize = (_chunkSize + sizeof(void*) - 1) & (~(sizeof(void*) - 1)); + chunkSize = (chunkSize + sizeof(void*) - 1) & (~(sizeof(void*) - 1)); + + return chunkSize; +} + + +MemoryPool::MemoryPool(size_t chunkSize) + : _chunkSize(adjustChunkSize(chunkSize)) { _next = NULL; diff --git a/common/memorypool.h b/common/memorypool.h index 37e8efb7760..4e750161908 100644 --- a/common/memorypool.h +++ b/common/memorypool.h @@ -32,6 +32,16 @@ namespace Common { +/** + * This class provides a pool of memory 'chunks' of identical size. + * The size of a chunk is determined when creating the memory pool. + * + * Using a memory pool may yield better performance and memory usage + * when allocating and deallocating many memory blocks of equal size. + * E.g. the Common::String class uses a memory pool for the refCount + * variables (each the size of an int) it allocates for each string + * instance. + */ class MemoryPool { protected: MemoryPool(const MemoryPool&); @@ -42,7 +52,7 @@ protected: size_t numChunks; }; - size_t _chunkSize; + const size_t _chunkSize; Array _pages; void *_next; size_t _chunksPerPage; @@ -52,17 +62,48 @@ protected: bool isPointerInPage(void *ptr, const Page &page); public: + /** + * Constructor for a memory pool with the given chunk size. + * @param chunkSize the chunk size of this memory pool + */ MemoryPool(size_t chunkSize); ~MemoryPool(); + /** + * Allocate a new chunk from the memory pool. + */ void *allocChunk(); + /** + * Return a chunk to the memory pool. The given pointer must have + * been obtained from calling the allocChunk() method of the very + * same MemoryPool instance. Passing any other pointer (e.g. to + * a chunk from another MemoryPool, or a malloc'ed memory block) + * will lead to undefined behavior and may result in a crash (if + * you are lucky) or in silent data corruption. + */ void freeChunk(void *ptr); + /** + * Perform garbage collection. The memory pool stores all the + * chunks it manages in memory 'pages' obtained via the classic + * memory allocation APIs (i.e. malloc/free). Ordinarily, once + * a page has been allocated, it won't be released again during + * the life time of the memory pool. The exception is when this + * method is called. + */ void freeUnusedPages(); + /** + * Return the chunk size used by this memory pool. + */ size_t getChunkSize() const { return _chunkSize; } }; +/** + * This is a memory pool which already contains in itself some storage + * space for a fixed number of chunks. Thus if the memory pool is only + * lightly used, no malloc() calls have to be made at all. + */ template class FixedSizeMemoryPool : public MemoryPool { private: @@ -80,32 +121,39 @@ public: } }; +// Ensure NUM_INTERNAL_CHUNKS == 0 results in a compile error template class FixedSizeMemoryPool : public MemoryPool { public: FixedSizeMemoryPool() : MemoryPool(CHUNK_SIZE) {} }; - +/** + * A memory pool for C++ objects. + */ template class ObjectPool : public FixedSizeMemoryPool { public: + /** + * Return the memory chunk used as storage for the given object back + * to the pool, after calling its destructor. + */ void deleteChunk(T *ptr) { ptr->~T(); - freeChunk(ptr); + this->freeChunk(ptr); } }; } // End of namespace Common -// Provide a custom placement new operator, using an arbitrary -// MemoryPool. -// -// This *should* work with all C++ implementations, but may not. -// -// For details on using placement new for custom allocators, see e.g. -// - +/** + * A custom placement new operator, using an arbitrary MemoryPool. + * + * This *should* work with all C++ implementations, but may not. + * + * For details on using placement new for custom allocators, see e.g. + * + */ inline void* operator new(size_t nbytes, Common::MemoryPool& pool) { assert(nbytes <= pool.getChunkSize()); return pool.allocChunk(); diff --git a/common/pack-end.h b/common/pack-end.h new file mode 100644 index 00000000000..175f0db43d8 --- /dev/null +++ b/common/pack-end.h @@ -0,0 +1,27 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#if defined(SCUMMVM_USE_PRAGMA_PACK) + #pragma pack() +#endif diff --git a/common/pack-start.h b/common/pack-start.h new file mode 100644 index 00000000000..c356635f343 --- /dev/null +++ b/common/pack-start.h @@ -0,0 +1,27 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#if defined(SCUMMVM_USE_PRAGMA_PACK) + #pragma pack(1) +#endif diff --git a/common/rect.h b/common/rect.h index 1037d6d020a..5458e47576c 100644 --- a/common/rect.h +++ b/common/rect.h @@ -36,13 +36,13 @@ namespace Common { * Simple class for handling both 2D position and size. */ struct Point { - int16 x; //!< The horizontal part of the point - int16 y; //!< The vertical part of the point + int16 x; ///< The horizontal part of the point + int16 y; ///< The vertical part of the point Point() : x(0), y(0) {} Point(int16 x1, int16 y1) : x(x1), y(y1) {} - bool operator==(const Point & p) const { return x == p.x && y == p.y; }; - bool operator!=(const Point & p) const { return x != p.x || y != p.y; }; + bool operator==(const Point &p) const { return x == p.x && y == p.y; }; + bool operator!=(const Point &p) const { return x != p.x || y != p.y; }; /** * Return the square of the distance between this point and the point p. @@ -50,7 +50,7 @@ struct Point { * @param p the other point * @return the distance between this and p */ - uint sqrDist(const Point & p) const { + uint sqrDist(const Point &p) const { int diffx = ABS(p.x - x); if (diffx >= 0x1000) return 0xFFFFFF; @@ -59,7 +59,7 @@ struct Point { if (diffy >= 0x1000) return 0xFFFFFF; - return uint(diffx*diffx + diffy*diffy); + return uint(diffx * diffx + diffy * diffy); } }; @@ -82,8 +82,8 @@ struct Point { * When writing code using our Rect class, always keep this principle in mind! */ struct Rect { - int16 top, left; //!< The point at the top left of the rectangle (part of the rect). - int16 bottom, right; //!< The point at the bottom right of the rectangle (not part of the rect). + int16 top, left; ///< The point at the top left of the rectangle (part of the rect). + int16 bottom, right; ///< The point at the bottom right of the rectangle (not part of the rect). Rect() : top(0), left(0), bottom(0), right(0) {} Rect(int16 w, int16 h) : top(0), left(0), bottom(h), right(w) {} @@ -198,7 +198,7 @@ struct Rect { else if (right < r.left) right = r.left; } - void clip(int maxw, int maxh) { + void clip(int16 maxw, int16 maxh) { clip(Rect(0, 0, maxw, maxh)); } diff --git a/common/str.cpp b/common/str.cpp index 87c0ac481ad..140322c4914 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -37,13 +37,6 @@ namespace Common { -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) -const String String::emptyString; -#else -const char *String::emptyString = ""; -#endif - - MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now static uint32 computeCapacity(uint32 len) { @@ -464,6 +457,7 @@ String String::printf(const char *fmt, ...) { len = vsnprintf(output._str, size, fmt, va); va_end(va); } while (len == -1 || len >= size); + output._size = len; } else if (len < (int)_builtinCapacity) { // vsnprintf succeeded output._size = len; diff --git a/common/str.h b/common/str.h index f3dcb133c39..3bb0b07d999 100644 --- a/common/str.h +++ b/common/str.h @@ -90,12 +90,6 @@ protected: } public: -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) - static const String emptyString; -#else - static const char *emptyString; -#endif - /** Construct a new empty string. */ String() : _size(0), _str(_storage) { _storage[0] = 0; } diff --git a/common/stream.cpp b/common/stream.cpp index 27355da624c..969ee6d3e9f 100644 --- a/common/stream.cpp +++ b/common/stream.cpp @@ -37,7 +37,7 @@ MemoryReadStream *ReadStream::readStream(uint32 dataSize) { void *buf = malloc(dataSize); dataSize = read(buf, dataSize); assert(dataSize > 0); - return new MemoryReadStream((byte *)buf, dataSize, true); + return new MemoryReadStream((byte *)buf, dataSize, DisposeAfterUse::YES); } @@ -94,7 +94,7 @@ enum { CR = 0x0D }; -char *SeekableReadStream::readLine_NEW(char *buf, size_t bufSize) { +char *SeekableReadStream::readLine(char *buf, size_t bufSize) { assert(buf != 0 && bufSize > 1); char *p = buf; size_t len = 0; @@ -162,7 +162,7 @@ String SeekableReadStream::readLine() { String line; while (line.lastChar() != '\n') { char buf[256]; - if (!readLine_NEW(buf, 256)) + if (!readLine(buf, 256)) break; line += buf; } @@ -188,7 +188,7 @@ uint32 SubReadStream::read(void *dataPtr, uint32 dataSize) { return dataSize; } -SeekableSubReadStream::SeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, bool disposeParentStream) +SeekableSubReadStream::SeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, DisposeAfterUse::Flag disposeParentStream) : SubReadStream(parentStream, end, disposeParentStream), _parentStream(parentStream), _begin(begin) { @@ -222,7 +222,7 @@ bool SeekableSubReadStream::seek(int32 offset, int whence) { return ret; } -BufferedReadStream::BufferedReadStream(ReadStream *parentStream, uint32 bufSize, bool disposeParentStream) +BufferedReadStream::BufferedReadStream(ReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream) : _parentStream(parentStream), _disposeParentStream(disposeParentStream), _pos(0), @@ -279,7 +279,7 @@ uint32 BufferedReadStream::read(void *dataPtr, uint32 dataSize) { return alreadyRead + dataSize; } -BufferedSeekableReadStream::BufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, bool disposeParentStream) +BufferedSeekableReadStream::BufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream) : BufferedReadStream(parentStream, bufSize, disposeParentStream), _parentStream(parentStream) { } diff --git a/common/stream.h b/common/stream.h index 582681e1090..fdabed596ed 100644 --- a/common/stream.h +++ b/common/stream.h @@ -28,6 +28,7 @@ #include "common/sys.h" #include "common/endian.h" +#include "common/types.h" namespace Common { @@ -176,12 +177,6 @@ public: // The remaining methods all have default implementations; subclasses // in general should not overload them. - /** - * DEPRECATED - * Default implementation for backward compatibility - */ - inline bool ioFailed() { return (eos() || err()); } - /** * Read an unsigned byte from the stream and return it. * Performs no error checking. The return value is undefined @@ -380,7 +375,7 @@ public: * @param bufSize the size of the buffer * @return a pointer to the read string, or NULL if an error occurred */ - virtual char *readLine_NEW(char *s, size_t bufSize); + virtual char *readLine(char *s, size_t bufSize); /** @@ -390,12 +385,13 @@ public: * * Upon successful completion, return a string with the content * of the line, *without* the end of a line marker. This method - * does not indicate whether an error occured. Callers must use + * does not indicate whether an error occurred. Callers must use * err() or eos() to determine whether an exception occurred. */ virtual String readLine(); }; + /** * SubReadStream provides access to a ReadStream restricted to the range * [currentPosition, currentPosition+end). @@ -407,12 +403,12 @@ public: class SubReadStream : virtual public ReadStream { protected: ReadStream *_parentStream; - bool _disposeParentStream; + DisposeAfterUse::Flag _disposeParentStream; uint32 _pos; uint32 _end; bool _eos; public: - SubReadStream(ReadStream *parentStream, uint32 end, bool disposeParentStream = false) + SubReadStream(ReadStream *parentStream, uint32 end, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::NO) : _parentStream(parentStream), _disposeParentStream(disposeParentStream), _pos(0), @@ -421,7 +417,8 @@ public: assert(parentStream); } ~SubReadStream() { - if (_disposeParentStream) delete _parentStream; + if (_disposeParentStream) + delete _parentStream; } virtual bool eos() const { return _eos; } @@ -443,7 +440,7 @@ protected: SeekableReadStream *_parentStream; uint32 _begin; public: - SeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, bool disposeParentStream = false); + SeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::NO); virtual int32 pos() const { return _pos - _begin; } virtual int32 size() const { return _end - _begin; } @@ -463,7 +460,7 @@ private: const bool _bigEndian; public: - SeekableSubReadStreamEndian(SeekableReadStream *parentStream, uint32 begin, uint32 end, bool bigEndian = false, bool disposeParentStream = false) + SeekableSubReadStreamEndian(SeekableReadStream *parentStream, uint32 begin, uint32 end, bool bigEndian = false, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::NO) : SeekableSubReadStream(parentStream, begin, end, disposeParentStream), _bigEndian(bigEndian) { } @@ -496,14 +493,14 @@ public: class BufferedReadStream : virtual public ReadStream { protected: ReadStream *_parentStream; - bool _disposeParentStream; + DisposeAfterUse::Flag _disposeParentStream; byte *_buf; uint32 _pos; uint32 _bufSize; uint32 _realBufSize; public: - BufferedReadStream(ReadStream *parentStream, uint32 bufSize, bool disposeParentStream = false); + BufferedReadStream(ReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::NO); ~BufferedReadStream(); virtual bool eos() const { return (_pos == _bufSize) && _parentStream->eos(); } @@ -521,7 +518,7 @@ class BufferedSeekableReadStream : public BufferedReadStream, public SeekableRea protected: SeekableReadStream *_parentStream; public: - BufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, bool disposeParentStream = false); + BufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::NO); virtual int32 pos() const { return _parentStream->pos() - (_bufSize - _pos); } virtual int32 size() const { return _parentStream->size(); } @@ -542,7 +539,7 @@ private: const uint32 _size; uint32 _pos; byte _encbyte; - bool _disposeMemory; + DisposeAfterUse::Flag _disposeMemory; bool _eos; public: @@ -552,7 +549,7 @@ public: * wraps it. If disposeMemory is true, the MemoryReadStream takes ownership * of the buffer and hence free's it when destructed. */ - MemoryReadStream(const byte *dataPtr, uint32 dataSize, bool disposeMemory = false) : + MemoryReadStream(const byte *dataPtr, uint32 dataSize, DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : _ptrOrig(dataPtr), _ptr(dataPtr), _size(dataSize), @@ -649,7 +646,7 @@ private: byte *_ptr; byte *_data; uint32 _pos; - bool _disposeMemory; + DisposeAfterUse::Flag _disposeMemory; void ensureCapacity(uint32 new_len) { if (new_len <= _capacity) @@ -670,7 +667,7 @@ private: _size = new_len; } public: - MemoryWriteStreamDynamic(bool disposeMemory = false) : _capacity(0), _size(0), _ptr(0), _data(0), _pos(0), _disposeMemory(disposeMemory) {} + MemoryWriteStreamDynamic(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : _capacity(0), _size(0), _ptr(0), _data(0), _pos(0), _disposeMemory(disposeMemory) {} ~MemoryWriteStreamDynamic() { if (_disposeMemory) diff --git a/common/sys.h b/common/sys.h index 2b2f2c02639..680b38bca41 100644 --- a/common/sys.h +++ b/common/sys.h @@ -79,9 +79,6 @@ // - Define this on a big endian target // SYSTEM_NEED_ALIGNMENT // - Define this if your system has problems reading e.g. an int32 from an odd address -// SYSTEM_USE_LONG_INT -// - Define this if your port needs to use 'long' for the int32 datatype -// (i.e. an integer with exactly 32 bits). // SYSTEM_DONT_DEFINE_TYPES // - Define this if you need to provide your own typedefs, e.g. because your // system headers conflict with our typenames, or because you have odd @@ -97,6 +94,20 @@ #endif +// +// By default we try to use pragma push/pop to ensure various structs we use +// are "packed". If your compiler doesn't support this pragma, you are in for +// a problem. If you are lucky, there is a compiler switch, or another pragma, +// doing the same thing -- in that case, try to modify common/pack-begin.h and +// common/pack-end.h accordingly. Or maybe your port simply *always* packs +// everything, in which case you could #undefine SCUMMVM_USE_PRAGMA_PACK. +// +// If neither is possible, tough luck. Try to contact the team, maybe we can +// come up with a solution, though I wouldn't hold my breath on it :-/. +// +#define SYSTEM_USE_PRAGMA_PACK + + #if defined(__SYMBIAN32__) #define SYSTEM_LITTLE_ENDIAN @@ -127,17 +138,17 @@ #define snprintf _snprintf #define SYSTEM_LITTLE_ENDIAN - #define SYSTEM_NEED_ALIGNMENT + + #ifndef __GNUC__ + #define FORCEINLINE __forceinline + #define NORETURN_PRE __declspec(noreturn) + #endif + #define PLUGIN_EXPORT __declspec(dllexport) #if _WIN32_WCE < 300 #define SMALL_SCREEN_DEVICE #endif - typedef signed char int8_t; - typedef signed short int16_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - #elif defined(_MSC_VER) #define strcasecmp stricmp @@ -145,15 +156,10 @@ #define SYSTEM_LITTLE_ENDIAN - typedef signed char int8_t; - typedef signed short int16_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; + #define FORCEINLINE __forceinline + #define NORETURN_PRE __declspec(noreturn) + #define PLUGIN_EXPORT __declspec(dllexport) -// #if !defined(SDL_COMPILEDVERSION) || (SDL_COMPILEDVERSION < 1210) -// typedef signed long int32_t; -// typedef unsigned long uint32_t; -// #endif #elif defined(__MINGW32__) @@ -161,6 +167,8 @@ #define SYSTEM_LITTLE_ENDIAN + #define PLUGIN_EXPORT __declspec(dllexport) + #elif defined(UNIX) #ifndef CONFIG_H @@ -233,6 +241,30 @@ #define SYSTEM_LITTLE_ENDIAN #define SYSTEM_NEED_ALIGNMENT +#elif defined(__N64__) + + #define scumm_stricmp strcasecmp + #define scumm_strnicmp strncasecmp + + #define SCUMM_BIG_ENDIAN + #define SCUMM_NEED_ALIGNMENT + + #define STRINGBUFLEN 256 + + #define SCUMMVM_DONT_DEFINE_TYPES + typedef unsigned char byte; + + typedef unsigned char uint8; + typedef signed char int8; + + typedef unsigned short int uint16; + typedef signed short int int16; + + typedef unsigned int uint32; + typedef signed int int32; + + typedef unsigned long long uint64; + typedef signed long long int64; #elif defined(__PSP__) @@ -268,6 +300,7 @@ #endif + // // GCC specific stuff // @@ -291,8 +324,12 @@ #define PLUGIN_EXPORT #endif -#ifndef NORETURN -#define NORETURN +#ifndef NORETURN_PRE +#define NORETURN_PRE +#endif + +#ifndef NORETURN_POST +#define NORETURN_POST #endif #ifndef STRINGBUFLEN @@ -303,32 +340,20 @@ #define MAXPATHLEN 256 #endif -#ifndef NORETURN -#define NORETURN -#endif // // Typedef our system types unless SYSTEM_DONT_DEFINE_TYPES is set. // #ifndef SYSTEM_DONT_DEFINE_TYPES - typedef unsigned char byte; - typedef unsigned char uint8; typedef signed char int8; - typedef unsigned short uint16; typedef signed short int16; - - #ifdef SYSTEM_USE_LONG_INT - typedef unsigned long uint32; - typedef signed long int32; - typedef unsigned long uint; - #else typedef unsigned int uint32; typedef signed int int32; typedef unsigned int uint; - #endif #endif + #endif // COMMON_SYS_H diff --git a/common/system.h b/common/system.h index 21865e9a02a..703ea25a0aa 100644 --- a/common/system.h +++ b/common/system.h @@ -55,6 +55,24 @@ namespace Common { class FilesystemFactory; +/** + * A structure describing time and date. This is a clone of struct tm + * from time.h. We roll our own since not all systems provide time.h. + * We also do not imitate all files of struct tm, only those we + * actually need. + * + * @note For now, the members are named exactly as in struct tm to ease + * the transition. + */ +struct TimeDate { + int tm_sec; ///< seconds (0 - 60) + int tm_min; ///< minutes (0 - 59) + int tm_hour; ///< hours (0 - 23) + int tm_mday; ///< day of month (1 - 31) + int tm_mon; ///< month of year (0 - 11) + int tm_year; ///< year - 1900 +}; + /** * Interface for Residual backends. */ @@ -183,6 +201,8 @@ public: */ virtual void launcherInitSize(uint width, uint height) = 0; + virtual int getScreenChangeID() const { return 0; } + /** * Set the size of the screen. @@ -193,8 +213,6 @@ public: */ virtual byte *setupScreen(int screenW, int screenH, bool fullscreen, bool accel3d) = 0; - int getScreenChangeID() const { return 0; } - /** * Returns the currently set virtual screen height. * @see initSize @@ -300,7 +318,18 @@ public: */ //@{ - /** Show or hide the mouse cursor. */ + /** + * Show or hide the mouse cursor. + * + * Currently the backend is not required to immediately draw the + * mouse cursor on showMouse(true). + * + * TODO: We might want to reconsider this fact, + * check Graphics::CursorManager::showMouse for some details about + * this. + * + * @see Graphics::CursorManager::showMouse + */ virtual bool showMouse(bool visible) = 0; /** @@ -314,16 +343,18 @@ public: /** * Set the bitmap used for drawing the cursor. * - * @param buf the pixmap data to be used (8bit/pixel) + * @param buf the pixmap data to be used * @param w width of the mouse cursor * @param h height of the mouse cursor * @param hotspotX horizontal offset from the left side to the hotspot * @param hotspotY vertical offset from the top side to the hotspot - * @param keycolor transparency color index + * @param keycolor transparency color value. This should not exceed the maximum color value of the specified format. + * In case it does the behavior is undefined. The backend might just error out or simply ignore the + * value. (The SDL backend will just assert to prevent abuse of this). * @param cursorTargetScale scale factor which cursor is designed for - * @param format pointer to the pixel format which cursor graphic uses + * @param format pointer to the pixel format which cursor graphic uses (0 means CLUT8) */ - virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int cursorTargetScale = 1, const Graphics::PixelFormat *format = NULL) = 0; + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale = 1, const Graphics::PixelFormat *format = NULL) = 0; //@} @@ -343,7 +374,7 @@ public: * Corresponds on many systems to the combination of time() * and localtime(). */ - virtual void getTimeAndDate(struct tm &t) const = 0; + virtual void getTimeAndDate(TimeDate &t) const = 0; /** * Return the timer manager singleton. For more information, refer diff --git a/common/textconsole.cpp b/common/textconsole.cpp new file mode 100644 index 00000000000..5435fc0bd85 --- /dev/null +++ b/common/textconsole.cpp @@ -0,0 +1,144 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/textconsole.h" +#include "common/system.h" + +namespace Common { + +static OutputFormatter s_errorOutputFormatter = 0; + +void setErrorOutputFormatter(OutputFormatter f) { + s_errorOutputFormatter = f; +} + +static ErrorHandler s_errorHandler = 0; + +void setErrorHandler(ErrorHandler handler) { + s_errorHandler = handler; +} + + +} // End of namespace Common + + +#ifndef DISABLE_TEXT_CONSOLE + +void warning(const char *s, ...) { + char buf[STRINGBUFLEN]; + va_list va; + + va_start(va, s); + vsnprintf(buf, STRINGBUFLEN, s, va); + va_end(va); + +#if !defined (__SYMBIAN32__) + fputs("WARNING: ", stderr); + fputs(buf, stderr); + fputs("!\n", stderr); +#endif + +#if defined( USE_WINDBG ) + strcat(buf, "\n"); +#if defined( _WIN32_WCE ) + TCHAR buf_unicode[1024]; + MultiByteToWideChar(CP_ACP, 0, buf, strlen(buf) + 1, buf_unicode, sizeof(buf_unicode)); + OutputDebugString(buf_unicode); +#else + OutputDebugString(buf); +#endif +#endif +} + +#endif + +void NORETURN_PRE error(const char *s, ...) { + char buf_input[STRINGBUFLEN]; + char buf_output[STRINGBUFLEN]; + va_list va; + + // Generate the full error message + va_start(va, s); + vsnprintf(buf_input, STRINGBUFLEN, s, va); + va_end(va); + + + // Next, give the active engine (if any) a chance to augment the message + if (Common::s_errorOutputFormatter) { + (*Common::s_errorOutputFormatter)(buf_output, buf_input, STRINGBUFLEN); + } else { + strncpy(buf_output, buf_input, STRINGBUFLEN); + } + + buf_output[STRINGBUFLEN-3] = '\0'; + buf_output[STRINGBUFLEN-2] = '\0'; + buf_output[STRINGBUFLEN-1] = '\0'; + strcat(buf_output, "!\n"); + + + // Print the error message to stderr + fputs(buf_output, stderr); + + // If there is an error handler, invoke it now + if (Common::s_errorHandler) + (*Common::s_errorHandler)(buf_output); + + // TODO: Add a OSystem::fatalError() method and invoke it here. + // The default implementation would just call OSystem::quit(). + +#if defined( USE_WINDBG ) +#if defined( _WIN32_WCE ) + TCHAR buf_output_unicode[1024]; + MultiByteToWideChar(CP_ACP, 0, buf_output, strlen(buf_output) + 1, buf_output_unicode, sizeof(buf_output_unicode)); + OutputDebugString(buf_output_unicode); +#ifndef DEBUG + drawError(buf_output); +#else + int cmon_break_into_the_debugger_if_you_please = *(int *)(buf_output + 1); // bus error + printf("%d", cmon_break_into_the_debugger_if_you_please); // don't optimize the int out +#endif +#else + OutputDebugString(buf_output); +#endif +#endif + +#ifdef PALMOS_MODE + extern void PalmFatalError(const char *err); + PalmFatalError(buf_output); +#endif + +#ifdef __SYMBIAN32__ + Symbian::FatalError(buf_output); +#endif + // Finally exit. quit() will terminate the program if g_system is present + if (g_system) + g_system->quit(); + +#if defined(SAMSUNGTV) + // FIXME + for (;;) {} +#else + exit(1); +#endif +} diff --git a/common/textconsole.h b/common/textconsole.h new file mode 100644 index 00000000000..6a8feefee3c --- /dev/null +++ b/common/textconsole.h @@ -0,0 +1,85 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef COMMON_CONSOLE_H +#define COMMON_CONSOLE_H + +#include "common/sys.h" + +namespace Common { + +/** + * An output formatter takes a source string and 'decorates' it with + * extra information, storing the result in a destination buffer. + * A typical use is to (optionally) enhance the output given by + * the error() and debug() functions with extra information on + * the state of the active engine. + */ +typedef void (*OutputFormatter)(char *dst, const char *src, size_t dstSize); + +/** + * Set the output formatter used by error(). + */ +void setErrorOutputFormatter(OutputFormatter f); + + +/** + * A callback which is invoked by error() just before aborting. + * A typical example would be a function which shows a debug + * console and displays the given message in it. + */ +typedef void (*ErrorHandler)(const char *msg); + +/** + * Set a callback that is invoked by error() after the error + * message has been printed, but before the application is + * terminated. + * This can be used to e.g. show a debugger console. + */ +void setErrorHandler(ErrorHandler handler); + +} // End of namespace Common + + +void NORETURN_PRE error(const char *s, ...) GCC_PRINTF(1, 2) NORETURN_POST; + +#ifdef DISABLE_TEXT_CONSOLE + +inline int printf(const char *s, ...) { return 0; } + +inline void warning(const char *s, ...) {} + +#else + +/** + * Print a warning message to the text console (stderr). + * Automatically prepends the text "WARNING: " and appends + * an exclamation mark and a newline. + */ +void warning(const char *s, ...) GCC_PRINTF(1, 2); + +#endif + + +#endif diff --git a/common/types.h b/common/types.h new file mode 100644 index 00000000000..32a5d0e8e7c --- /dev/null +++ b/common/types.h @@ -0,0 +1,35 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef COMMON_TYPES_H +#define COMMON_TYPES_H + +#include "common/sys.h" + +namespace DisposeAfterUse { + enum Flag { NO, YES }; +} + + +#endif diff --git a/common/unzip.cpp b/common/unzip.cpp index 37c7553acc7..7f5b1717b06 100644 --- a/common/unzip.cpp +++ b/common/unzip.cpp @@ -1442,7 +1442,7 @@ Common::SeekableReadStream *ZipArchive::createReadStreamForMember(const Common:: assert(buffer); unzReadCurrentFile(_zipFile, buffer, fileInfo.uncompressed_size); unzCloseCurrentFile(_zipFile); - return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size+1, true); + return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size+1, DisposeAfterUse::YES); // FIXME: instead of reading all into a memory stream, we could // instead create a new ZipStream class. But then we have to be diff --git a/common/util.cpp b/common/util.cpp index 01ce4317466..9e5aa70a345 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -25,14 +25,12 @@ #include "common/util.h" #include "common/system.h" #include "common/config-manager.h" -#include "gui/debugger.h" -#include "engines/engine.h" #include // For va_list etc. #ifdef _WIN32_WCE // This is required for the debugger attachment -extern bool isSmartphone(void); +extern bool isSmartphone(); #endif #ifdef __PLAYSTATION2__ @@ -41,23 +39,15 @@ extern bool isSmartphone(void); typedef signed long int64; #include "backends/platform/ps2/fileio.h" - #define fprintf ps2_fprintf #define fputs(str, file) ps2_fputs(str, file) - #define fflush(a) ps2_fflush(a) #endif #ifdef __DS__ #include "backends/fs/ds/ds-fs.h" - void std_fprintf(FILE* handle, const char* fmt, ...); - void std_fflush(FILE* handle); - - #define fprintf(file, fmt, ...) do { char str[128]; sprintf(str, fmt, ##__VA_ARGS__); DS::std_fwrite(str, strlen(str), 1, file); } while(0) - #define fputs(str, file) DS::std_fwrite(str, strlen(str), 1, file) - #define fflush(file) DS::std_fflush(file) + #define fputs(str, file) DS::std_fwrite(str, strlen(str), 1, file) #endif - namespace Common { StringTokenizer::StringTokenizer(const String &str, const String &delimiters) : _str(str), _delimiters(delimiters) { @@ -150,7 +140,7 @@ String tag2string(uint32 tag) { str[4] = '\0'; // Replace non-printable chars by dot for (int i = 0; i < 4; ++i) { - if (!isprint(str[i])) + if (!isprint((unsigned char)str[i])) str[i] = '.'; } return Common::String(str); @@ -178,7 +168,7 @@ uint RandomSource::getRandomNumber(uint max) { return _randSeed % (max + 1); } -uint RandomSource::getRandomBit(void) { +uint RandomSource::getRandomBit() { _randSeed = 0xDEADBF03 * (_randSeed + 1); _randSeed = (_randSeed >> 13) | (_randSeed << 19); return _randSeed & 1; @@ -193,6 +183,7 @@ uint RandomSource::getRandomNumberRng(uint min, uint max) { const LanguageDescription g_languages[] = { + {"zh-cn", "Chinese (China)", ZH_CNA}, {"zh", "Chinese (Taiwan)", ZH_TWN}, {"cz", "Czech", CZ_CZE}, {"nl", "Dutch", NL_NLD}, @@ -203,6 +194,7 @@ const LanguageDescription g_languages[] = { {"de", "German", DE_DEU}, {"gr", "Greek", GR_GRE}, {"hb", "Hebrew", HB_ISR}, + {"hu", "Hungarian", HU_HUN}, {"it", "Italian", IT_ITA}, {"jp", "Japanese", JA_JPN}, {"kr", "Korean", KO_KOR}, @@ -212,7 +204,6 @@ const LanguageDescription g_languages[] = { {"ru", "Russian", RU_RUS}, {"es", "Spanish", ES_ESP}, {"se", "Swedish", SE_SWE}, - {"hu", "Hungarian", HU_HUN}, {0, 0, UNK_LANG} }; @@ -252,7 +243,7 @@ const char *getLanguageDescription(Language id) { const PlatformDescription g_platforms[] = { - {"2gs", "2gs", "2gs", "Apple IIgs", kPlatformApple2GS }, + {"2gs", "2gs", "2gs", "Apple IIgs", kPlatformApple2GS}, {"3do", "3do", "3do", "3DO", kPlatform3DO}, {"acorn", "acorn", "acorn", "Acorn", kPlatformAcorn}, {"amiga", "ami", "amiga", "Amiga", kPlatformAmiga}, @@ -270,12 +261,12 @@ const PlatformDescription g_platforms[] = { {"linux", "linux", "linux", "Linux", kPlatformLinux}, {"macintosh", "mac", "mac", "Macintosh", kPlatformMacintosh}, - {"pce", "pce", "pce", "PC-Engine", kPlatformPCEngine }, + {"pce", "pce", "pce", "PC-Engine", kPlatformPCEngine}, {"nes", "nes", "nes", "NES", kPlatformNES}, {"segacd", "segacd", "sega", "SegaCD", kPlatformSegaCD}, {"windows", "win", "win", "Windows", kPlatformWindows}, {"playstation", "psx", "psx", "Sony PlayStation", kPlatformPSX}, - + {"cdi", "cdi", "cdi", "Phillips CD-i", kPlatformCDi}, {0, 0, 0, "Default", kPlatformUnknown} }; @@ -430,106 +421,3 @@ void updateGameGUIOptions(const uint32 options) { } } // End of namespace Common - - - -#ifndef DISABLE_TEXT_CONSOLE - -void warning(const char *s, ...) { - char buf[STRINGBUFLEN]; - va_list va; - - va_start(va, s); - vsnprintf(buf, STRINGBUFLEN, s, va); - va_end(va); - -#if !defined (__SYMBIAN32__) - fprintf(stderr, "WARNING: %s!\n", buf); -#endif - -#if defined( USE_WINDBG ) - strcat(buf, "\n"); -#if defined( _WIN32_WCE ) - TCHAR buf_unicode[1024]; - MultiByteToWideChar(CP_ACP, 0, buf, strlen(buf) + 1, buf_unicode, sizeof(buf_unicode)); - OutputDebugString(buf_unicode); -#else - OutputDebugString(buf); -#endif -#endif -} - -#endif - -void NORETURN error(const char *s, ...) { - char buf_input[STRINGBUFLEN]; - char buf_output[STRINGBUFLEN]; - va_list va; - - // Generate the full error message - va_start(va, s); - vsnprintf(buf_input, STRINGBUFLEN, s, va); - va_end(va); - - - // Next, give the active engine (if any) a chance to augment the message - if (g_engine) { - g_engine->errorString(buf_input, buf_output, STRINGBUFLEN); - } else { - strncpy(buf_output, buf_input, STRINGBUFLEN); - } - - buf_output[STRINGBUFLEN-3] = '\0'; - buf_output[STRINGBUFLEN-2] = '\0'; - buf_output[STRINGBUFLEN-1] = '\0'; - strcat(buf_output, "!\n"); - - - // Print the error message to stderr - fputs(buf_output, stderr); - - // Unless this error -originated- within the debugger itself, we - // now invoke the debugger, if available / supported. - if (g_engine) { - GUI::Debugger *debugger = g_engine->getDebugger(); -#ifdef _WIN32_WCE - if (isSmartphone()) - debugger = 0; -#endif - if (debugger && !debugger->isAttached()) { - debugger->attach(buf_output); - debugger->onFrame(); - } - } - - -#if defined( USE_WINDBG ) -#if defined( _WIN32_WCE ) - TCHAR buf_output_unicode[1024]; - MultiByteToWideChar(CP_ACP, 0, buf_output, strlen(buf_output) + 1, buf_output_unicode, sizeof(buf_output_unicode)); - OutputDebugString(buf_output_unicode); -#ifndef DEBUG - drawError(buf_output); -#else - int cmon_break_into_the_debugger_if_you_please = *(int *)(buf_output + 1); // bus error - printf("%d", cmon_break_into_the_debugger_if_you_please); // don't optimize the int out -#endif -#else - OutputDebugString(buf_output); -#endif -#endif - -#ifdef PALMOS_MODE - extern void PalmFatalError(const char *err); - PalmFatalError(buf_output); -#endif - -#ifdef __SYMBIAN32__ - Symbian::FatalError(buf_output); -#endif - // Finally exit. quit() will terminate the program if g_system is present - if (g_system) - g_system->quit(); - - exit(1); -} diff --git a/common/util.h b/common/util.h index 593f31f15bb..983c783602b 100644 --- a/common/util.h +++ b/common/util.h @@ -26,11 +26,9 @@ #define COMMON_UTIL_H #include "common/sys.h" +#include "common/textconsole.h" #include "common/str.h" -#if defined(WIN32) -#include -#endif /** * Check whether a given pointer is aligned correctly. @@ -89,15 +87,15 @@ public: * @note Uses space, horizontal tab, carriage return, newline, form feed and vertical tab as delimiters by default. */ StringTokenizer(const String &str, const String &delimiters = " \t\r\n\f\v"); - void reset(); //!< Resets the tokenizer to its initial state - bool empty() const; //!< Returns true if there are no more tokens left in the string, false otherwise - String nextToken(); //!< Returns the next token from the string (Or an empty string if there are no more tokens) + void reset(); ///< Resets the tokenizer to its initial state + bool empty() const; ///< Returns true if there are no more tokens left in the string, false otherwise + String nextToken(); ///< Returns the next token from the string (Or an empty string if there are no more tokens) private: - const String _str; //!< The string to be tokenized - const String _delimiters; //!< String containing all the delimiter characters - uint _tokenBegin; //!< Latest found token's begin (Valid after a call to nextToken(), zero otherwise) - uint _tokenEnd; //!< Latest found token's end (Valid after a call to nextToken(), zero otherwise) + const String _str; ///< The string to be tokenized + const String _delimiters; ///< String containing all the delimiter characters + uint _tokenBegin; ///< Latest found token's begin (Valid after a call to nextToken(), zero otherwise) + uint _tokenEnd; ///< Latest found token's end (Valid after a call to nextToken(), zero otherwise) }; /** @@ -149,7 +147,7 @@ public: * Identical to getRandomNumber(1), but faster, hopefully. * @return a random bit, either 0 or 1 */ - uint getRandomBit(void); + uint getRandomBit(); /** * Generates a random unsigned integer in the interval [min, max]. * @param min the lower bound @@ -163,6 +161,7 @@ public: * List of game language. */ enum Language { + ZH_CNA, ZH_TWN, CZ_CZE, NL_NLD, @@ -173,6 +172,7 @@ enum Language { DE_DEU, GR_GRE, HB_ISR, + HU_HUN, IT_ITA, JA_JPN, KO_KOR, @@ -182,7 +182,6 @@ enum Language { RU_RUS, ES_ESP, SE_SWE, - HU_HUN, UNK_LANG = -1 // Use default language (i.e. none specified) }; @@ -222,11 +221,11 @@ enum Platform { kPlatformSegaCD, kPlatform3DO, kPlatformPCEngine, - kPlatformApple2GS, kPlatformPC98, kPlatformWii, kPlatformPSX, + kPlatformCDi, kPlatformUnknown = -1 }; @@ -303,28 +302,4 @@ void updateGameGUIOptions(const uint32 options); } // End of namespace Common - -#if defined(__GNUC__) -void error(const char *s, ...) GCC_PRINTF(1, 2) NORETURN; -#else -void NORETURN error(const char *s, ...); -#endif - -#ifdef DISABLE_TEXT_CONSOLE - -inline int printf(const char *s, ...) { return 0; } - -inline void warning(const char *s, ...) {} - -#else - -/** - * Print a warning message to the text console (stderr). - * Automatically prepends the text "WARNING: " and appends - * an exclamation mark and a newline. - */ -void warning(const char *s, ...) GCC_PRINTF(1, 2); - -#endif - #endif diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp index 47a72cb780a..438b1069882 100644 --- a/common/xmlparser.cpp +++ b/common/xmlparser.cpp @@ -48,7 +48,7 @@ bool XMLParser::loadFile(const FSNode &node) { return true; } -bool XMLParser::loadBuffer(const byte *buffer, uint32 size, bool disposable) { +bool XMLParser::loadBuffer(const byte *buffer, uint32 size, DisposeAfterUse::Flag disposable) { _stream = new MemoryReadStream(buffer, size, disposable); _fileName = "Memory Stream"; return true; diff --git a/common/xmlparser.h b/common/xmlparser.h index 19f37bb3da4..b27f240d1ac 100644 --- a/common/xmlparser.h +++ b/common/xmlparser.h @@ -97,8 +97,6 @@ class FSNode; * @see XMLParser::keyCallback() */ class XMLParser { - static const int kErrorMessageWidth = 512; - public: /** * Parser constructor. @@ -198,7 +196,7 @@ public: * i.e. if it can be freed safely after it's * no longer needed by the parser. */ - bool loadBuffer(const byte *buffer, uint32 size, bool disposable = false); + bool loadBuffer(const byte *buffer, uint32 size, DisposeAfterUse::Flag disposable = DisposeAfterUse::NO); bool loadStream(Common::SeekableReadStream *stream); diff --git a/config.guess b/config.guess index e792aac6080..64cae2741b9 100755 --- a/config.guess +++ b/config.guess @@ -4,7 +4,7 @@ # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. -timestamp='2009-09-18' +timestamp='2009-12-13' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -57,7 +57,7 @@ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, -2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -333,6 +333,9 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" @@ -807,12 +810,12 @@ EOF i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; - *:Interix*:[3456]*) + *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; - EM64T | authenticamd | genuineintel) + authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) @@ -891,7 +894,15 @@ EOF echo frv-unknown-linux-gnu exit ;; i*86:Linux:*:*) - echo ${UNAME_MACHINE}-pc-linux-gnu + LIBC=gnu + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu @@ -918,11 +929,7 @@ EOF #endif #endif EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^CPU/{ - s: ::g - p - }'`" + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; or32:Linux:*:*) diff --git a/config.sub b/config.sub index 8ca084bf334..110a68e347e 100755 --- a/config.sub +++ b/config.sub @@ -4,7 +4,7 @@ # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. -timestamp='2009-08-19' +timestamp='2009-12-13' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -76,7 +76,7 @@ version="\ GNU config.sub ($timestamp) Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, -2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -287,6 +287,7 @@ case $basic_machine in | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ | pyramid \ + | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ @@ -294,13 +295,14 @@ case $basic_machine in | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu | strongarm \ | tahoe | thumb | tic4x | tic80 | tron \ + | ubicom32 \ | v850 | v850e \ | we32k \ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; - m6811 | m68hc11 | m6812 | m68hc12) + m6811 | m68hc11 | m6812 | m68hc12 | picochip) # Motorola 68HC11/12. basic_machine=$basic_machine-unknown os=-none @@ -371,7 +373,7 @@ case $basic_machine in | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ | pyramid-* \ - | romp-* | rs6000-* \ + | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ @@ -380,6 +382,7 @@ case $basic_machine in | tahoe-* | thumb-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \ | tron-* \ + | ubicom32-* \ | v850-* | v850e-* | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ @@ -1253,6 +1256,9 @@ case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; @@ -1274,8 +1280,8 @@ case $os in # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ - | -kopensolaris* \ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ @@ -1296,7 +1302,7 @@ case $os in | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops*) + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) diff --git a/configure b/configure index a1641f15665..1354f0285a8 100755 --- a/configure +++ b/configure @@ -86,6 +86,13 @@ _vorbis=auto _tremor=auto _flac=auto _mad=auto +_alsa=auto +_fluidsynth=auto +# Default option behaviour yes/no +_debug_build=auto +_release_build=auto +_text_console=no +_mt32emu=yes # Default vkeybd/keymapper options _vkeybd=no _keymapper=no @@ -198,7 +205,7 @@ add_to_config_mk_if_no() { # # Determine sdl-config # -# TODO: small bit of code to test sdl useability +# TODO: small bit of code to test sdl usability find_sdlconfig() { echo_n "Looking for sdl-config... " sdlconfigs="$_sdlconfig:sdl-config:sdl11-config:sdl12-config" @@ -248,7 +255,7 @@ get_system_exe_extension() { gp2x-linux) _exeext=".gp2x" ;; - dreamcast | wii | gamecube | nds | psp) + dreamcast | wii | gamecube | nds | psp | ps2) _exeext=".elf" ;; *) @@ -504,7 +511,7 @@ Usage: $0 [OPTIONS]... Configuration: -h, --help display this help and exit - --backend=BACKEND backend to build (sdl, dc, gp2x, gp2xwiz, iphone, morphos, nds, psp, wii, wince, linuxmoto, null) [sdl] + --backend=BACKEND backend to build (sdl, dc, gp2x, gp2xwiz, iphone, morphos, nds, psp, ps2, wii, wince, linuxmoto, null) [sdl] Installation directories: --prefix=DIR use this prefix for installing Residual [/usr/local] @@ -518,11 +525,12 @@ Special configuration feature: special targets: linupy for Yopy PDA dreamcast for Sega Dreamcast wii for Nintendo Wii - gamecube for Nintendo Gamecube + gamecube for Nintendo GameCube nds for Nintendo DS iphone for Apple iPhone wince for Windows CE psp for PlayStation Portable + ps2 for PlayStation 2 Game engines: --enable-all-engines enable all engines @@ -533,11 +541,16 @@ Optional Features: --enable-Werror treat warnings as errors --enable-plugins enable the support for dynamic plugins --default-dynamic make plugins dynamic by default + --disable-mt32emu don't enable the integrated MT-32 emulator --enable-profiling enable building with gprof profile information --enable-release set flags to build release binary + --enable-text-console use text console instead of graphical console --enable-verbose-build enable regular echoing of commands during build process Optional Libraries: + --with-alsa-prefix=DIR Prefix where alsa is installed (optional) + --disable-alsa disable ALSA midi sound support [autodetect] + --with-ogg-prefix=DIR Prefix where libogg is installed (optional) --with-vorbis-prefix=DIR Prefix where libvorbis is installed (optional) --disable-vorbis disable Ogg Vorbis support [autodetect] @@ -552,6 +565,8 @@ Optional Libraries: --disable-flac disable FLAC support [autodetect] --with-zlib-prefix=DIR Prefix where zlib is installed (optional) + --with-fluidsynth-prefix=DIR Prefix where libfluidsynth is installed (optional) + --disable-fluidsynth disable fluidsynth MIDI driver [autodetect] --with-sdl-prefix=DIR Prefix where the sdl-config script is installed (optional) @@ -574,6 +589,8 @@ DEBFLAGS="-g" for ac_option in $@; do case "$ac_option" in + --enable-alsa) _alsa=yes ;; + --disable-alsa) _alsa=no ;; --enable-vorbis) _vorbis=yes ;; --disable-vorbis) _vorbis=no ;; --enable-tremor) _tremor=yes ;; @@ -583,13 +600,28 @@ for ac_option in $@; do --enable-mad) _mad=yes ;; --disable-mad) _mad=no ;; + --disable-fluidsynth) _fluidsynth=no ;; --enable-verbose-build) _verbose_build=yes ;; --enable-plugins) _dynamic_modules=yes ;; --default-dynamic) _plugins_default=dynamic ;; + --enable-mt32emu) _mt32emu=yes ;; + --disable-mt32emu) _mt32emu=no ;; --enable-vkeybd) _vkeybd=yes ;; --disable-vkeybd) _vkeybd=no ;; --enable-keymapper) _keymapper=yes ;; --disable-keymapper) _keymapper=no ;; + --enable-text-console) _text_console=yes ;; + --disable-text-console) _text_console=no ;; + --with-fluidsynth-prefix=*) + arg=`echo $ac_option | cut -d '=' -f 2` + FLUIDSYNTH_CFLAGS="-I$arg/include" + FLUIDSYNTH_LIBS="-L$arg/lib" + ;; + --with-alsa-prefix=*) + arg=`echo $ac_option | cut -d '=' -f 2` + ALSA_CFLAGS="-I$arg/include" + ALSA_LIBS="-L$arg/lib" + ;; --with-ogg-prefix=*) arg=`echo $ac_option | cut -d '=' -f 2` OGG_CFLAGS="-I$arg/include" @@ -703,6 +735,11 @@ motomagx) _host_cpu=arm _host_alias=arm-linux-gnueabi ;; +samsungtv) + _host_os=linux + _host_cpu=arm + _host_alias=arm-linux-gnueabi + ;; arm-riscos) _host_os=riscos _host_cpu=arm @@ -765,8 +802,29 @@ psp) _host_os=psp _host_cpu=mipsallegrexel _host_alias=psp + if test -z "$PSPDEV"; then + PSPDEV=$(psp-config --pspdev-path) + fi + if test -d "$PSPDEV/psp/lib"; then + LDFLAGS="$LDFLAGS -L$PSPDEV/psp/lib" + fi LDFLAGS="$LDFLAGS -L$PSPDEV/psp/sdk/lib -specs=$_srcdir/backends/platform/psp/psp.spec" ;; +ps2) + _host_os=ps2 + _host_cpu=mips64r5900el + _host_alias=ee + if test "$_debug_build" = auto; then + # Disable debug mode by default. The resulting binaries are far too big in general, + # and one has to disable multiple engines to make it usable. + _debug_build=no + fi + + if test "$_release_build" = auto; then + # Enable release build by default. + _release_build=yes + fi + ;; *) if test -n "$_host"; then guessed_host=`$_srcdir/config.sub $_host` @@ -824,8 +882,15 @@ wii | gamecube | nds) fi ;; psp) - if test -z "$PSPDEV"; then - echo "Please set PSPDEV in your environment. export PSPDEV=" + PSPSDK=$(psp-config --pspsdk-path) + if test -z "$PSPSDK"; then + echo "Please set the path to PSPSDK in your environment." + exit 1 + fi + ;; +ps2) + if test -z "$PS2SDK"; then + echo "Please set PS2SDK in your environment. export PS2SDK=" exit 1 fi ;; @@ -849,7 +914,7 @@ EOF if test -n "$_host"; then # In cross-compiling mode, we cannot run the result - eval "$1 $CXXFLAGS $LDFLAGS -o tmp_cxx_compiler$HOSTEXEEXT tmp_cxx_compiler.cpp" 2> /dev/null && rm -f tmp_cxx_compiler$HOSTEXEEXT tmp_cxx_compiler.cpp + eval "$1 $CXXFLAGS $LDFLAGS -o tmp_cxx_compiler$HOSTEXEEXT -c tmp_cxx_compiler.cpp" 2> /dev/null && rm -f tmp_cxx_compiler$HOSTEXEEXT tmp_cxx_compiler.cpp else eval "$1 $CXXFLAGS $LDFLAGS -o tmp_cxx_compiler$HOSTEXEEXT tmp_cxx_compiler.cpp" 2> /dev/null && eval "./tmp_cxx_compiler$HOSTEXEEXT 2> /dev/null" && rm -rf tmp_cxx_compiler$HOSTEXEEXT tmp_cxx_compiler.dSYM tmp_cxx_compiler.cpp fi @@ -882,6 +947,9 @@ if test -z "$CXX"; then exit 1 fi +# By default, use the C++ compiler as linker +LD=$CXX + # # Determine the compiler version # @@ -1096,9 +1164,19 @@ echo "$_have_x86" echo_n "Checking hosttype... " echo $_host_os case $_host_os in - linux* | uclinux* | openbsd* | netbsd* | bsd* | sunos* | hpux*) + linux* | uclinux*) + # When not cross-compiling, enable large file support, but don't + # care if getconf doesn't exist or doesn't recognize LFS_CFLAGS. + if test -z "$_host"; then + CXXFLAGS="$CXXFLAGS $(getconf LFS_CFLAGS 2>/dev/null)" + fi DEFINES="$DEFINES -DUNIX -DUSE_OPENGL" LIBS="$LIBS -lGL -lGLU -L/usr/X11/lib" + ;; + openbsd* | netbsd* | bsd* | sunos* | hpux*) + DEFINES="$DEFINES -DUNIX -DUSE_OPENGL" + LIBS="$LIBS -lGL -lGLU -L/usr/X11/lib" + ;; freebsd*) DEFINES="$DEFINES -DUNIX -DUSE_OPENGL" @@ -1108,42 +1186,46 @@ case $_host_os in ;; beos*) DEFINES="$DEFINES -DUNIX -DSYSTEM_NOT_SUPPORTING_D_TYPE" + # Needs -lbind -lsocket for the timidity MIDI driver LDFLAGS="-L/boot/home/config/lib" CFLAGS="-I/boot/home/config/include" CXXFLAGS="$CXXFLAGS -fhuge-objects" - type_1_byte='char' - type_2_byte='short' - type_4_byte='long' + LIBS="$LIBS -lbind -lsocket" + # FIXME: Please document why 'long' has to be used instead of int + #type_4_byte='long' ;; haiku*) DEFINES="$DEFINES -DUNIX -DSYSTEM_NOT_SUPPORTING_D_TYPE" + # Needs -lnetwork for the timidity MIDI driver + LIBS="$LIBS -lnetwork" CXXFLAGS="$CXXFLAGS -fhuge-objects" - type_1_byte='char' - type_2_byte='short' - type_4_byte='long' + # FIXME: Please document why 'long' has to be used instead of int + #type_4_byte='long' ;; solaris*) DEFINES="$DEFINES -DUNIX -DSOLARIS -DSYSTEM_NOT_SUPPORTING_D_TYPE" - LIBS="$LIBS -lnsl" + # Needs -lbind -lsocket for the timidity MIDI driver + LIBS="$LIBS -lnsl -lsocket" ;; irix*) DEFINES="$DEFINES -DUNIX -DIRIX -DSYSTEM_NOT_SUPPORTING_D_TYPE" - LIBS="$LIBS -lmd" + LIBS="$LIBS -lmd -lfastm -lm" _ranlib=: ;; darwin*) DEFINES="$DEFINES -DUNIX -DMACOSX -DUSE_OPENGL" - LIBS="$LIBS -framework QuickTime -framework AudioUnit -framework AudioToolbox -framework Carbon -framework OpenGL \ + LIBS="$LIBS -framework QuickTime -framework AudioUnit -framework AudioToolbox -framework Carbon -framework CoreMIDI -framework OpenGL \ -dylib_file /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib:/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib" + add_line_to_config_mk 'MACOSX = 1' ;; mingw*) DEFINES="$DEFINES -DWIN32 -D__USE_MINGW_ANSI_STDIO=0 -DUSE_OPENGL" - LIBS="$LIBS -lmingw32 -lopengl32 -lglu32" + LIBS="$LIBS -lmingw32 -lwinmm -lopengl32 -lglu32" OBJS="$OBJS residualico.o" ;; cygwin*) echo ERROR: Cygwin building is not supported by ScummVM anymore. Consider using MinGW. - exit 0 + exit 1 ;; os2-emx*) DEFINES="$DEFINES -DUNIX" @@ -1156,8 +1238,8 @@ case $_host_os in CXXFLAGS="$CXXFLAGS -mcrt=newlib -mstrict-align -mcpu=750 -mtune=7400" LDFLAGS="$LDFLAGS -mcrt=newlib -use-dynld -Lsobjs:" LIBS="$LIBS -lGL -lGLU" - type_1_byte='char' - type_2_byte='short' + # We have to use 'long' for our 4 byte typedef because AmigaOS already typedefs (u)int32 + # as (unsigned) long, and consequently we'd get a compiler error otherwise. type_4_byte='long' ;; dreamcast) @@ -1177,13 +1259,20 @@ case $_host_os in ;; nds) # TODO nds + DEFINES="$DEFINES -D__DS__ -DNDS -DARM9 -DARM -DNONSTANDARD_PORT" ;; psp) - CXXFLAGS="$CXXFLAGS -O3 -G0 -I$PSPDEV/psp/sdk/include -D_PSP_FW_VERSION=150" + CXXFLAGS="$CXXFLAGS -O3 -I$PSPSDK/include -D_PSP_FW_VERSION=150" + ;; + ps2) + # TODO ps2 + CXXFLAGS="$CXXFLAGS -G2" + DEFINES="$DEFINES -D_EE -D__PLAYSTATION2__" ;; wince) - CXXFLAGS="$CXXFLAGS -O3 -march=armv4 -mtune=xscale -D_WIN32_WCE=300 -D__ARM__ -D_ARM_ -DUNICODE -DFPM_DEFAULT -DNONSTANDARD_PORT" - CXXFLAGS="$CXXFLAGS -DWIN32 -Dcdecl= -D__cdecl__=" + CXXFLAGS="$CXXFLAGS -O3 -march=armv4 -mtune=xscale" + DEFINES="$DEFINES -D_WIN32_WCE=300 -D__ARM__ -D_ARM_ -DUNICODE -DFPM_DEFAULT -DNONSTANDARD_PORT" + DEFINES="$DEFINES -DWIN32 -Dcdecl= -D__cdecl__=" ;; # given this is a shell script assume some type of unix *) @@ -1194,29 +1283,38 @@ esac if test -n "$_host"; then # Cross-compiling mode - add your target here if needed + echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" case "$_host" in linupy|arm-riscos) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" - DEFINES="$DEFINES -DUNIX" - #not true for all ARM systems, but the interesting ones are all LE. Most (if not all) BE arm devices don't have a screen - _endian=little + DEFINES="$DEFINES -DUNIX -DLINUPY" + _need_memalign=yes + ;; + arm-linux|arm*-linux-gnueabi|arm-*-linux|*-angstrom-linux) + DEFINES="$DEFINES -DUNIX" _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' ;; - motoezx) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" - DEFINES="$DEFINES -DUNIX -DMOTOEZX" - #not true for all ARM systems, but the interesting ones are all LE. Most (if not all) BE arm devices don't have a screen + samsungtv) + DEFINES="$DEFINES -DUNIX -DSAMSUNGTV -DDISABLE_COMMAND_LINE" + ASFLAGS="$ASFLAGS -mfpu=vfp" + HOSTEXEEXT=".so" + _need_memalign=yes + add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' + add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + add_line_to_config_mk 'USE_ARM_GFX_ASM = 1' + add_line_to_config_mk 'USE_ARM_COSTUME_ASM = 1' + add_line_to_config_mk 'USE_ARM_SCALER_ASM = 1' + _backend="samsungtv" + _mt32emu="no" + _build_scalers="yes" + _build_hq_scalers="yes" + _vkeybd="yes" + ;; + motoezx) + DEFINES="$DEFINES -DUNIX -DMOTOEZX" ASFLAGS="$ASFLAGS -mfpu=vfp" - _endian=little _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' add_line_to_config_mk 'USE_ARM_GFX_ASM = 1' @@ -1225,23 +1323,22 @@ if test -n "$_host"; then _backend="linuxmoto" _build_hq_scalers="no" _mt32emu="no" + _vkeybd="yes" _port_mk="backends/platform/linuxmoto/linuxmoto.mk" ;; motomagx) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" DEFINES="$DEFINES -DUNIX -DMOTOMAGX" - #not true for all ARM systems, but the interesting ones are all LE. Most (if not all) BE arm devices don't have a screen ASFLAGS="$ASFLAGS -mfpu=vfp" - _endian=little _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + add_line_to_config_mk 'USE_ARM_GFX_ASM = 1' + add_line_to_config_mk 'USE_ARM_COSTUME_ASM = 1' + add_line_to_config_mk 'USE_ARM_SCALER_ASM = 1' _backend="linuxmoto" _build_hq_scalers="no" _mt32emu="no" + _vkeybd="yes" _port_mk="backends/platform/linuxmoto/linuxmoto.mk" ;; bfin*) @@ -1252,127 +1349,103 @@ if test -n "$_host"; then _strip=$_host-strip ;; gp2xwiz) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" DEFINES="$DEFINES -DUNIX -DGP2XWIZ -DNDEBUG" CXXFLAGS="$CXXFLAGS -mcpu=arm926ej-s -mtune=arm926ej-s" + ASFLAGS="$ASFLAGS -mfloat-abi=soft" LDFLAGS="$LDFLAGS" - _endian=little _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + add_line_to_config_mk 'USE_ARM_GFX_ASM = 1' + add_line_to_config_mk 'USE_ARM_SCALER_ASM = 1' + add_line_to_config_mk 'USE_ARM_COSTUME_ASM = 1' _backend="gp2xwiz" _build_hq_scalers="no" _mt32emu="no" + _vkeybd="yes" ;; gp2x) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" DEFINES="$DEFINES -DUNIX -DGP2X -DNDEBUG" CXXFLAGS="$CXXFLAGS -march=armv4t" ASFLAGS="$ASFLAGS -mfloat-abi=soft" LDFLAGS="$LDFLAGS -static" - _endian=little _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + add_line_to_config_mk 'USE_ARM_GFX_ASM = 1' + add_line_to_config_mk 'USE_ARM_SCALER_ASM = 1' + add_line_to_config_mk 'USE_ARM_COSTUME_ASM = 1' _backend="gp2x" _build_hq_scalers="no" _mt32emu="no" + _vkeybd="yes" ;; neuros) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" DEFINES="$DEFINES -DUNIX -DNEUROS" - _endian=little _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' _backend='null' + _build_hq_scalers="no" + _mt32emu="no" ;; ppc-amigaos) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" _endian=big _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='long' + # FIXME: Please document why 'long' has to be used instead of int + #type_4_byte='long' ;; m68k-atari-mint) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" DEFINES="$DEFINES -DUNIX -DSYSTEM_NOT_SUPPORTING_D_TYPE" _endian=big _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='long' + # FIXME: Please document why 'long' has to be used instead of int + #type_4_byte='long' _ranlib=m68k-atari-mint-ranlib _ar="m68k-atari-mint-ar cru" ;; *mingw32*) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" - _endian=little - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' _sdlconfig=$_host-sdl-config _windres=$_host-windres _ar="$_host-ar cru" _ranlib=$_host-ranlib ;; iphone) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" DEFINES="$DEFINES -DIPHONE -DUNIX" - _endian=little _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' _backend="iphone" + _build_hq_scalers="no" ;; wince) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" LDFLAGS="$LDFLAGS -Wl,-Map,scummvm.exe.map -Wl,--stack,65536" - _endian=little _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' add_line_to_config_mk 'USE_TREMOLO = 1' add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1' add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' + add_line_to_config_mk 'USE_ARM_GFX_ASM = 1' + add_line_to_config_mk 'USE_ARM_COSTUME_ASM = 1' + add_line_to_config_mk 'USE_ARM_SCALER_ASM = 1' _backend="wince" _mt32emu="no" _port_mk="backends/platform/wince/wince.mk" ;; dreamcast) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" DEFINES="$DEFINES -DDISABLE_DEFAULT_SAVEFILEMANAGER -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE" CXXFLAGS="$CXXFLAGS -O3 -funroll-loops -fschedule-insns2 -fomit-frame-pointer -fdelete-null-pointer-checks" - _endian=little _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' _backend="dc" + _build_scalers="no" + _build_hq_scalers="no" _mad="yes" _zlib="yes" add_line_to_config_mk 'include $(srcdir)/backends/platform/dc/dreamcast.mk' ;; wii) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" _endian=big _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' _backend="wii" + _build_scalers="no" + _build_hq_scalers="no" _port_mk="backends/platform/wii/wii.mk" add_line_to_config_mk 'GAMECUBE = 0' add_line_to_config_h "#define DEBUG_WII_USBGECKO" @@ -1383,13 +1456,12 @@ if test -n "$_host"; then add_line_to_config_h "#define USE_WII_KBD" ;; gamecube) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" _endian=big _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' _backend="wii" + _build_scalers="no" + _build_hq_scalers="no" + _mt32emu="no" _port_mk="backends/platform/wii/wii.mk" add_line_to_config_mk 'GAMECUBE = 1' add_line_to_config_h '#define GAMECUBE' @@ -1398,19 +1470,13 @@ if test -n "$_host"; then add_line_to_config_h "/* #define DEBUG_WII_GDB */" ;; nds) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" # TODO: complete this # TODO: Maybe rename nds -> ds (would be more consistent with other backends) - DEFINES="$DEFINES -D__DS__ -DNDS -DARM9 -DARM -DNONSTANDARD_PORT" DEFINES="$DEFINES -DDISABLE_FANCY_THEMES -DVECTOR_RENDERER_FORMAT=1555" DEFINES="$DEFINES -DDISABLE_DEFAULT_SAVEFILEMANAGER" DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" DEFINES="$DEFINES -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE" - _endian=little _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' _backend="nds" _build_hq_scalers="no" _mt32emu="no" @@ -1420,17 +1486,45 @@ if test -n "$_host"; then add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1' ;; psp) - echo "Cross-compiling to $_host, forcing endianness, alignment and type sizes" - _endian=little _need_memalign=yes - type_1_byte='char' - type_2_byte='short' - type_4_byte='int' _backend="psp" + _build_scalers="no" + _build_hq_scalers="no" + _mt32emu="no" _port_mk="backends/platform/psp/psp.mk" ;; + ps2) + # TODO: complete this + DEFINES="$DEFINES -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DDISABLE_DOSBOX_OPL" + _need_memalign=yes + _backend="ps2" + _build_scalers="no" + _build_hq_scalers="no" + _mt32emu="no" + # HACK to enable mad & zlib (they are not properly detected due to linker issues). + # This trick doesn't work for tremor right now, as the PS2 port the resulting library + # libtremor, while our code later on expects it to be called libvorbisidec. + # TODO: Enable tremor, e.g. by adding -ltremor or by renaming the lib. + _mad="yes" + _zlib="yes" + # HACK to fix compilation of C source files for now. + add_line_to_config_mk 'CC = ee-gcc' + # HACK to fix linking for now. It seems ee-g++ does not handle linking correctly. + LD=ee-gcc + + if test "$_debug_build" = yes; then + # TODO: Setup debug build properly + DEFINES="$DEFINES -D__PS2_DEBUG__" + #INCLUDES="$INCLUDES -I$(PS2GDB)/ee" + #LDFLAGS="$LDFLAGS -L$(PS2GDB)/lib" + LDFLAGS="$LDFLAGS -lps2gdbStub -lps2ip -ldebug" + else + # If not building for debug mode, strip binaries. + CXXFLAGS="$CXXFLAGS -s" + fi + ;; *) - echo "Continuing with auto-detected values ... if you have problems, please add your target to configure." + echo "WARNING: Unknown target, continuing with auto-detected values" ;; esac @@ -1621,6 +1715,23 @@ CXXFLAGS += -DDYNAMIC_MODULES PLUGIN_LDFLAGS = -ml -m4-single-only -nostartfiles -Wl,-q,-T$(srcdir)/backends/platform/dc/plugin.x,--just-symbols,$(EXECUTABLE),--retain-symbols-file,$(srcdir)/backends/platform/dc/plugin.syms -L$(ronindir)/lib PRE_OBJS_FLAGS := -Wl,--whole-archive POST_OBJS_FLAGS := -Wl,--no-whole-archive +' + ;; + psp) +_def_plugin=' +#define PLUGIN_PREFIX "" +#define PLUGIN_SUFFIX ".plg" +' +_mak_plugins=' +DYNAMIC_MODULES := 1 +PLUGIN_PREFIX := +PLUGIN_SUFFIX := .plg +PLUGIN_EXTRA_DEPS = $(EXECUTABLE) +CXXFLAGS += -DDYNAMIC_MODULES +LDFLAGS += -Wl,-T$(srcdir)/backends/platform/psp/main_prog.ld +PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,--just-symbols,$(EXECUTABLE),--retain-symbols-file,$(srcdir)/backends/platform/psp/plugin.syms,-T$(srcdir)/backends/platform/psp/plugin.ld -lstdc++ -lc -lm +PRE_OBJS_FLAGS := -Wl,--whole-archive +POST_OBJS_FLAGS := -Wl,--no-whole-archive ' ;; *) @@ -1633,6 +1744,16 @@ POST_OBJS_FLAGS := -Wl,--no-whole-archive fi +# +# Check whether integrated MT-32 emulator support is requested +# +if test "$_mt32emu" = no ; then + _def_mt32emu='#undef USE_MT32EMU' +else + _def_mt32emu='#define USE_MT32EMU' +fi +add_to_config_mk_if_yes "$_mt32emu" 'USE_MT32EMU = 1' + # # Check for math lib # @@ -1745,6 +1866,27 @@ fi add_to_config_mk_if_yes "$_mad" 'USE_MAD = 1' echo "$_mad" +# +# Check for ALSA +# +echocheck "ALSA >= 0.9" +if test "$_alsa" = auto ; then + _alsa=no + cat > $TMPC << EOF +#include +int main(void) { return (!(SND_LIB_MAJOR==0 && SND_LIB_MINOR==9)); } +EOF + cc_check $LDFLAGS $CXXFLAGS $ALSA_CFLAGS $ALSA_LIBS -lasound && _alsa=yes +fi +if test "$_alsa" = yes ; then + _def_alsa='#define USE_ALSA' + LIBS="$LIBS $ALSA_LIBS -lasound" + INCLUDES="$INCLUDES $ALSA_CFLAGS" +else + _def_alsa='#undef USE_ALSA' +fi +echo "$_alsa" + # # Check for ZLib # @@ -1771,6 +1913,78 @@ fi add_to_config_mk_if_yes "$_zlib" 'USE_ZLIB = 1' echo "$_zlib" +# +# Check for libfluidsynth +# +echocheck "libfluidsynth" +if test "$_fluidsynth" = auto ; then + _fluidsynth=no + cat > $TMPC << EOF +#include +int main(void) { return 0; } +EOF + cc_check $LDFLAGS $CXXFLAGS $FLUIDSYNTH_CFLAGS $FLUIDSYNTH_LIBS -lfluidsynth && _fluidsynth=yes +fi +if test "$_fluidsynth" = yes ; then + _def_fluidsynth='#define USE_FLUIDSYNTH' + case $_host_os in + mingw*) + LIBS="$LIBS $FLUIDSYNTH_LIBS -lfluidsynth -ldsound -lwinmm" + ;; + *) + LIBS="$LIBS $FLUIDSYNTH_LIBS -lfluidsynth" + ;; + esac + INCLUDES="$INCLUDES $FLUIDSYNTH_CFLAGS" +else + _def_fluidsynth='#undef USE_FLUIDSYNTH' +fi +echo "$_fluidsynth" +rm -rf $TMPC $TMPO$HOSTEXEEXT $TMPO.dSYM + +# +# Check for readline if text_console is enabled +# +echocheck "readline" +if test "$_text_console" = yes ; then + _READLINE_LIBS="-lreadline" + if test "$_readline" = auto ; then + _readline=no + cat > $TMPC << EOF +#include +#include +#include + +int main(void) { + char *x = readline(""); +} +EOF + cc_check $LDFLAGS $CXXFLAGS $READLINE_CFLAGS $READLINE_LIBS $_READLINE_LIBS && _readline=yes + if test "$_readline" = no ; then + _READLINE_LIBS="-lreadline -ltermcap" + cc_check $LDFLAGS $CXXFLAGS $READLINE_CFLAGS $READLINE_LIBS $_READLINE_LIBS && _readline=yes + fi + fi + echo "$_readline" + rm -rf $TMPC $TMPO$HOSTEXEEXT $TMPO.dSYM +else + _readline=no + echo "skipping (text console disabled)" +fi + +if test "$_readline" = yes ; then + _def_readline='#define USE_READLINE' + LIBS="$LIBS $READLINE_LIBS $_READLINE_LIBS" + INCLUDES="$INCLUDES $READLINE_CFLAGS" +else + _def_readline='#undef USE_READLINE' +fi + +if test "$_text_console" = yes ; then + _def_text_console='#define USE_TEXT_CONSOLE' +else + _def_text_console='#undef USE_TEXT_CONSOLE' +fi rm -rf $TMPC $TMPO$EXEEXT $TMPO.dSYM @@ -1799,6 +2013,14 @@ DEFINES="$DEFINES -DPLUGIN_DIRECTORY=\\\"$_libdir/residual\\\"" echo_n "Backend... " echo_n "$_backend" echo +if test "$_mt32emu" = yes ; then + echo_n ", MT-32 emu" +fi + +if test "$_text_console" = yes ; then + echo_n ", text console" +fi + if test "$_vkeybd" = yes ; then echo_n ", virtual keyboard" fi @@ -1825,6 +2047,15 @@ case $_backend in LIBS="$LIBS `$_sdlconfig --prefix="$_sdlpath" --libs`" DEFINES="$DEFINES -DSDL_BACKEND -DLINUXMOTO" ;; + samsungtv) + _sdlconfig="arm-linux-gnueabi-sdl-config" + find_sdlconfig + INCLUDES="$INCLUDES `$_sdlconfig --cflags`" + LIBS="$LIBS `$_sdlconfig --libs`" + DEFINES="$DEFINES -DSDL_BACKEND -DSAMSUNGTV" + LDFLAGS="$LDFLAGS -shared -fpic -Wl,-whole-archive" + MODULES="$MODULES backends/platform/sdl" + ;; gp2x) find_sdlconfig INCLUDES="$INCLUDES `$_sdlconfig --prefix="$_sdlpath" --cflags`" @@ -1869,7 +2100,21 @@ case $_backend in psp) DEFINES="$DEFINES -D__PSP__ -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DDISABLE_DOSBOX_OPL" INCLUDES="$INCLUDES -I$PSPDEV/psp/include/SDL" - LIBS="$LIBS -lSDL" + LIBS="$LIBS -lpng -lSDL -Wl,-Map,mapfile.txt" + SDLLIBS=$($PSPDEV/psp/bin/sdl-config --libs) + + if `echo "$SDLLIBS" | grep ".*-lGL.*" 1>/dev/null 2>&1` + then + LIBS="$LIBS -lGL" + fi + ;; + ps2) + # TODO ps2 + DEFINES="$DEFINES -D_EE -DFORCE_RTL" + INCLUDES="$INCLUDES -I$PS2SDK/ee/include -I$PS2SDK/common/include -I$PS2SDK/ports/include" + LDFLAGS="$LDFLAGS -mno-crt0 $PS2SDK/ee/startup/crt0.o -T $PS2SDK/ee/startup/linkfile" + LDFLAGS="$LDFLAGS -L$PS2SDK/ee/lib -L$PS2SDK/ports/lib" + LIBS="$LIBS -lmc -lpad -lmouse -lhdd -lpoweroff -lsjpcm -lm -lc -lfileXio -lkernel -lstdc++ " ;; *) echo "support for $_backend backend not implemented in configure script yet" @@ -1879,7 +2124,7 @@ esac MODULES="$MODULES backends/platform/$_backend" # -# Do CXXFLAGS now we know the compiler version +# Do CXXFLAGS now that we know the compiler version # if test "$have_gcc" = yes ; then if test "$_cxx_major" -ge "3" ; then @@ -1920,6 +2165,9 @@ _engines_built_static="" _engines_built_dynamic="" _engines_skipped="" +echo "dynamic modules = $_dynamic_modules" +echo "plugins default = $_plugins_default" + for engine in $_engines; do if test "`get_engine_sub $engine`" = "no" ; then # It's a main engine @@ -2021,7 +2269,14 @@ $_def_vorbis $_def_tremor $_def_flac $_def_mad +$_def_alsa $_def_zlib +$_def_fluidsynth +$_def_readline + +/* Options */ +$_def_text_console +$_def_mt32emu /* Plugin settings */ $_def_plugin @@ -2035,6 +2290,7 @@ cat > config.mk << EOF CXX := $CXX CXXFLAGS := $CXXFLAGS +LD := $LD LIBS += $LIBS RANLIB := $_ranlib STRIP := $_strip @@ -2082,7 +2338,7 @@ EOF # Create a custom Makefile when building outside the source tree # TODO: Add a better check than just looking for 'Makefile' # -if test ! -f Makefile ; then +if test ! -f Makefile.common ; then echo "Creating Makefile" cat > Makefile << EOF @@ -2092,6 +2348,7 @@ vpath %.h \$(srcdir) vpath %.cpp \$(srcdir) vpath %.c \$(srcdir) vpath %.m \$(srcdir) +vpath %.mm \$(srcdir) vpath %.asm \$(srcdir) vpath %.s \$(srcdir) vpath %.S \$(srcdir) diff --git a/dists/msvc9/residual.vcproj b/dists/msvc9/residual.vcproj index 833169a004e..b587d86390b 100644 --- a/dists/msvc9/residual.vcproj +++ b/dists/msvc9/residual.vcproj @@ -6,341 +6,1538 @@ ProjectGUID="{44D38F29-303D-4E3D-AF45-B96523BF1F9F}" RootNamespace="residual" Keyword="Win32Proj" - TargetFrameworkVersion="131072"> + TargetFrameworkVersion="131072" + > - + + + + InheritedPropertySheets=".\Residual_Debug.vsprops" + > + + + + + + + + + + + + + + + + + InheritedPropertySheets=".\Residual_Release.vsprops" + > + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - - - + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + - - + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp index ee30bbff66f..a19e6bb56dd 100644 --- a/engines/advancedDetector.cpp +++ b/engines/advancedDetector.cpp @@ -142,15 +142,23 @@ GameDescriptor findGameID( static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGameDescriptor *sg) { const char *title = 0; + const char *extra; - while (sg->gameid) { - if (!strcasecmp(g.gameid, sg->gameid)) - title = sg->description; - sg++; + if (g.flags & ADGF_USEEXTRAASTITLE) { + title = g.extra; + extra = ""; + } else { + while (sg->gameid) { + if (!strcasecmp(g.gameid, sg->gameid)) + title = sg->description; + sg++; + } + + extra = g.extra; } GameDescriptor gd(g.gameid, title, g.language, g.platform); - gd.updateDesc(g.extra); + gd.updateDesc(extra); return gd; } @@ -370,17 +378,19 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p debug(3, "+ %s", fname.c_str()); SizeMD5 tmp; - if (!md5_file_string(allFiles[fname], tmp.md5, params.md5Bytes)) + Common::File testFile; + + if (testFile.open(allFiles[fname])) { + tmp.size = (int32)testFile.size(); + if (!md5_file_string(testFile, tmp.md5, params.md5Bytes)) + tmp.md5[0] = 0; + } else { + tmp.size = -1; tmp.md5[0] = 0; + } debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5); - Common::File testFile; - if (testFile.open(allFiles[fname])) - tmp.size = (int32)testFile.size(); - else - tmp.size = -1; - filesSizeMD5[fname] = tmp; } } diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index 738be089545..06662165566 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -43,11 +43,12 @@ struct ADGameFileDescription { enum ADGameFlags { ADGF_NO_FLAGS = 0, + ADGF_USEEXTRAASTITLE = (1 << 26), // Extra field value will be used as main game title, not gameid ADGF_KEEPMATCH = (1 << 27), // this entry is kept even when there are matched // entries with more files 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_CD = (1 << 29), // add "-cd" to gameid + ADGF_DEMO = (1 << 30) // add "-demo" to gameid }; struct ADGameDescription { @@ -102,7 +103,16 @@ enum ADFlags { * not equal to english) and platform (if not equal to PC). */ kADFlagDontAugmentPreferredTarget = (1 << 0), + /** + * 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. + */ kADFlagUseExtraAsHint = (1 << 2) }; diff --git a/engines/engine.cpp b/engines/engine.cpp index 6a2dfcc83b1..d33723d275d 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -22,7 +22,7 @@ * $Id$ */ -#if defined(WIN32) +#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) #include #include // winnt.h defines ARRAYSIZE, but we want our own one... @@ -31,11 +31,13 @@ #include "engines/engine.h" #include "common/config-manager.h" +#include "common/debug.h" #include "common/events.h" #include "common/file.h" #include "common/timer.h" #include "common/savefile.h" #include "common/system.h" +#include "gui/debugger.h" #include "gui/message.h" #include "gui/GuiManager.h" #include "sound/mixer.h" @@ -43,12 +45,38 @@ #include "engines/metaengine.h" #ifdef _WIN32_WCE -extern bool isSmartphone(void); +extern bool isSmartphone(); #endif // FIXME: HACK for MidiEmu & error() Engine *g_engine = 0; +// Output formatter for debug() and error() which invokes +// the errorString method of the active engine, if any. +static void defaultOutputFormatter(char *dst, const char *src, size_t dstSize) { + if (g_engine) { + g_engine->errorString(src, dst, dstSize); + } else { + strncpy(dst, src, dstSize); + } +} + +static void defaultErrorHandler(const char *msg) { + // Unless this error -originated- within the debugger itself, we + // now invoke the debugger, if available / supported. + if (g_engine) { + GUI::Debugger *debugger = g_engine->getDebugger(); +#ifdef _WIN32_WCE + if (isSmartphone()) + debugger = 0; +#endif + if (debugger && !debugger->isAttached()) { + debugger->attach(msg); + debugger->onFrame(); + } + } +} + Engine::Engine(OSystem *syst) : _system(syst), @@ -62,6 +90,9 @@ Engine::Engine(OSystem *syst) _mainMenuDialog(NULL) { g_engine = this; + Common::setDebugOutputFormatter(defaultOutputFormatter); + Common::setErrorOutputFormatter(defaultOutputFormatter); + Common::setErrorHandler(defaultErrorHandler); // FIXME: Get rid of the following again. It is only here temporarily. // We really should never run with a non-working Mixer, so ought to handle @@ -91,7 +122,7 @@ void GUIErrorMessage(const Common::String msg) { } void Engine::checkCD() { -#if defined (WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) +#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) // It is a known bug under Windows that games that play CD audio cause // ScummVM to crash if the data files are read from the same CD. Check // if this appears to be the case and issue a warning. @@ -177,7 +208,7 @@ void Engine::pauseEngine(bool pause) { else _pauseLevel--; - if (_pauseLevel == 1) { + if (_pauseLevel == 1 && pause) { pauseEngineIntern(true); } else if (_pauseLevel == 0) { pauseEngineIntern(false); diff --git a/engines/engine.h b/engines/engine.h index 11d66a81cf8..8aac41b88b3 100644 --- a/engines/engine.h +++ b/engines/engine.h @@ -255,6 +255,7 @@ protected: }; +// FIXME: HACK for MidiEmu & error() extern Engine *g_engine; #endif diff --git a/engines/grim/grim.cpp b/engines/grim/grim.cpp index 94a8195da6d..6f761d3c981 100644 --- a/engines/grim/grim.cpp +++ b/engines/grim/grim.cpp @@ -23,6 +23,13 @@ * */ + +#if defined(WIN32) +#include +// winnt.h defines ARRAYSIZE, but we want our own one... - this is needed before including util.h +#undef ARRAYSIZE +#endif + #include "common/events.h" #include "common/file.h" #include "common/config-manager.h" diff --git a/engines/grim/imuse/imuse.cpp b/engines/grim/imuse/imuse.cpp index 809d7652a9e..b66e98405f8 100644 --- a/engines/grim/imuse/imuse.cpp +++ b/engines/grim/imuse/imuse.cpp @@ -30,6 +30,10 @@ #include "engines/grim/imuse/imuse.h" +#include "sound/audiostream.h" +#include "sound/mixer.h" +#include "sound/raw.h" + namespace Grim { Imuse *g_imuse = NULL; @@ -139,8 +143,10 @@ void Imuse::restoreState(SaveGame *savedState) { if (channels == 2) track->mixerFlags |= kFlagStereo | kFlagReverseStereo; - track->stream = Audio::makeAppendableAudioStream(freq, makeMixerFlags(track->mixerFlags)); - g_system->getMixer()->playInputStream(track->getType(), &track->handle, track->stream, -1, track->getVol(), track->getPan()); + track->stream = Audio::makeQueuingAudioStream(freq, (track->mixerFlags & kFlagStereo) != 0); + g_system->getMixer()->playInputStream(track->getType(), &track->handle, track->stream, -1, track->getVol(), + track->getPan(), DisposeAfterUse::YES, false, + (track->mixerFlags & kFlagReverseStereo) != 0); g_system->getMixer()->pauseHandle(track->handle, true); } savedState->endSection(); @@ -188,14 +194,14 @@ void Imuse::saveState(SaveGame *savedState) { int32 Imuse::makeMixerFlags(int32 flags) { int32 mixerFlags = 0; + if (flags & kFlagUnsigned) + mixerFlags |= Audio::FLAG_UNSIGNED; if (flags & kFlag16Bits) - mixerFlags |= Audio::Mixer::FLAG_16BITS; + mixerFlags |= Audio::FLAG_16BITS; if (flags & kFlagLittleEndian) - mixerFlags |= Audio::Mixer::FLAG_LITTLE_ENDIAN; + mixerFlags |= Audio::FLAG_LITTLE_ENDIAN; if (flags & kFlagStereo) - mixerFlags |= Audio::Mixer::FLAG_STEREO; - if (flags & kFlagReverseStereo) - mixerFlags |= Audio::Mixer::FLAG_REVERSE_STEREO; + mixerFlags |= Audio::FLAG_STEREO; return mixerFlags; } @@ -301,7 +307,7 @@ void Imuse::callback() { result = mixer_size; if (g_system->getMixer()->isReady()) { - track->stream->queueBuffer(data, result); + track->stream->queueBuffer(data, result, DisposeAfterUse::YES, makeMixerFlags(track->mixerFlags)); track->regionOffset += result; } else delete[] data; diff --git a/engines/grim/imuse/imuse_track.cpp b/engines/grim/imuse/imuse_track.cpp index fa993fdbdf2..04dd8bf1c50 100644 --- a/engines/grim/imuse/imuse_track.cpp +++ b/engines/grim/imuse/imuse_track.cpp @@ -137,8 +137,10 @@ bool Imuse::startSound(const char *soundName, int volGroupId, int hookId, int vo track->regionOffset = otherTrack->regionOffset; } - track->stream = Audio::makeAppendableAudioStream(freq, makeMixerFlags(track->mixerFlags)); - g_system->getMixer()->playInputStream(track->getType(), &track->handle, track->stream, -1, track->getVol(), track->getPan()); + track->stream = Audio::makeQueuingAudioStream(freq, track->mixerFlags & kFlagStereo); + g_system->getMixer()->playInputStream(track->getType(), &track->handle, track->stream, -1, + track->getVol(), track->getPan(), DisposeAfterUse::YES, + false, (track->mixerFlags & kFlagReverseStereo) != 0); track->used = true; return true; @@ -374,8 +376,10 @@ Track *Imuse::cloneToFadeOutTrack(Track *track, int fadeDelay) { fadeTrack->volFadeUsed = true; // Create an appendable output buffer - fadeTrack->stream = Audio::makeAppendableAudioStream(_sound->getFreq(fadeTrack->soundDesc), makeMixerFlags(fadeTrack->mixerFlags)); - g_system->getMixer()->playInputStream(track->getType(), &fadeTrack->handle, fadeTrack->stream, -1, fadeTrack->getVol(), fadeTrack->getPan()); + fadeTrack->stream = Audio::makeQueuingAudioStream(_sound->getFreq(fadeTrack->soundDesc), track->mixerFlags & kFlagStereo); + g_system->getMixer()->playInputStream(track->getType(), &fadeTrack->handle, fadeTrack->stream, -1, fadeTrack->getVol(), + fadeTrack->getPan(), DisposeAfterUse::YES, false, + (track->mixerFlags & kFlagReverseStereo) != 0); fadeTrack->used = true; return fadeTrack; diff --git a/engines/grim/imuse/imuse_track.h b/engines/grim/imuse/imuse_track.h index 1fe43ffe086..429c5519156 100644 --- a/engines/grim/imuse/imuse_track.h +++ b/engines/grim/imuse/imuse_track.h @@ -66,7 +66,7 @@ struct Track { ImuseSndMgr::SoundDesc *soundDesc; Audio::SoundHandle handle; - Audio::AppendableAudioStream *stream; + Audio::QueuingAudioStream *stream; Track() : used(false), stream(NULL) { soundName[0] = 0; diff --git a/engines/grim/lua/liolib.cpp b/engines/grim/lua/liolib.cpp index 3a1601499d6..2716ba19bea 100644 --- a/engines/grim/lua/liolib.cpp +++ b/engines/grim/lua/liolib.cpp @@ -319,7 +319,7 @@ static void io_write() { } static void io_date() { - tm t; + TimeDate t; char b[BUFSIZ]; g_system->getTimeAndDate(t); diff --git a/engines/grim/lua_v1.cpp b/engines/grim/lua_v1.cpp index 35f237f84ae..ad3ffd61570 100644 --- a/engines/grim/lua_v1.cpp +++ b/engines/grim/lua_v1.cpp @@ -1889,7 +1889,7 @@ static void TextFileGetLine() { int pos = (int)lua_getnumber(posObj); file->seek(pos, SEEK_SET); memset(textBuf, 0, 1000); - file->readLine_NEW(textBuf, 1000); + file->readLine(textBuf, 1000); delete file; lua_pushstring(textBuf); @@ -1923,7 +1923,7 @@ static void TextFileGetLineCount() { int pos = file->pos(); lua_pushnumber(pos); lua_settable(); - file->readLine_NEW(textBuf, 1000); + file->readLine(textBuf, 1000); line++; } delete file; diff --git a/engines/grim/smush/smush.cpp b/engines/grim/smush/smush.cpp index 335d3a453a7..41cfc607110 100644 --- a/engines/grim/smush/smush.cpp +++ b/engines/grim/smush/smush.cpp @@ -28,6 +28,10 @@ #include "common/file.h" #include "common/events.h" +#include "sound/audiostream.h" +#include "sound/mixer.h" +#include "sound/raw.h" + #include "engines/grim/smush/smush.h" #include "engines/grim/grim.h" @@ -127,16 +131,16 @@ void Smush::handleWave(const byte *src, uint32 size) { int16 *dst = new int16[size * _channels]; decompressVima(src, dst, size * _channels * 2, smushDestTable); - int flags = Audio::Mixer::FLAG_16BITS; + int flags = Audio::FLAG_16BITS; if (_channels == 2) - flags |= Audio::Mixer::FLAG_STEREO; + flags |= Audio::FLAG_STEREO; if (!_stream) { - _stream = Audio::makeAppendableAudioStream(_freq, flags); + _stream = Audio::makeQueuingAudioStream(_freq, (_channels == 2)); g_system->getMixer()->playInputStream(Audio::Mixer::kMusicSoundType, &_soundHandle, _stream); } if (g_system->getMixer()->isReady()) { - _stream->queueBuffer((byte *)dst, size * _channels * 2); + _stream->queueBuffer((byte *)dst, size * _channels * 2, DisposeAfterUse::YES, flags); } else { delete[] dst; } @@ -297,10 +301,10 @@ void Smush::handleIACT(const byte *src, int32 size) { } while (--count); if (!_stream) { - _stream = Audio::makeAppendableAudioStream(22050, Audio::Mixer::FLAG_STEREO | Audio::Mixer::FLAG_16BITS); + _stream = Audio::makeQueuingAudioStream(22050, true); g_system->getMixer()->playInputStream(Audio::Mixer::kSFXSoundType, &_soundHandle, _stream); } - _stream->queueBuffer(output_data, 0x1000); + _stream->queueBuffer(output_data, 0x1000, DisposeAfterUse::YES, Audio::FLAG_STEREO | Audio::FLAG_16BITS); bsize -= len; d_src += len; @@ -571,7 +575,7 @@ bool zlibFile::setPos(struct SavePos *pos) { return false; } _handle->seek(pos->filePos, SEEK_SET); - if (_handle->ioFailed()) { + if (_handle->err()) { warning("Unable to rewind SMUSH movie (seek failed)"); return false; } diff --git a/engines/grim/smush/smush.h b/engines/grim/smush/smush.h index 5be07c7cfcf..a203681d04d 100644 --- a/engines/grim/smush/smush.h +++ b/engines/grim/smush/smush.h @@ -80,7 +80,7 @@ private: zlibFile _file; Common::File _f; Audio::SoundHandle _soundHandle; - Audio::AppendableAudioStream *_stream; + Audio::QueuingAudioStream *_stream; int32 _frame; bool _updateNeeded; diff --git a/graphics/VectorRenderer.cpp b/graphics/VectorRenderer.cpp index 845b8466e6f..9f2ea601774 100644 --- a/graphics/VectorRenderer.cpp +++ b/graphics/VectorRenderer.cpp @@ -146,4 +146,4 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect & } } -} // end of namespace Graphics +} // End of namespace Graphics diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index 45a43b7dbcb..325d1365cb0 100644 --- a/graphics/VectorRenderer.h +++ b/graphics/VectorRenderer.h @@ -493,6 +493,6 @@ protected: int _gradientBytes[3]; /**< Color bytes of the active gradient, used to speed up calculation */ }; -} // end of namespace Graphics +} // End of namespace Graphics #endif diff --git a/graphics/colormasks.h b/graphics/colormasks.h index a6754445719..ef3997aa6a3 100644 --- a/graphics/colormasks.h +++ b/graphics/colormasks.h @@ -116,10 +116,18 @@ struct ColorMasks<555> { kGreenBits = 5, kBlueBits = 5, +#ifdef __N64__ + /* Nintendo 64 uses a BGR555 color format for 16bit display */ + kAlphaShift = 0, + kRedShift = kBlueBits+kGreenBits+1, + kGreenShift = kBlueBits + 1, + kBlueShift = 1, +#else /* RGB555 */ kAlphaShift = 0, kRedShift = kGreenBits+kBlueBits, kGreenShift = kBlueBits, kBlueShift = 0, +#endif kAlphaMask = ((1 << kAlphaBits) - 1) << kAlphaShift, kRedMask = ((1 << kRedBits) - 1) << kRedShift, @@ -321,6 +329,6 @@ PixelFormat createPixelFormat() { } -} // end of namespace Graphics +} // End of namespace Graphics #endif diff --git a/graphics/cursorman.cpp b/graphics/cursorman.cpp index a128a1c9996..ca9d37aac3d 100644 --- a/graphics/cursorman.cpp +++ b/graphics/cursorman.cpp @@ -31,14 +31,13 @@ DECLARE_SINGLETON(Graphics::CursorManager); namespace Graphics { -static bool g_initialized = false; - -CursorManager::CursorManager() { - if (!g_initialized) { - g_initialized = true; - _cursorStack.clear(); - _cursorPaletteStack.clear(); - } +CursorManager::~CursorManager() { + for (int i = 0; i < _cursorStack.size(); ++i) + delete _cursorStack[i]; + _cursorStack.clear(); + for (int i = 0; i < _cursorPaletteStack.size(); ++i) + delete _cursorPaletteStack[i]; + _cursorPaletteStack.clear(); } bool CursorManager::isVisible() { @@ -157,4 +156,50 @@ void CursorManager::replaceCursorPalette(const byte *colors, uint start, uint nu return; } +CursorManager::Cursor::Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale, const Graphics::PixelFormat *format) { +#ifdef USE_RGB_COLOR + if (!format) + _format = Graphics::PixelFormat::createFormatCLUT8(); + else + _format = *format; + _size = w * h * _format.bytesPerPixel; + _keycolor = keycolor & ((1 << (_format.bytesPerPixel << 3)) - 1); +#else + _format = Graphics::PixelFormat::createFormatCLUT8(); + _size = w * h; + _keycolor = keycolor & 0xFF; +#endif + _data = new byte[_size]; + if (data && _data) + memcpy(_data, data, _size); + _width = w; + _height = h; + _hotspotX = hotspotX; + _hotspotY = hotspotY; + _targetScale = targetScale; +} + +CursorManager::Cursor::~Cursor() { + delete[] _data; +} + +CursorManager::Palette::Palette(const byte *colors, uint start, uint num) { + _start = start; + _num = num; + _size = 4 * num; + + if (num) { + _data = new byte[_size]; + memcpy(_data, colors, _size); + } else { + _data = NULL; + } + + _disabled = false; +} + +CursorManager::Palette::~Palette() { + delete[] _data; +} + } // End of namespace Graphics diff --git a/graphics/cursorman.h b/graphics/cursorman.h index b835e895009..c90fde97246 100644 --- a/graphics/cursorman.h +++ b/graphics/cursorman.h @@ -67,7 +67,8 @@ public: * @param h the height * @param hotspotX the hotspot X coordinate * @param hotspotY the hotspot Y coordinate - * @param keycolor the index for the transparent color + * @param keycolor the color value for the transparent color. This may not exceed + * the maximum color value as defined by format. * @param targetScale the scale for which the cursor is designed * @param format a pointer to the pixel format which the cursor graphic uses, * CLUT8 will be used if this is NULL or not specified. @@ -75,7 +76,7 @@ public: * useful to push a "dummy" cursor and modify it later. The * cursor will be added to the stack, but not to the backend. */ - void pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, const Graphics::PixelFormat *format = NULL); + void pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale = 1, const Graphics::PixelFormat *format = NULL); /** * Pop a cursor from the stack, and restore the previous one to the @@ -93,12 +94,13 @@ public: * @param h the height * @param hotspotX the hotspot X coordinate * @param hotspotY the hotspot Y coordinate - * @param keycolor the index for the transparent color + * @param keycolor the color value for the transparent color. This may not exceed + * the maximum color value as defined by format. * @param targetScale the scale for which the cursor is designed * @param format a pointer to the pixel format which the cursor graphic uses, * CLUT8 will be used if this is NULL or not specified. */ - void replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, const Graphics::PixelFormat *format = NULL); + void replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale = 1, const Graphics::PixelFormat *format = NULL); /** * Pop all of the cursors and cursor palettes from their respective stacks. @@ -164,7 +166,11 @@ public: private: friend class Common::Singleton; - CursorManager(); + // Even though this is basically the default constructor we implement it + // ourselves, so it is private and thus there is no way to create this class + // except from the Singleton code. + CursorManager() {} + ~CursorManager(); struct Cursor { byte *_data; @@ -175,35 +181,12 @@ private: int _hotspotY; uint32 _keycolor; Graphics::PixelFormat _format; - byte _targetScale; + int _targetScale; uint _size; - Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, const Graphics::PixelFormat *format = NULL) { -#ifdef USE_RGB_COLOR - if (!format) - _format = Graphics::PixelFormat::createFormatCLUT8(); - else - _format = *format; - _size = w * h * _format.bytesPerPixel; - _keycolor = keycolor & ((1 << (_format.bytesPerPixel << 3)) - 1); -#else - _format = Graphics::PixelFormat::createFormatCLUT8(); - _size = w * h; - _keycolor = keycolor & 0xFF; -#endif - _data = new byte[_size]; - if (data && _data) - memcpy(_data, data, _size); - _width = w; - _height = h; - _hotspotX = hotspotX; - _hotspotY = hotspotY; - _targetScale = targetScale; - } - ~Cursor() { - delete[] _data; - } + Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale = 1, const Graphics::PixelFormat *format = NULL); + ~Cursor(); }; struct Palette { @@ -214,24 +197,8 @@ private: bool _disabled; - Palette(const byte *colors, uint start, uint num) { - _start = start; - _num = num; - _size = 4 * num; - - if (num) { - _data = new byte[_size]; - memcpy(_data, colors, _size); - } else { - _data = NULL; - } - - _disabled = false; - } - - ~Palette() { - delete[] _data; - } + Palette(const byte *colors, uint start, uint num); + ~Palette(); }; Common::Stack _cursorStack; Common::Stack _cursorPaletteStack; diff --git a/graphics/font.cpp b/graphics/font.cpp index adfe9e91e71..3f9b9507adc 100644 --- a/graphics/font.cpp +++ b/graphics/font.cpp @@ -524,7 +524,7 @@ int bdf_read_bitmaps(Common::SeekableReadStream &fp, NewFontData* pf) { } /* read the next non-comment line, returns buf or NULL if EOF*/ -// TODO: Can we use SeekableReadStream::readLine resp. readLine_NEW instead? +// TODO: Can we use SeekableReadStream::readLine instead? char *bdf_getline(Common::SeekableReadStream &fp, char *buf, int len) { int c; char *b; @@ -782,20 +782,31 @@ int Font::getStringWidth(const Common::String &str) const { return space; } -void Font::drawString(Surface *dst, const Common::String &s, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const { +void Font::drawString(Surface *dst, const Common::String &sOld, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const { assert(dst != 0); const int leftX = x, rightX = x + w; uint i; + Common::String s = sOld; int width = getStringWidth(s); Common::String str; + if (useEllipsis && width > w && s.hasSuffix("...")) { + // String is too wide. Check whether it ends in an ellipsis + // ("..."). If so, remove that and try again! + s.deleteLastChar(); + s.deleteLastChar(); + s.deleteLastChar(); + width = getStringWidth(s); + } + if (useEllipsis && width > w) { - // String is too wide. So we shorten it "intellegently", by replacing - // parts of it by an ellipsis ("..."). There are three possibilities - // for this: replace the start, the end, or the middle of the string. - // What is best really depends on the context; but unless we want to - // make this configurable, replacing the middle probably is a good - // compromise. + // String is too wide. So we shorten it "intelligently" by + // replacing parts of the string by an ellipsis. There are + // three possibilities for this: replace the start, the end, or + // the middle of the string. What is best really depends on the + // context; but unless we want to make this configurable, + // replacing the middle seems to be a good compromise. + const int ellipsisWidth = getStringWidth("..."); // SLOW algorithm to remove enough of the middle. But it is good enough @@ -816,7 +827,7 @@ void Font::drawString(Surface *dst, const Common::String &s, int x, int y, int w // The original string is width wide. Of those we already skipped past // w2 pixels, which means (width - w2) remain. - // The new str is (w2+ellipsisWidth) wide, so we can accomodate about + // The new str is (w2+ellipsisWidth) wide, so we can accommodate about // (w - (w2+ellipsisWidth)) more pixels. // Thus we skip ((width - w2) - (w - (w2+ellipsisWidth))) = // (width + ellipsisWidth - w) @@ -831,7 +842,6 @@ void Font::drawString(Surface *dst, const Common::String &s, int x, int y, int w } width = getStringWidth(str); - } else { str = s; } diff --git a/graphics/font.h b/graphics/font.h index 6a8f20ff938..34277ef5a55 100644 --- a/graphics/font.h +++ b/graphics/font.h @@ -37,9 +37,9 @@ namespace Graphics { /** Text alignment modes */ enum TextAlign { kTextAlignInvalid, - kTextAlignLeft, //!< Text should be aligned to the left - kTextAlignCenter, //!< Text should be centered - kTextAlignRight //!< Text should be aligned to the right + kTextAlignLeft, ///< Text should be aligned to the left + kTextAlignCenter, ///< Text should be centered + kTextAlignRight ///< Text should be aligned to the right }; /** @@ -140,17 +140,18 @@ public: static NewFont *loadFromCache(Common::SeekableReadStream &stream); }; -#if (defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) -# define DEFINE_FONT(n) \ - const NewFont *n; \ +#define DEFINE_FONT(n) \ + const NewFont *n = 0; \ void create_##n() { \ n = new NewFont(desc); \ } -# define INIT_FONT(n) \ - extern void create_##n(); \ +#define FORWARD_DECLARE_FONT(n) \ + extern const NewFont *n; \ + extern void create_##n(); + +#define INIT_FONT(n) \ create_##n(); -#endif } // End of namespace Graphics diff --git a/graphics/fontman.cpp b/graphics/fontman.cpp index cceb6e52abc..05d7f7f1084 100644 --- a/graphics/fontman.cpp +++ b/graphics/fontman.cpp @@ -29,38 +29,34 @@ DECLARE_SINGLETON(Graphics::FontManager); namespace Graphics { -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) -const ScummFont g_scummfont; -extern const NewFont g_sysfont; -extern const NewFont g_sysfont_big; -extern const NewFont g_consolefont; +const ScummFont *g_scummfont = 0; +FORWARD_DECLARE_FONT(g_sysfont) +FORWARD_DECLARE_FONT(g_sysfont_big) +FORWARD_DECLARE_FONT(g_consolefont) FontManager::FontManager() { + // This assert should *never* trigger, because + // FontManager is a singleton, thus there is only + // one instance of it per time. (g_scummfont gets + // reset to 0 in the desctructor of this class). + assert(g_scummfont == 0); + g_scummfont = new ScummFont; + INIT_FONT(g_sysfont) + INIT_FONT(g_sysfont_big) + INIT_FONT(g_consolefont) } -#else -const ScummFont *g_scummfont; -extern const NewFont *g_sysfont; -extern const NewFont *g_sysfont_big; -extern const NewFont *g_consolefont; - -static bool g_initialized = false; -void initfonts() { - if (!g_initialized) { - // FIXME : this need to be freed - g_initialized = true; - g_scummfont = new ScummFont; - INIT_FONT(g_sysfont) - INIT_FONT(g_sysfont_big) - INIT_FONT(g_consolefont) - } +FontManager::~FontManager() { + delete g_scummfont; + g_scummfont = 0; + delete g_sysfont; + g_sysfont = 0; + delete g_sysfont_big; + g_sysfont_big = 0; + delete g_consolefont; + g_consolefont = 0; } -FontManager::FontManager() { - initfonts(); -} -#endif - const Font *FontManager::getFontByName(const Common::String &name) const { if (!_fontMap.contains(name)) return 0; @@ -69,16 +65,6 @@ const Font *FontManager::getFontByName(const Common::String &name) const { const Font *FontManager::getFontByUsage(FontUsage usage) const { switch (usage) { -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) - case kOSDFont: - return &g_scummfont; - case kConsoleFont: - return &g_consolefont; - case kGUIFont: - return &g_sysfont; - case kBigGUIFont: - return &g_sysfont_big; -#else case kOSDFont: return g_scummfont; case kConsoleFont: @@ -87,7 +73,6 @@ const Font *FontManager::getFontByUsage(FontUsage usage) const { return g_sysfont; case kBigGUIFont: return g_sysfont_big; -#endif } return 0; diff --git a/graphics/fontman.h b/graphics/fontman.h index 2fa4fb1bb2d..2d58c03e15d 100644 --- a/graphics/fontman.h +++ b/graphics/fontman.h @@ -83,6 +83,7 @@ public: private: friend class Common::Singleton; FontManager(); + ~FontManager(); Common::HashMap _fontMap; }; diff --git a/graphics/fonts/consolefont.cpp b/graphics/fonts/consolefont.cpp index 113bf876494..65ccd3ec704 100644 --- a/graphics/fonts/consolefont.cpp +++ b/graphics/fonts/consolefont.cpp @@ -5651,10 +5651,6 @@ static const FontDesc desc = { sizeof(_font_bits)/sizeof(bitmap_t) }; -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) -extern const NewFont g_consolefont(desc); -#else DEFINE_FONT(g_consolefont) -#endif } // End of namespace Graphics diff --git a/graphics/fonts/newfont.cpp b/graphics/fonts/newfont.cpp index 8d0e879117b..0327c0997a9 100644 --- a/graphics/fonts/newfont.cpp +++ b/graphics/fonts/newfont.cpp @@ -7435,10 +7435,6 @@ static const FontDesc desc = { sizeof(_font_bits)/sizeof(bitmap_t) }; -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) -extern const NewFont g_sysfont(desc); -#else DEFINE_FONT(g_sysfont) -#endif } // End of namespace Graphics diff --git a/graphics/fonts/newfont_big.cpp b/graphics/fonts/newfont_big.cpp index 45b797a3aef..7b15a6ab389 100644 --- a/graphics/fonts/newfont_big.cpp +++ b/graphics/fonts/newfont_big.cpp @@ -5539,10 +5539,6 @@ static const FontDesc desc = { sizeof(_font_bits)/sizeof(bitmap_t) }; -#if !(defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__)) -extern const NewFont g_sysfont_big(desc); -#else DEFINE_FONT(g_sysfont_big) -#endif } // End of namespace Graphics diff --git a/graphics/imagedec.cpp b/graphics/imagedec.cpp index 259e039b27a..957d25221a5 100644 --- a/graphics/imagedec.cpp +++ b/graphics/imagedec.cpp @@ -175,4 +175,4 @@ Surface *ImageDecoder::loadFile(Common::SeekableReadStream &stream, const PixelF return decoder->decodeImage(stream, format); } -} // end of namespace Graphics +} // End of namespace Graphics diff --git a/graphics/imagedec.h b/graphics/imagedec.h index 8bf24869d67..e636c689793 100644 --- a/graphics/imagedec.h +++ b/graphics/imagedec.h @@ -61,7 +61,7 @@ public: */ virtual Surface *decodeImage(Common::SeekableReadStream &stream, const PixelFormat &format) = 0; }; -} // end of namespace Graphics +} // End of namespace Graphics #endif diff --git a/graphics/pixelformat.h b/graphics/pixelformat.h index 9b6727a8dfd..78edc18c85b 100644 --- a/graphics/pixelformat.h +++ b/graphics/pixelformat.h @@ -27,7 +27,6 @@ #define GRAPHICS_PIXELFORMAT_H #include "common/sys.h" -#include "common/list.h" namespace Graphics { @@ -148,26 +147,6 @@ struct PixelFormat { } }; -/** - * Determines the first matching format between two lists. - * - * @param backend The higher priority list, meant to be a list of formats supported by the backend - * @param frontend The lower priority list, meant to be a list of formats supported by the engine - * @return The first item on the backend list that also occurs on the frontend list - * or PixelFormat::createFormatCLUT8() if no matching formats were found. - */ -inline PixelFormat findCompatibleFormat(Common::List backend, Common::List frontend) { -#ifdef USE_RGB_COLOR - for (Common::List::iterator i = backend.begin(); i != backend.end(); ++i) { - for (Common::List::iterator j = frontend.begin(); j != frontend.end(); ++j) { - if (*i == *j) - return *i; - } - } -#endif - return PixelFormat::createFormatCLUT8(); -} - -} // end of namespace Graphics +} // End of namespace Graphics #endif diff --git a/graphics/thumbnail.cpp b/graphics/thumbnail.cpp index 86fa23942e2..355767fdf36 100644 --- a/graphics/thumbnail.cpp +++ b/graphics/thumbnail.cpp @@ -166,5 +166,5 @@ bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb) { return true; } -} // end of namespace Graphics +} // End of namespace Graphics diff --git a/graphics/thumbnail.h b/graphics/thumbnail.h index 460907d2c06..e668464e24d 100644 --- a/graphics/thumbnail.h +++ b/graphics/thumbnail.h @@ -63,7 +63,7 @@ bool saveThumbnail(Common::WriteStream &out); */ bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb); -} // end of namespace Graphics +} // End of namespace Graphics #endif diff --git a/gui/GuiManager.cpp b/gui/GuiManager.cpp index 57bdc405793..5c1e2a10bb5 100644 --- a/gui/GuiManager.cpp +++ b/gui/GuiManager.cpp @@ -412,7 +412,7 @@ void GuiManager::setupCursor() { }; CursorMan.pushCursorPalette(palette, 0, 4); - CursorMan.pushCursor(NULL, 0, 0, 0, 0); + CursorMan.pushCursor(NULL, 0, 0, 0, 0, 0); CursorMan.showMouse(true); } @@ -430,7 +430,7 @@ void GuiManager::animateCursor() { } } - CursorMan.replaceCursor(_cursor, 16, 16, 7, 7); + CursorMan.replaceCursor(_cursor, 16, 16, 7, 7, 255); _cursorAnimateTimer = time; _cursorAnimateCounter = (_cursorAnimateCounter + 1) % 4; diff --git a/gui/GuiManager.h b/gui/GuiManager.h index 22323136fab..565741583fb 100644 --- a/gui/GuiManager.h +++ b/gui/GuiManager.h @@ -30,7 +30,7 @@ #include "common/stack.h" #include "common/str.h" -#include "graphics/fontman.h" +#include "graphics/font.h" #include "gui/widget.h" #include "gui/ThemeEngine.h" diff --git a/gui/ListWidget.cpp b/gui/ListWidget.cpp index c5c15226426..b37b99ef869 100644 --- a/gui/ListWidget.cpp +++ b/gui/ListWidget.cpp @@ -286,7 +286,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) { bool dirty = false; int oldSelectedItem = _selectedItem; - if (!_editMode && isprint((char)state.ascii)) { + if (!_editMode && state.keycode <= Common::KEYCODE_z && isprint((unsigned char)state.ascii)) { // Quick selection mode: Go to first list item starting with this key // (or a substring accumulated from the last couple key presses). // Only works in a useful fashion if the list entries are sorted. @@ -351,27 +351,33 @@ bool ListWidget::handleKeyDown(Common::KeyState state) { } break; case Common::KEYCODE_UP: + case Common::KEYCODE_KP8: if (_selectedItem > 0) _selectedItem--; break; case Common::KEYCODE_DOWN: + case Common::KEYCODE_KP2: if (_selectedItem < (int)_list.size() - 1) _selectedItem++; break; case Common::KEYCODE_PAGEUP: + case Common::KEYCODE_KP9: _selectedItem -= _entriesPerPage - 1; if (_selectedItem < 0) _selectedItem = 0; break; case Common::KEYCODE_PAGEDOWN: + case Common::KEYCODE_KP3: _selectedItem += _entriesPerPage - 1; if (_selectedItem >= (int)_list.size() ) _selectedItem = _list.size() - 1; break; case Common::KEYCODE_HOME: + case Common::KEYCODE_KP7: _selectedItem = 0; break; case Common::KEYCODE_END: + case Common::KEYCODE_1: _selectedItem = _list.size() - 1; break; default: diff --git a/gui/ListWidget.h b/gui/ListWidget.h index 603efafe047..426e219d87d 100644 --- a/gui/ListWidget.h +++ b/gui/ListWidget.h @@ -40,12 +40,12 @@ enum NumberingMode { kListNumberingOne = 1 }; -// Some special commands +/// Some special commands enum { - kListItemDoubleClickedCmd = 'LIdb', // double click on item - 'data' will be item index - kListItemActivatedCmd = 'LIac', // item activated by return/enter - 'data' will be item index - kListItemRemovalRequestCmd = 'LIrm', // request to remove the item with the delete/backspace keys - 'data' will be item index - kListSelectionChangedCmd = 'Lsch' // selection changed - 'data' will be item index + kListItemDoubleClickedCmd = 'LIdb', ///< double click on item - 'data' will be item index + kListItemActivatedCmd = 'LIac', ///< item activated by return/enter - 'data' will be item index + kListItemRemovalRequestCmd = 'LIrm', ///< request to remove the item with the delete/backspace keys - 'data' will be item index + kListSelectionChangedCmd = 'Lsch' ///< selection changed - 'data' will be item index }; /* ListWidget */ @@ -137,7 +137,7 @@ public: protected: void drawWidget(); - //! Finds the item at position (x,y). Returns -1 if there is no item there. + /// Finds the item at position (x,y). Returns -1 if there is no item there. int findItem(int x, int y) const; void scrollBarRecalc(); diff --git a/gui/PopUpWidget.cpp b/gui/PopUpWidget.cpp index 675331e2afe..53ee9ba571a 100644 --- a/gui/PopUpWidget.cpp +++ b/gui/PopUpWidget.cpp @@ -228,15 +228,19 @@ void PopUpDialog::handleKeyDown(Common::KeyState state) { close(); break; case Common::KEYCODE_UP: + case Common::KEYCODE_KP8: moveUp(); break; case Common::KEYCODE_DOWN: + case Common::KEYCODE_KP2: moveDown(); break; case Common::KEYCODE_HOME: + case Common::KEYCODE_KP7: setSelection(0); break; case Common::KEYCODE_END: + case Common::KEYCODE_KP1: setSelection(_popUpBoss->_entries.size()-1); break; default: diff --git a/gui/PopUpWidget.h b/gui/PopUpWidget.h index afba6a89893..17ae7259b8a 100644 --- a/gui/PopUpWidget.h +++ b/gui/PopUpWidget.h @@ -75,7 +75,7 @@ public: int getSelected() const { return _selectedItem; } uint32 getSelectedTag() const { return (_selectedItem >= 0) ? _entries[_selectedItem].tag : (uint32)-1; } - const String& getSelectedString() const { return (_selectedItem >= 0) ? _entries[_selectedItem].name : String::emptyString; } +// const String& getSelectedString() const { return (_selectedItem >= 0) ? _entries[_selectedItem].name : String::emptyString; } void handleMouseEntered(int button) { setFlags(WIDGET_HILITED); draw(); } void handleMouseLeft(int button) { clearFlags(WIDGET_HILITED); draw(); } diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index 2d0ce0acf1f..5cd4b2a14e2 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -31,10 +31,11 @@ #include "common/fs.h" #include "common/unzip.h" -#include "graphics/surface.h" #include "graphics/colormasks.h" -#include "graphics/imagedec.h" #include "graphics/cursorman.h" +#include "graphics/fontman.h" +#include "graphics/imagedec.h" +#include "graphics/surface.h" #include "graphics/VectorRenderer.h" #include "gui/launcher.h" @@ -154,10 +155,10 @@ protected: * Data definitions for theme engine elements *********************************************************/ struct DrawDataInfo { - DrawData id; //!< The actual ID of the DrawData item. - const char *name; //!< The name of the DrawData item as it appears in the Theme Description files - bool buffer; //!< Sets whether this item is buffered on the backbuffer or drawn directly to the screen. - DrawData parent; //!< Parent DrawData item, for items that overlay. E.g. kButtonIdle -> kButtonHover + DrawData id; ///< The actual ID of the DrawData item. + const char *name; ///< The name of the DrawData item as it appears in the Theme Description files + bool buffer; ///< Sets whether this item is buffered on the backbuffer or drawn directly to the screen. + DrawData parent; ///< Parent DrawData item, for items that overlay. E.g. kButtonIdle -> kButtonHover }; /** @@ -696,7 +697,7 @@ bool ThemeEngine::loadDefaultXML() { #include "themes/default.inc" ; - if (!_parser->loadBuffer((const byte*)defaultXML, strlen(defaultXML), false)) + if (!_parser->loadBuffer((const byte*)defaultXML, strlen(defaultXML))) return false; _themeName = "ScummVM Classic Theme (Builtin Version)"; @@ -849,7 +850,7 @@ void ThemeEngine::drawButton(const Common::Rect &r, const Common::String &str, W dd = kDDButtonDisabled; queueDD(dd, r, 0, hints & WIDGET_CLEARBG); - queueDDText(getTextData(dd), getTextColor(dd), r, str, false, false, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV); + queueDDText(getTextData(dd), getTextColor(dd), r, str, false, true, _widgets[dd]->_textAlignH, _widgets[dd]->_textAlignV); } void ThemeEngine::drawLineSeparator(const Common::Rect &r, WidgetStateInfo state) { @@ -1204,7 +1205,7 @@ void ThemeEngine::renderDirtyScreen() { if (_dirtyScreen.empty()) return; - Common::List::iterator i, j; + Common::List::iterator i; for (i = _dirtyScreen.begin(); i != _dirtyScreen.end(); ++i) { _vectorRenderer->copyFrame(_system, *i); } @@ -1641,4 +1642,4 @@ Common::String ThemeEngine::getThemeId(const Common::String &filename) { } -} // end of namespace GUI. +} // End of namespace GUI. diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index 76d03daf530..bc78e736b49 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -30,7 +30,7 @@ #include "common/system.h" #include "common/fs.h" #include "graphics/surface.h" -#include "graphics/fontman.h" +#include "graphics/font.h" #define RESIDUAL_THEME_VERSION_STR "RESIDUAL_STX0.8" @@ -134,7 +134,7 @@ protected: friend class GUI::GuiObject; public: - //! Vertical alignment of the text. + /// Vertical alignment of the text. enum TextAlignVertical { kTextAlignVInvalid, kTextAlignVBottom, @@ -142,17 +142,17 @@ public: kTextAlignVTop }; - //! Widget background type + /// Widget background type enum WidgetBackground { - kWidgetBackgroundNo, //!< No background at all - kWidgetBackgroundPlain, //!< Simple background, this may not include borders - kWidgetBackgroundBorder, //!< Same as kWidgetBackgroundPlain just with a border - kWidgetBackgroundBorderSmall, //!< Same as kWidgetBackgroundPlain just with a small border - kWidgetBackgroundEditText, //!< Background used for edit text fields - kWidgetBackgroundSlider //!< Background used for sliders + kWidgetBackgroundNo, ///< No background at all + kWidgetBackgroundPlain, ///< Simple background, this may not include borders + kWidgetBackgroundBorder, ///< Same as kWidgetBackgroundPlain just with a border + kWidgetBackgroundBorderSmall, ///< Same as kWidgetBackgroundPlain just with a small border + kWidgetBackgroundEditText, ///< Background used for edit text fields + kWidgetBackgroundSlider ///< Background used for sliders }; - //! Dialog background type + /// Dialog background type enum DialogBackground { kDialogBackgroundMain, kDialogBackgroundSpecial, @@ -160,20 +160,20 @@ public: kDialogBackgroundDefault }; - //! State of the widget to be drawn + /// State of the widget to be drawn enum State { - kStateDisabled, //!< Indicates that the widget is disabled, that does NOT include that it is invisible - kStateEnabled, //!< Indicates that the widget is enabled - kStateHighlight //!< Indicates that the widget is highlighted by the user + kStateDisabled, ///< Indicates that the widget is disabled, that does NOT include that it is invisible + kStateEnabled, ///< Indicates that the widget is enabled + kStateHighlight ///< Indicates that the widget is highlighted by the user }; typedef State WidgetStateInfo; - //! Text inversion state of the text to be draw + /// Text inversion state of the text to be draw enum TextInversionState { - kTextInversionNone, //!< Indicates that the text should not be drawn inverted - kTextInversion, //!< Indicates that the text should be drawn inverted, but not focused - kTextInversionFocus //!< Indicates thte the test should be drawn inverted, and focused + kTextInversionNone, ///< Indicates that the text should not be drawn inverted + kTextInversion, ///< Indicates that the text should be drawn inverted, but not focused + kTextInversionFocus ///< Indicates thte the test should be drawn inverted, and focused }; enum ScrollbarState { @@ -184,35 +184,35 @@ public: kScrollbarStateSinglePage }; - //! Font style selector + /// Font style selector enum FontStyle { - kFontStyleBold = 0, //!< A bold font. This is also the default font. - kFontStyleNormal = 1, //!< A normal font. - kFontStyleItalic = 2, //!< Italic styled font. - kFontStyleFixedNormal = 3, //!< Fixed size font. - kFontStyleFixedBold = 4, //!< Fixed size bold font. - kFontStyleFixedItalic = 5, //!< Fixed size italic font. + kFontStyleBold = 0, ///< A bold font. This is also the default font. + kFontStyleNormal = 1, ///< A normal font. + kFontStyleItalic = 2, ///< Italic styled font. + kFontStyleFixedNormal = 3, ///< Fixed size font. + kFontStyleFixedBold = 4, ///< Fixed size bold font. + kFontStyleFixedItalic = 5, ///< Fixed size italic font. kFontStyleMax }; - //! Font color selector + /// Font color selector enum FontColor { - kFontColorNormal = 0, //!< The default color of the theme - kFontColorAlternate = 1, //!< Alternative font color + kFontColorNormal = 0, ///< The default color of the theme + kFontColorAlternate = 1, ///< Alternative font color kFontColorMax }; - //! Function used to process areas other than the current dialog + /// Function used to process areas other than the current dialog enum ShadingStyle { - kShadingNone, //!< No special post processing - kShadingDim, //!< Dimming unused areas - kShadingLuminance //!< Converting colors to luminance for unused areas + kShadingNone, ///< No special post processing + kShadingDim, ///< Dimming unused areas + kShadingLuminance ///< Converting colors to luminance for unused areas }; // Special image ids for images used in the GUI - static const char * const kImageLogo; //!< ScummVM logo used in the launcher - static const char * const kImageLogoSmall; //!< ScummVM logo used in the GMM - static const char * const kImageSearch; //!< Search tool image used in the launcher + static const char * const kImageLogo; ///< ScummVM logo used in the launcher + static const char * const kImageLogoSmall; ///< ScummVM logo used in the GMM + static const char * const kImageSearch; ///< Search tool image used in the launcher /** * Graphics mode enumeration. @@ -220,9 +220,9 @@ public: * surface. */ enum GraphicsMode { - kGfxDisabled = 0, //!< No GFX - kGfxStandard16bit, //!< 2BPP with the standard (aliased) renderer. - kGfxAntialias16bit //!< 2BPP with the optimized AA renderer. + kGfxDisabled = 0, ///< No GFX + kGfxStandard16bit, ///< 2BPP with the standard (aliased) renderer. + kGfxAntialias16bit ///< 2BPP with the optimized AA renderer. }; /** Constant value to expand dirty rectangles, to make sure they are fully copied */ @@ -625,11 +625,11 @@ protected: /** Queue with all the drawing that must be done to the screen */ Common::List _screenQueue; - bool _initOk; //!< Class and renderer properly initialized - bool _themeOk; //!< Theme data successfully loaded. - bool _enabled; //!< Whether the Theme is currently shown on the overlay + bool _initOk; ///< Class and renderer properly initialized + bool _themeOk; ///< Theme data successfully loaded. + bool _enabled; ///< Whether the Theme is currently shown on the overlay - Common::String _themeName; //!< Name of the currently loaded theme + Common::String _themeName; ///< Name of the currently loaded theme Common::String _themeId; Common::String _themeFile; Common::Archive *_themeArchive; @@ -647,6 +647,6 @@ protected: byte _cursorPalSize; }; -} // end of namespace GUI. +} // End of namespace GUI. #endif diff --git a/gui/about.cpp b/gui/about.cpp index 5631d1a3314..95e5da5873b 100644 --- a/gui/about.cpp +++ b/gui/about.cpp @@ -58,7 +58,7 @@ enum { static const char *copyright_text[] = { "", -"C0""Copyright (C) 2003-2009 The Residual project", +"C0""Copyright (C) 2003-2010 The Residual project", "C0""http://residual.sourceforge.net", "", "C0""Residual is the legal property of its developers, whose names are too numerous to list here. Please refer to the AUTHORS file distributed with this binary.", diff --git a/gui/browser.cpp b/gui/browser.cpp index f262a0602e2..3782dbd97db 100644 --- a/gui/browser.cpp +++ b/gui/browser.cpp @@ -37,86 +37,6 @@ enum { kGoUpCmd = 'GoUp' }; -#ifdef MACOSX -/* On Mac OS X, use the native file selector dialog. We could do the same for - * other operating systems. - */ - -BrowserDialog::BrowserDialog(const char *title, bool dirBrowser) - : Dialog("Browser") { - _titleRef = CFStringCreateWithCString(0, title, CFStringGetSystemEncoding()); - _isDirBrowser = dirBrowser; -} - -BrowserDialog::~BrowserDialog() { - CFRelease(_titleRef); -} - -int BrowserDialog::runModal() { - NavDialogRef dialogRef; - WindowRef windowRef = 0; - NavDialogCreationOptions options; - NavUserAction result; - NavReplyRecord reply; - OSStatus err; - bool choiceMade = false; - - // Temporarily show the real mouse - CGDisplayShowCursor(kCGDirectMainDisplay); - - err = NavGetDefaultDialogCreationOptions(&options); - assert(err == noErr); - options.windowTitle = _titleRef; -// options.message = CFSTR("Select your game directory"); - options.modality = kWindowModalityAppModal; - - if (_isDirBrowser) - err = NavCreateChooseFolderDialog(&options, 0, 0, 0, &dialogRef); - else - err = NavCreateChooseFileDialog(&options, 0, 0, 0, 0, 0, &dialogRef); - assert(err == noErr); - - windowRef = NavDialogGetWindow(dialogRef); - - err = NavDialogRun(dialogRef); - assert(err == noErr); - - CGDisplayHideCursor(kCGDirectMainDisplay); - - result = NavDialogGetUserAction(dialogRef); - - if (result == kNavUserActionChoose) { - err = NavDialogGetReply(dialogRef, &reply); - assert(err == noErr); - - if (reply.validRecord && err == noErr) { - SInt32 theCount; - AECountItems(&reply.selection, &theCount); - assert(theCount == 1); - - AEKeyword keyword; - FSRef ref; - char buf[4096]; - err = AEGetNthPtr(&reply.selection, 1, typeFSRef, &keyword, NULL, &ref, sizeof(ref), NULL); - assert(err == noErr); - err = FSRefMakePath(&ref, (UInt8*)buf, sizeof(buf)-1); - assert(err == noErr); - - _choice = Common::FSNode(buf); - choiceMade = true; - } - - err = NavDisposeReply(&reply); - assert(err == noErr); - } - - NavDialogDispose(dialogRef); - - return choiceMade; -} - -#else - /* We want to use this as a general directory selector at some point... possible uses * - to select the data dir for a game * - to select the place where save games are stored @@ -169,11 +89,10 @@ void BrowserDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data // If nothing is selected in the list widget, choose the current dir. // Else, choose the dir that is selected. int selection = _fileList->getSelected(); - if (selection >= 0) { + if (selection >= 0) _choice = _nodeContent[selection]; - } else { + else _choice = _node; - } setResult(1); close(); } else { @@ -199,12 +118,19 @@ void BrowserDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data if (_nodeContent[data].isDirectory()) { _node = _nodeContent[data]; updateListing(); - } else { + } else if (!_isDirBrowser) { _choice = _nodeContent[data]; setResult(1); close(); } break; + case kListSelectionChangedCmd: + // We do not allow selecting directories in directory + // browser mode, thus we will invalidate the selection + // when the user selects an directory over here. + if (data != (uint32)-1 && _isDirBrowser && !_nodeContent[data].isDirectory()) + _fileList->setSelected(-1); + break; default: Dialog::handleCommand(sender, cmd, data); } @@ -218,30 +144,36 @@ void BrowserDialog::updateListing() { ConfMan.set("browser_lastpath", _node.getPath()); // Read in the data from the file system - Common::FSNode::ListMode listMode = - _isDirBrowser ? Common::FSNode::kListDirectoriesOnly - : Common::FSNode::kListAll; - if (!_node.getChildren(_nodeContent, listMode)) { + if (!_node.getChildren(_nodeContent, Common::FSNode::kListAll)) _nodeContent.clear(); - } else { + else Common::sort(_nodeContent.begin(), _nodeContent.end()); - } // Populate the ListWidget Common::StringList list; + ListWidget::ColorList colors; for (Common::FSList::iterator i = _nodeContent.begin(); i != _nodeContent.end(); ++i) { - if (!_isDirBrowser && i->isDirectory()) + if (i->isDirectory()) list.push_back(i->getDisplayName() + "/"); else list.push_back(i->getDisplayName()); + + if (_isDirBrowser) { + if (i->isDirectory()) + colors.push_back(ThemeEngine::kFontColorNormal); + else + colors.push_back(ThemeEngine::kFontColorAlternate); + } } - _fileList->setList(list); + + if (_isDirBrowser) + _fileList->setList(list, &colors); + else + _fileList->setList(list); _fileList->scrollTo(0); // Finally, redraw draw(); } -#endif // MACOSX - } // End of namespace GUI diff --git a/gui/console.cpp b/gui/console.cpp index b39e2307e61..d1d419e613b 100644 --- a/gui/console.cpp +++ b/gui/console.cpp @@ -32,7 +32,7 @@ #include "common/events.h" #include "common/system.h" -#include "graphics/font.h" +#include "graphics/fontman.h" namespace GUI { @@ -386,17 +386,21 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) { draw(); break; case Common::KEYCODE_UP: + case Common::KEYCODE_KP8: historyScroll(+1); break; case Common::KEYCODE_DOWN: + case Common::KEYCODE_KP2: historyScroll(-1); break; case Common::KEYCODE_RIGHT: + case Common::KEYCODE_KP6: if (_currentPos < _promptEndPos) _currentPos++; drawLine(pos2line(_currentPos)); break; case Common::KEYCODE_LEFT: + case Common::KEYCODE_KP4: if (_currentPos > _promptStartPos) _currentPos--; drawLine(pos2line(_currentPos)); diff --git a/gui/editable.cpp b/gui/editable.cpp index f9d40cfe1c5..939290fc483 100644 --- a/gui/editable.cpp +++ b/gui/editable.cpp @@ -124,6 +124,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) { forcecaret = true; break; case Common::KEYCODE_LEFT: + case Common::KEYCODE_KP4: if (_caretPos > 0) { dirty = setCaretPos(_caretPos - 1); } @@ -131,6 +132,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) { dirty = true; break; case Common::KEYCODE_RIGHT: + case Common::KEYCODE_KP6: if (_caretPos < (int)_editString.size()) { dirty = setCaretPos(_caretPos + 1); } @@ -138,10 +140,12 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) { dirty = true; break; case Common::KEYCODE_HOME: + case Common::KEYCODE_KP7: dirty = setCaretPos(0); forcecaret = true; break; case Common::KEYCODE_END: + case Common::KEYCODE_KP1: dirty = setCaretPos(_editString.size()); forcecaret = true; break; diff --git a/gui/launcher.cpp b/gui/launcher.cpp index 221e0bd160e..ddb389bae38 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -48,6 +48,9 @@ #include "graphics/cursorman.h" +#include "sound/mididrv.h" + + using Common::ConfigManager; namespace GUI { @@ -220,6 +223,18 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc) addVolumeControls(tab, "GameOptions_Volume."); + // + // 6) The MIDI tab + // + tab->addTab("MIDI"); + + _globalMIDIOverride = new CheckboxWidget(tab, "GameOptions_MIDI.EnableTabCheckbox", "Override global MIDI settings", kCmdGlobalMIDIOverride, 0); + + if (_guioptions & Common::GUIO_NOMIDI) + _globalMIDIOverride->setEnabled(false); + + addMIDIControls(tab, "GameOptions_MIDI."); + // // 2) The 'Path' tab // @@ -285,6 +300,13 @@ void EditGameDialog::open() { ConfMan.hasKey("speech_volume", _domain); _globalVolumeOverride->setState(e); + e = ConfMan.hasKey("soundfont", _domain) || + ConfMan.hasKey("multi_midi", _domain) || + ConfMan.hasKey("native_mt32", _domain) || + ConfMan.hasKey("enable_gs", _domain) || + ConfMan.hasKey("midi_gain", _domain); + _globalMIDIOverride->setState(e); + // TODO: game path const Common::LanguageDescription *l = Common::g_languages; @@ -354,6 +376,10 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat setVolumeSettingsState(data != 0); draw(); break; + case kCmdGlobalMIDIOverride: + setMIDISettingsState(data != 0); + draw(); + break; case kCmdGlobalVolumeOverride: setVolumeSettingsState(data != 0); draw(); @@ -469,19 +495,19 @@ LauncherDialog::LauncherDialog() #endif new ButtonWidget(this, "Launcher.QuitButton", "Quit", kQuitCmd, 'Q'); - new ButtonWidget(this, "Launcher.AboutButton", "About", kAboutCmd, 'B'); - new ButtonWidget(this, "Launcher.OptionsButton", "Options", kOptionsCmd, 'O'); + new ButtonWidget(this, "Launcher.AboutButton", "About...", kAboutCmd, 'B'); + new ButtonWidget(this, "Launcher.OptionsButton", "Options...", kOptionsCmd, 'O'); _startButton = new ButtonWidget(this, "Launcher.StartButton", "Start", kStartCmd, 'S'); _loadButton = - new ButtonWidget(this, "Launcher.LoadGameButton", "Load", kLoadGameCmd, 'L'); + new ButtonWidget(this, "Launcher.LoadGameButton", "Load...", kLoadGameCmd, 'L'); // Above the lowest button rows: two more buttons (directly below the list box) _addButton = - new ButtonWidget(this, "Launcher.AddGameButton", "Add Game", kAddGameCmd, 'A'); + new ButtonWidget(this, "Launcher.AddGameButton", "Add Game...", kAddGameCmd, 'A'); _editButton = - new ButtonWidget(this, "Launcher.EditGameButton", "Edit Game", kEditGameCmd, 'E'); + new ButtonWidget(this, "Launcher.EditGameButton", "Edit Game...", kEditGameCmd, 'E'); _removeButton = new ButtonWidget(this, "Launcher.RemoveGameButton", "Remove Game", kRemoveGameCmd, 'R'); @@ -510,7 +536,7 @@ LauncherDialog::LauncherDialog() // Restore last selection String last(ConfMan.get("lastselectedgame", ConfigManager::kApplicationDomain)); - selectGame(last); + selectTarget(last); // En-/disable the buttons depending on the list selection updateButtons(); @@ -522,12 +548,12 @@ LauncherDialog::LauncherDialog() _loadDialog = new SaveLoadChooser("Load game:", "Load"); } -void LauncherDialog::selectGame(const String &name) { - if (!name.empty()) { +void LauncherDialog::selectTarget(const String &target) { + if (!target.empty()) { int itemToSelect = 0; StringList::const_iterator iter; for (iter = _domains.begin(); iter != _domains.end(); ++iter, ++itemToSelect) { - if (name == *iter) { + if (target == *iter) { _list->setSelected(itemToSelect); break; } @@ -621,7 +647,7 @@ void LauncherDialog::updateListing() { void LauncherDialog::addGame() { int modifiers = g_system->getEventManager()->getModifierState(); - bool massAdd = (modifiers & Common::KBD_SHIFT) != 0; + const bool massAdd = (modifiers & Common::KBD_SHIFT) != 0; if (massAdd) { MessageDialog alert("Do you really want to run the mass game detector? " @@ -629,19 +655,17 @@ void LauncherDialog::addGame() { if (alert.runModal() == GUI::kMessageOK && _browser->runModal() > 0) { MassAddDialog massAddDlg(_browser->getResult()); - if (_list->getSelected() != -1) { - // Save current game position, so on cancel cursor will move back - ConfMan.set("temp_selection", _domains[_list->getSelected()], ConfigManager::kApplicationDomain); - } - massAddDlg.runModal(); // Update the ListWidget and force a redraw - updateListing(); - // Set cursor to first detected game - selectGame(ConfMan.get("temp_selection", ConfigManager::kApplicationDomain)); - ConfMan.removeKey("temp_selection", ConfigManager::kApplicationDomain); + // If new target(s) were added, update the ListWidget and move + // the selection to to first newly detected game. + Common::String newTarget = massAddDlg.getFirstAddedTarget(); + if (!newTarget.empty()) { + updateListing(); + selectTarget(newTarget); + } draw(); } @@ -722,7 +746,7 @@ void LauncherDialog::addGame() { // Update the ListWidget, select the new item, and force a redraw updateListing(); - selectGame(editDialog.getDomain()); + selectTarget(editDialog.getDomain()); draw(); } else { // User aborted, remove the the new domain again @@ -812,7 +836,7 @@ void LauncherDialog::editGame(int item) { // Update the ListWidget, reselect the edited game and force a redraw updateListing(); - selectGame(editDialog.getDomain()); + selectTarget(editDialog.getDomain()); draw(); } } @@ -895,7 +919,7 @@ void LauncherDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat case kStartCmd: case kListItemActivatedCmd: case kListItemDoubleClickedCmd: - // Print out what was selected + // Start the selected game. assert(item >= 0); ConfMan.setActiveDomain(_domains[item]); close(); @@ -912,9 +936,11 @@ void LauncherDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat close(); break; case kSearchCmd: + // Update the active search filter. _list->setFilter(_searchWidget->getEditString()); break; case kSearchClearCmd: + // Reset the active search filter, thus showing all games again _searchWidget->setEditString(""); _list->setFilter(""); break; @@ -951,9 +977,10 @@ void LauncherDialog::updateButtons() { // Update the label of the "Add" button depending on whether shift is pressed or not int modifiers = g_system->getEventManager()->getModifierState(); - const char *newAddButtonLabel = ((modifiers & Common::KBD_SHIFT) != 0) - ? "Mass Add" - : "Add Game"; + const bool massAdd = (modifiers & Common::KBD_SHIFT) != 0; + const char *newAddButtonLabel = massAdd + ? "Mass Add..." + : "Add Game..."; if (_addButton->getLabel() != newAddButtonLabel) _addButton->setLabel(newAddButtonLabel); diff --git a/gui/launcher.h b/gui/launcher.h index 9bfe61dc1bc..70962fab55f 100644 --- a/gui/launcher.h +++ b/gui/launcher.h @@ -72,17 +72,44 @@ protected: virtual void reflowLayout(); + /** + * Fill the list widget with all currently configured targets, and trigger + * a redraw. + */ void updateListing(); + void updateButtons(); void open(); void close(); + + /** + * Handle "Add game..." button. + */ virtual void addGame(); + + /** + * Handle "Remove game..." button. + */ void removeGame(int item); + + /** + * Handle "Edit game..." button. + */ void editGame(int item); + + /** + * Handle "Load..." button. + */ void loadGame(int item); - void selectGame(const String &name); + /** + * Select the target with the given name in the launcher game list. + * Also scrolls the list so that the newly selected item is visible. + * + * @target name of target to select + */ + void selectTarget(const String &target); }; } // End of namespace GUI diff --git a/gui/massadd.cpp b/gui/massadd.cpp index 42ddfa4e7a7..715b5987435 100644 --- a/gui/massadd.cpp +++ b/gui/massadd.cpp @@ -139,7 +139,7 @@ void MassAddDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data sort(_games.begin(), _games.end(), GameTargetLess()); // Add all the detected games to the config for (GameList::iterator iter = _games.begin(); iter != _games.end(); ++iter) { - printf(" Added gameid '%s', desc '%s'\n", + debug(1, " Added gameid '%s', desc '%s'\n", (*iter)["gameid"].c_str(), (*iter)["description"].c_str()); (*iter)["gameid"] = addGameToConf(*iter); @@ -157,6 +157,7 @@ void MassAddDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data close(); } else if (cmd == kCancelCmd) { // User cancelled, so we don't do anything and just leave. + _games.clear(); close(); } else { Dialog::handleCommand(sender, cmd, data); @@ -261,5 +262,5 @@ void MassAddDialog::handleTickle() { } -} // end of namespace GUI +} // End of namespace GUI diff --git a/gui/massadd.h b/gui/massadd.h index 3442179e101..6920fa786e4 100644 --- a/gui/massadd.h +++ b/gui/massadd.h @@ -44,6 +44,12 @@ public: void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); void handleTickle(); + Common::String getFirstAddedTarget() const { + if (!_games.empty()) + return _games.front().gameid(); + return Common::String(); + } + private: Common::Stack _scanStack; GameList _games; diff --git a/gui/module.mk b/gui/module.mk index cd0c73b1a3b..e97a6eb7419 100644 --- a/gui/module.mk +++ b/gui/module.mk @@ -2,7 +2,6 @@ MODULE := gui MODULE_OBJS := \ about.o \ - browser.o \ chooser.o \ console.o \ debugger.o \ @@ -27,5 +26,13 @@ MODULE_OBJS := \ ThemeParser.o \ widget.o +ifdef MACOSX +MODULE_OBJS += \ + browser_osx.o +else +MODULE_OBJS += \ + browser.o +endif + # Include common rules include $(srcdir)/rules.mk diff --git a/gui/object.cpp b/gui/object.cpp index 7620799b04a..d3c6f5cdd0a 100644 --- a/gui/object.cpp +++ b/gui/object.cpp @@ -32,6 +32,7 @@ namespace GUI { GuiObject::GuiObject(const Common::String &name) : _firstWidget(0) { _name = name; + reflowLayout(); } GuiObject::~GuiObject() { diff --git a/gui/saveload.cpp b/gui/saveload.cpp index 0a3ad92d70f..1c7e5626488 100644 --- a/gui/saveload.cpp +++ b/gui/saveload.cpp @@ -114,7 +114,8 @@ void SaveLoadChooser::open() { } const Common::String &SaveLoadChooser::getResultString() const { - return (_list->getSelected() > -1) ? _list->getSelectedString() : _resultString; + int selItem = _list->getSelected(); + return (selItem >= 0) ? _list->getSelectedString() : _resultString; } void SaveLoadChooser::setSaveMode(bool saveMode) { diff --git a/gui/themebrowser.cpp b/gui/themebrowser.cpp index c79237cf570..146791adf07 100644 --- a/gui/themebrowser.cpp +++ b/gui/themebrowser.cpp @@ -116,5 +116,5 @@ void ThemeBrowser::updateListing() { draw(); } -} // end of namespace GUI +} // End of namespace GUI diff --git a/gui/themebrowser.h b/gui/themebrowser.h index c0ee2b0d2d4..36ea347c8b5 100644 --- a/gui/themebrowser.h +++ b/gui/themebrowser.h @@ -54,7 +54,7 @@ private: void updateListing(); }; -} // end of namespace GUI +} // End of namespace GUI #endif diff --git a/ports.mk b/ports.mk index b78fcb76e1e..ed58200a81a 100644 --- a/ports.mk +++ b/ports.mk @@ -63,6 +63,7 @@ iphonebundle: iphone cp $(srcdir)/dists/iphone/Info.plist $(bundle_name)/ cp $(DIST_FILES_THEMES) $(bundle_name)/ #cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/ + ldid -S residual cp residual $(bundle_name)/Residual cp $(srcdir)/dists/iphone/icon.png $(bundle_name)/icon.png cp $(srcdir)/dists/iphone/Default.png $(bundle_name)/Default.png @@ -96,6 +97,10 @@ ifdef USE_MPEG2 OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libmpeg2.a endif +ifdef USE_ZLIB +OSX_ZLIB ?= -lz +endif + # Special target to create a static linked binary for Mac OS X. # We use -force_cpusubtype_ALL to ensure the binary runs on every # PowerPC machine. @@ -103,7 +108,8 @@ residual-static: $(OBJS) $(CXX) $(LDFLAGS) -force_cpusubtype_ALL -o residual-static $(OBJS) \ -framework CoreMIDI \ $(OSX_STATIC_LIBS) \ - -lSystemStubs -lz + $(OSX_ZLIB) \ + -lSystemStubs # Special target to create a static linked binary for the iPhone iphone: $(OBJS) @@ -210,3 +216,8 @@ endif ifdef USE_ARM_SOUND_ASM DEFINES += -DUSE_ARM_SOUND_ASM endif + +ifdef USE_ARM_GFX_ASM +DEFINES += -DUSE_ARM_GFX_ASM +endif + diff --git a/sound/audiocd.cpp b/sound/audiocd.cpp index c9f2b0ac975..3a493c23204 100644 --- a/sound/audiocd.cpp +++ b/sound/audiocd.cpp @@ -60,24 +60,26 @@ void AudioCDManager::play(int track, int numLoops, int startFrame, int duration, char trackName[2][16]; sprintf(trackName[0], "track%d", track); sprintf(trackName[1], "track%02d", track); - Audio::AudioStream *stream = 0; + Audio::SeekableAudioStream *stream = 0; - for (int i = 0; !stream && i < 2; ++i) { - /* - FIXME: Seems numLoops == 0 and numLoops == 1 both indicate a single repetition, - while all other positive numbers indicate precisely the number of desired - repetitions. Finally, -1 means infinitely many - */ - // We multiply by 40 / 3 = 1000 / 75 to convert frames to milliseconds - stream = AudioStream::openStreamFile(trackName[i], startFrame * 40 / 3, duration * 40 / 3, (numLoops < 1) ? numLoops + 1 : numLoops); - } + for (int i = 0; !stream && i < 2; ++i) + stream = SeekableAudioStream::openStreamFile(trackName[i]); // Stop any currently playing emulated track _mixer->stopHandle(_handle); if (stream != 0) { + Timestamp start = Timestamp(0, startFrame, 75); + Timestamp end = duration ? Timestamp(0, startFrame + duration, 75) : stream->getLength(); + + /* + FIXME: Seems numLoops == 0 and numLoops == 1 both indicate a single repetition, + while all other positive numbers indicate precisely the number of desired + repetitions. Finally, -1 means infinitely many + */ _emulating = true; - _mixer->playInputStream(Audio::Mixer::kMusicSoundType, &_handle, stream); + _mixer->playInputStream(Mixer::kMusicSoundType, &_handle, + makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops)); } else { _emulating = false; if (!only_emulate) diff --git a/sound/audiostream.cpp b/sound/audiostream.cpp index 3d6cad9bb71..0e90358d3ff 100644 --- a/sound/audiostream.cpp +++ b/sound/audiostream.cpp @@ -26,23 +26,15 @@ #include "common/debug.h" #include "common/endian.h" #include "common/file.h" -#include "common/list.h" +#include "common/queue.h" #include "common/util.h" #include "sound/audiostream.h" +#include "sound/flac.h" #include "sound/mixer.h" #include "sound/mp3.h" +#include "sound/raw.h" #include "sound/vorbis.h" -#include "sound/flac.h" - - -// This used to be an inline template function, but -// buggy template function handling in MSVC6 forced -// us to go with the macro approach. So far this is -// the only template function that MSVC6 seemed to -// compile incorrectly. Knock on wood. -#define READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, ptr, isLE) \ - ((is16Bit ? (isLE ? READ_LE_UINT16(ptr) : READ_BE_UINT16(ptr)) : (*ptr << 8)) ^ (isUnsigned ? 0x8000 : 0)) namespace Audio { @@ -55,8 +47,7 @@ struct StreamFileFormat { * Pointer to a function which tries to open a file of type StreamFormat. * Return NULL in case of an error (invalid/nonexisting file). */ - AudioStream* (*openStreamFile)(Common::SeekableReadStream *stream, bool disposeAfterUse, - uint32 startTime, uint32 duration, uint numLoops); + SeekableAudioStream *(*openStreamFile)(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse); }; static const StreamFileFormat STREAM_FILEFORMATS[] = { @@ -75,8 +66,8 @@ static const StreamFileFormat STREAM_FILEFORMATS[] = { { NULL, NULL, NULL } // Terminator }; -AudioStream* AudioStream::openStreamFile(const Common::String &basename, uint32 startTime, uint32 duration, uint numLoops) { - AudioStream* stream = NULL; +SeekableAudioStream *SeekableAudioStream::openStreamFile(const Common::String &basename) { + SeekableAudioStream *stream = NULL; Common::File *fileHandle = new Common::File(); for (int i = 0; i < ARRAYSIZE(STREAM_FILEFORMATS)-1 && stream == NULL; ++i) { @@ -84,7 +75,7 @@ AudioStream* AudioStream::openStreamFile(const Common::String &basename, uint32 fileHandle->open(filename); if (fileHandle->isOpen()) { // Create the stream object - stream = STREAM_FILEFORMATS[i].openStreamFile(fileHandle, true, startTime, duration, numLoops); + stream = STREAM_FILEFORMATS[i].openStreamFile(fileHandle, DisposeAfterUse::YES); fileHandle = 0; break; } @@ -93,507 +84,288 @@ AudioStream* AudioStream::openStreamFile(const Common::String &basename, uint32 delete fileHandle; if (stream == NULL) { - debug(1, "AudioStream: Could not open compressed AudioFile %s", basename.c_str()); + debug(1, "SeekableAudioStream::openStreamFile: Could not open compressed AudioFile %s", basename.c_str()); } return stream; } #pragma mark - -#pragma mark --- LinearMemoryStream --- +#pragma mark --- LoopingAudioStream --- #pragma mark - -inline int32 calculatePlayTime(int rate, int samples) { - int32 seconds = samples / rate; - int32 milliseconds = (1000 * (samples % rate)) / rate; - return seconds * 1000 + milliseconds; +LoopingAudioStream::LoopingAudioStream(RewindableAudioStream *stream, uint loops, DisposeAfterUse::Flag disposeAfterUse) + : _parent(stream), _disposeAfterUse(disposeAfterUse), _loops(loops), _completeIterations(0) { } -/** - * A simple raw audio stream, purely memory based. It operates on a single - * block of data, which is passed to it upon creation. - * Optionally supports looping the sound. - * - * Design note: This code tries to be as efficient as possible (without - * resorting to assembly, that is). To this end, it is written as a template - * class. This way the compiler can create optimized code for each special - * case. This results in a total of 12 versions of the code being generated. - */ -template -class LinearMemoryStream : public AudioStream { -protected: - const byte *_ptr; - const byte *_end; - const byte *_loopPtr; - const byte *_loopEnd; +LoopingAudioStream::~LoopingAudioStream() { + if (_disposeAfterUse == DisposeAfterUse::YES) + delete _parent; +} + +int LoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) { + int samplesRead = _parent->readBuffer(buffer, numSamples); + + if (_parent->endOfStream()) { + ++_completeIterations; + if (_completeIterations == _loops) + return samplesRead; + + int remainingSamples = numSamples - samplesRead; + + if (!_parent->rewind()) { + // TODO: Properly indicate error + _loops = _completeIterations = 1; + return samplesRead; + } + + samplesRead += _parent->readBuffer(buffer + samplesRead, remainingSamples); + } + + return samplesRead; +} + +bool LoopingAudioStream::endOfData() const { + return (_loops != 0 && (_completeIterations == _loops)); +} + +AudioStream *makeLoopingAudioStream(RewindableAudioStream *stream, uint loops) { + if (loops != 1) + return new LoopingAudioStream(stream, loops); + else + return stream; +} + +AudioStream *makeLoopingAudioStream(SeekableAudioStream *stream, Timestamp start, Timestamp end, uint loops) { + if (!start.totalNumberOfFrames() && (!end.totalNumberOfFrames() || end == stream->getLength())) { + return makeLoopingAudioStream(stream, loops); + } else { + if (!end.totalNumberOfFrames()) + end = stream->getLength(); + + if (start >= end) { + warning("makeLoopingAudioStream: start (%d) >= end (%d)", start.msecs(), end.msecs()); + delete stream; + return 0; + } + + return makeLoopingAudioStream(new SubSeekableAudioStream(stream, start, end), loops); + } +} + +#pragma mark - +#pragma mark --- SubLoopingAudioStream --- +#pragma mark - + +SubLoopingAudioStream::SubLoopingAudioStream(SeekableAudioStream *stream, + uint loops, + const Timestamp loopStart, + const Timestamp loopEnd, + DisposeAfterUse::Flag disposeAfterUse) + : _parent(stream), _disposeAfterUse(disposeAfterUse), _loops(loops), + _pos(0, getRate() * (isStereo() ? 2 : 1)), + _loopStart(loopStart.convertToFramerate(getRate() * (isStereo() ? 2 : 1))), + _loopEnd(loopEnd.convertToFramerate(getRate() * (isStereo() ? 2 : 1))), + _done(false) { + if (!_parent->rewind()) + _done = true; +} + +SubLoopingAudioStream::~SubLoopingAudioStream() { + if (_disposeAfterUse == DisposeAfterUse::YES) + delete _parent; +} + +int SubLoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) { + int framesLeft = MIN(_loopEnd.frameDiff(_pos), numSamples); + int framesRead = _parent->readBuffer(buffer, framesLeft); + _pos = _pos.addFrames(framesRead); + + if (framesLeft < numSamples || framesRead < framesLeft) { + if (_loops != 0) { + --_loops; + if (!_loops) { + _done = true; + return framesRead; + } + } + + if (!_parent->seek(_loopStart)) { + _done = true; + return framesRead; + } + + _pos = _loopStart; + framesLeft = numSamples - framesLeft; + framesRead += _parent->readBuffer(buffer + framesRead, framesLeft); + + if (_parent->endOfStream()) + _done = true; + } + + return framesRead; +} + +#pragma mark - +#pragma mark --- SubSeekableAudioStream --- +#pragma mark - + +SubSeekableAudioStream::SubSeekableAudioStream(SeekableAudioStream *parent, const Timestamp start, const Timestamp end, DisposeAfterUse::Flag disposeAfterUse) + : _parent(parent), _disposeAfterUse(disposeAfterUse), + _start(start.convertToFramerate(getRate())), + _pos(0, getRate() * (isStereo() ? 2 : 1)), + _length((end - start).convertToFramerate(getRate() * (isStereo() ? 2 : 1))) { + + assert(_length.totalNumberOfFrames() % (isStereo() ? 2 : 1) == 0); + _parent->seek(_start); +} + +SubSeekableAudioStream::~SubSeekableAudioStream() { + if (_disposeAfterUse) + delete _parent; +} + +int SubSeekableAudioStream::readBuffer(int16 *buffer, const int numSamples) { + int framesLeft = MIN(_length.frameDiff(_pos), numSamples); + int framesRead = _parent->readBuffer(buffer, framesLeft); + _pos = _pos.addFrames(framesRead); + return framesRead; +} + +bool SubSeekableAudioStream::seek(const Timestamp &where) { + _pos = where.convertToFramerate(getRate()); + if (_pos > _length) { + _pos = _length; + return false; + } + + if (_parent->seek(_pos + _start)) { + return true; + } else { + _pos = _length; + return false; + } +} + +#pragma mark - +#pragma mark --- Queueing audio stream --- +#pragma mark - + + +void QueuingAudioStream::queueBuffer(byte *data, uint32 size, DisposeAfterUse::Flag disposeAfterUse, byte flags) { + AudioStream *stream = makeRawMemoryStream(data, size, disposeAfterUse, getRate(), flags, 0, 0); + queueAudioStream(stream, DisposeAfterUse::YES); +} + + +class QueuingAudioStreamImpl : public QueuingAudioStream { +private: + /** + * We queue a number of (pointers to) audio stream objects. + * In addition, we need to remember for each stream whether + * to dispose it after all data has been read from it. + * Hence, we don't store pointers to stream objects directly, + * but rather StreamHolder structs. + */ + struct StreamHolder { + AudioStream *_stream; + DisposeAfterUse::Flag _disposeAfterUse; + StreamHolder(AudioStream *stream, DisposeAfterUse::Flag disposeAfterUse) + : _stream(stream), + _disposeAfterUse(disposeAfterUse) {} + }; + + /** + * The sampling rate of this audio stream. + */ const int _rate; - const byte *_origPtr; - const int32 _playtime; -public: - LinearMemoryStream(int rate, const byte *ptr, uint len, uint loopOffset, uint loopLen, bool autoFreeMemory) - : _ptr(ptr), _end(ptr+len), _loopPtr(0), _loopEnd(0), _rate(rate), _playtime(calculatePlayTime(rate, len / (is16Bit ? 2 : 1) / (stereo ? 2 : 1))) { + /** + * Whether this audio stream is mono (=false) or stereo (=true). + */ + const int _stereo; - if (loopLen) { - _loopPtr = _ptr + loopOffset; - _loopEnd = _loopPtr + loopLen; - } + /** + * This flag is set by the finish() method only. See there for more details. + */ + bool _finished; - _origPtr = autoFreeMemory ? ptr : 0; - } - virtual ~LinearMemoryStream() { - free(const_cast(_origPtr)); - } - int readBuffer(int16 *buffer, const int numSamples); - - bool isStereo() const { return stereo; } - bool endOfData() const { return _ptr >= _end; } - - int getRate() const { return _rate; } - int32 getTotalPlayTime() const { return _playtime; } -}; - -template -int LinearMemoryStream::readBuffer(int16 *buffer, const int numSamples) { - int samples = numSamples; - while (samples > 0 && _ptr < _end) { - int len = MIN(samples, (int)(_end - _ptr) / (is16Bit ? 2 : 1)); - samples -= len; - do { - *buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE); - _ptr += (is16Bit ? 2 : 1); - } while (--len); - // Loop, if looping was specified - if (_loopPtr && _ptr >= _end) { - _ptr = _loopPtr; - _end = _loopEnd; - } - } - return numSamples-samples; -} - - - -#pragma mark - -#pragma mark --- LinearDiskStream --- -#pragma mark - - - - -/** - * LinearDiskStream. This can stream linear (PCM) audio from disk. The - * function takes an pointer to an array of LinearDiskStreamAudioBlock which defines the - * start position and length of each block of uncompressed audio in the stream. - */ -template -class LinearDiskStream : public AudioStream { - -// Allow backends to override buffer size -#ifdef CUSTOM_AUDIO_BUFFER_SIZE - static const int32 BUFFER_SIZE = CUSTOM_AUDIO_BUFFER_SIZE; -#else - static const int32 BUFFER_SIZE = 16384; -#endif - -protected: - byte* _buffer; ///< Streaming buffer - const byte *_ptr; ///< Pointer to current position in stream buffer - const int _rate; ///< Sample rate of stream - - int32 _playtime; ///< Calculated total play time - Common::SeekableReadStream *_stream; ///< Stream to read data from - int32 _filePos; ///< Current position in stream - int32 _diskLeft; ///< Samples left in stream in current block not yet read to buffer - int32 _bufferLeft; ///< Samples left in buffer in current block - bool _disposeAfterUse; ///< If true, delete stream object when LinearDiskStream is destructed - - LinearDiskStreamAudioBlock *_audioBlock; ///< Audio block list - int _audioBlockCount; ///< Number of blocks in _audioBlock - int _currentBlock; ///< Current audio block number - - int _beginLoop; ///< Loop start parameter - int _endLoop; ///< Loop end parameter, currently not implemented - bool _loop; ///< Determines if the stream should be looped when it finishes - -public: - LinearDiskStream(int rate, uint beginLoop, uint endLoop, bool disposeStream, Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, uint numBlocks, bool loop) - : _rate(rate), _stream(stream), _beginLoop(beginLoop), _endLoop(endLoop), _disposeAfterUse(disposeStream), - _audioBlockCount(numBlocks), _loop(loop) { - - // Allocate streaming buffer - if (is16Bit) { - _buffer = (byte *)malloc(BUFFER_SIZE * sizeof(int16)); - } else { - _buffer = (byte *)malloc(BUFFER_SIZE * sizeof(byte)); - } - - _ptr = _buffer; - _bufferLeft = 0; - - // Copy audio block data to our buffer - // TODO: Replace this with a Common::Array or Common::List to - // make it a little friendlier. - _audioBlock = new LinearDiskStreamAudioBlock[numBlocks]; - memcpy(_audioBlock, block, numBlocks * sizeof(LinearDiskStreamAudioBlock)); - - // Set current buffer state, playing first block - _currentBlock = 0; - _filePos = _audioBlock[_currentBlock].pos; - _diskLeft = _audioBlock[_currentBlock].len; - - // Add up length of all blocks in order to caluclate total play time - int len = 0; - for (int r = 0; r < _audioBlockCount; r++) { - len += _audioBlock[r].len; - } - _playtime = calculatePlayTime(rate, len / (is16Bit ? 2 : 1) / (stereo ? 2 : 1)); - } - - - virtual ~LinearDiskStream() { - if (_disposeAfterUse) { - delete _stream; - } - - delete[] _audioBlock; - free(_buffer); - } - int readBuffer(int16 *buffer, const int numSamples); - - bool isStereo() const { return stereo; } - bool endOfData() const { return (_currentBlock == _audioBlockCount - 1) && (_diskLeft == 0) && (_bufferLeft == 0); } - - int getRate() const { return _rate; } - int32 getTotalPlayTime() const { return _playtime; } -}; - -template -int LinearDiskStream::readBuffer(int16 *buffer, const int numSamples) { - int oldPos = _stream->pos(); - bool restoreFilePosition = false; - - int samples = numSamples; - - while (samples > 0 && ((_diskLeft > 0 || _bufferLeft > 0) || (_currentBlock != _audioBlockCount - 1)) ) { - - // Output samples in the buffer to the output - int len = MIN(samples, _bufferLeft); - samples -= len; - _bufferLeft -= len; - - while (len > 0) { - *buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE); - _ptr += (is16Bit ? 2 : 1); - len--; - } - - // Have we now finished this block? If so, read the next block - if ((_bufferLeft == 0) && (_diskLeft == 0) && (_currentBlock != _audioBlockCount - 1)) { - // Next block - _currentBlock++; - - _filePos = _audioBlock[_currentBlock].pos; - _diskLeft = _audioBlock[_currentBlock].len; - } - - // Now read more data from disk if there is more to be read - if ((_bufferLeft == 0) && (_diskLeft > 0)) { - int32 readAmount = MIN(_diskLeft, BUFFER_SIZE); - - _stream->seek(_filePos, SEEK_SET); - _stream->read(_buffer, readAmount * (is16Bit? 2: 1)); - - // Amount of data in buffer is now the amount read in, and - // the amount left to read on disk is decreased by the same amount - _bufferLeft = readAmount; - _diskLeft -= readAmount; - _ptr = (byte *)_buffer; - _filePos += readAmount * (is16Bit? 2: 1); - - // Set this flag now we've used the file, it restores it's - // original position. - restoreFilePosition = true; - } - - // Looping - if (_diskLeft == 0 && _loop) { - // Reset the stream - _currentBlock = 0; - _filePos = _audioBlock[_currentBlock].pos + _beginLoop; - _diskLeft = _audioBlock[_currentBlock].len; - } - } - - // In case calling code relies on the position of this stream staying - // constant, I restore the location if I've changed it. This is probably - // not necessary. - if (restoreFilePosition) { - _stream->seek(oldPos, SEEK_SET); - } - - return numSamples-samples; -} - - - -#pragma mark - -#pragma mark --- Input stream factory --- -#pragma mark - - -/* In the following, we use preprocessor / macro tricks to simplify the code - * which instantiates the input streams. We used to use template functions for - * this, but MSVC6 / EVC 3-4 (used for WinCE builds) are extremely buggy when it - * comes to this feature of C++... so as a compromise we use macros to cut down - * on the (source) code duplication a bit. - * So while normally macro tricks are said to make maintenance harder, in this - * particular case it should actually help it :-) - */ - -#define MAKE_LINEAR(STEREO, UNSIGNED) \ - if (is16Bit) { \ - if (isLE) \ - return new LinearMemoryStream(rate, ptr, len, loopOffset, loopLen, autoFree); \ - else \ - return new LinearMemoryStream(rate, ptr, len, loopOffset, loopLen, autoFree); \ - } else \ - return new LinearMemoryStream(rate, ptr, len, loopOffset, loopLen, autoFree) - -AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte flags, uint loopStart, uint loopEnd) { - const bool isStereo = (flags & Audio::Mixer::FLAG_STEREO) != 0; - const bool is16Bit = (flags & Audio::Mixer::FLAG_16BITS) != 0; - const bool isUnsigned = (flags & Audio::Mixer::FLAG_UNSIGNED) != 0; - const bool isLE = (flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0; - const bool autoFree = (flags & Audio::Mixer::FLAG_AUTOFREE) != 0; - - - uint loopOffset = 0, loopLen = 0; - if (flags & Audio::Mixer::FLAG_LOOP) { - if (loopEnd == 0) - loopEnd = len; - assert(loopStart <= loopEnd); - assert(loopEnd <= len); - - loopOffset = loopStart; - loopLen = loopEnd - loopStart; - } - - // Verify the buffer sizes are sane - if (is16Bit && isStereo) { - assert((len & 3) == 0 && (loopLen & 3) == 0); - } else if (is16Bit || isStereo) { - assert((len & 1) == 0 && (loopLen & 1) == 0); - } - - if (isStereo) { - if (isUnsigned) { - MAKE_LINEAR(true, true); - } else { - MAKE_LINEAR(true, false); - } - } else { - if (isUnsigned) { - MAKE_LINEAR(false, true); - } else { - MAKE_LINEAR(false, false); - } - } -} - - - - - -#define MAKE_LINEAR_DISK(STEREO, UNSIGNED) \ - if (is16Bit) { \ - if (isLE) \ - return new LinearDiskStream(rate, loopStart, loopEnd, takeOwnership, stream, block, numBlocks, loop); \ - else \ - return new LinearDiskStream(rate, loopStart, loopEnd, takeOwnership, stream, block, numBlocks, loop); \ - } else \ - return new LinearDiskStream(rate, loopStart, loopEnd, takeOwnership, stream, block, numBlocks, loop) - - -AudioStream *makeLinearDiskStream(Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, int numBlocks, int rate, byte flags, bool takeOwnership, uint loopStart, uint loopEnd) { - const bool isStereo = (flags & Audio::Mixer::FLAG_STEREO) != 0; - const bool is16Bit = (flags & Audio::Mixer::FLAG_16BITS) != 0; - const bool isUnsigned = (flags & Audio::Mixer::FLAG_UNSIGNED) != 0; - const bool isLE = (flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0; - const bool loop = (flags & Audio::Mixer::FLAG_LOOP) != 0; - - if (isStereo) { - if (isUnsigned) { - MAKE_LINEAR_DISK(true, true); - } else { - MAKE_LINEAR_DISK(true, false); - } - } else { - if (isUnsigned) { - MAKE_LINEAR_DISK(false, true); - } else { - MAKE_LINEAR_DISK(false, false); - } - } -} - - - - -#pragma mark - -#pragma mark --- Appendable audio stream --- -#pragma mark - - -struct Buffer { - byte *start; - byte *end; -}; - -/** - * Wrapped memory stream. - */ -class BaseAppendableMemoryStream : public AppendableAudioStream { -protected: - - // A mutex to avoid access problems (causing e.g. corruption of - // the linked list) in thread aware environments. + /** + * A mutex to avoid access problems (causing e.g. corruption of + * the linked list) in thread aware environments. + */ Common::Mutex _mutex; - // List of all queued buffers - Common::List _bufferQueue; + /** + * The queue of audio streams. + */ + Common::Queue _queue; - // Position in the front buffer, if any - bool _finalized; - const int _rate; - byte *_pos; - - inline bool eosIntern() const { return _bufferQueue.empty(); }; public: - BaseAppendableMemoryStream(int rate); - ~BaseAppendableMemoryStream(); + QueuingAudioStreamImpl(int rate, bool stereo) + : _rate(rate), _stereo(stereo), _finished(false) {} + ~QueuingAudioStreamImpl(); - bool endOfStream() const { return _finalized && eosIntern(); } - bool endOfData() const { return eosIntern(); } + // Implement the AudioStream API + virtual int readBuffer(int16 *buffer, const int numSamples); + virtual bool isStereo() const { return _stereo; } + virtual int getRate() const { return _rate; } + virtual bool endOfData() const { + //Common::StackLock lock(_mutex); + return _queue.empty(); + } + virtual bool endOfStream() const { return _finished && _queue.empty(); } - int getRate() const { return _rate; } + // Implement the QueuingAudioStream API + virtual void queueAudioStream(AudioStream *stream, DisposeAfterUse::Flag disposeAfterUse); + virtual void finish() { _finished = true; } - void finish() { _finalized = true; } - - void queueBuffer(byte *data, uint32 size); + uint32 numQueuedStreams() const { + //Common::StackLock lock(_mutex); + return _queue.size(); + } }; -/** - * Wrapped memory stream. - */ -template -class AppendableMemoryStream : public BaseAppendableMemoryStream { -public: - AppendableMemoryStream(int rate) : BaseAppendableMemoryStream(rate) {} - - bool isStereo() const { return stereo; } - - int readBuffer(int16 *buffer, const int numSamples); -}; - -BaseAppendableMemoryStream::BaseAppendableMemoryStream(int rate) - : _finalized(false), _rate(rate), _pos(0) { - +QueuingAudioStreamImpl::~QueuingAudioStreamImpl() { + while (!_queue.empty()) { + StreamHolder tmp = _queue.pop(); + if (tmp._disposeAfterUse == DisposeAfterUse::YES) + delete tmp._stream; + } } -BaseAppendableMemoryStream::~BaseAppendableMemoryStream() { - // Clear the queue - Common::List::iterator iter; - for (iter = _bufferQueue.begin(); iter != _bufferQueue.end(); ++iter) - delete[] iter->start; -} +void QueuingAudioStreamImpl::queueAudioStream(AudioStream *stream, DisposeAfterUse::Flag disposeAfterUse) { + if ((stream->getRate() != getRate()) || (stream->isStereo() != isStereo())) + error("QueuingAudioStreamImpl::queueAudioStream: stream has mismatched parameters"); -template -int AppendableMemoryStream::readBuffer(int16 *buffer, const int numSamples) { Common::StackLock lock(_mutex); - int samples = numSamples; - while (samples > 0 && !eosIntern()) { - Buffer buf = *_bufferQueue.begin(); - if (_pos == 0) - _pos = buf.start; - - assert(buf.start <= _pos && _pos <= buf.end); - const int samplesLeftInCurBuffer = buf.end - _pos; - if (samplesLeftInCurBuffer == 0) { - delete[] buf.start; - _bufferQueue.erase(_bufferQueue.begin()); - _pos = 0; - continue; - } - - int len = MIN(samples, samplesLeftInCurBuffer / (is16Bit ? 2 : 1)); - samples -= len; - do { - *buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _pos, isLE); - _pos += (is16Bit ? 2 : 1); - } while (--len); - } - - return numSamples - samples; + _queue.push(StreamHolder(stream, disposeAfterUse)); } -void BaseAppendableMemoryStream::queueBuffer(byte *data, uint32 size) { +int QueuingAudioStreamImpl::readBuffer(int16 *buffer, const int numSamples) { Common::StackLock lock(_mutex); + int samplesDecoded = 0; -/* - // Verify the buffer size is sane - if (is16Bit && stereo) { - assert((size & 3) == 0); - } else if (is16Bit || stereo) { - assert((size & 1) == 0); + while (samplesDecoded < numSamples && !_queue.empty()) { + AudioStream *stream = _queue.front()._stream; + samplesDecoded += stream->readBuffer(buffer + samplesDecoded, numSamples - samplesDecoded); + + if (stream->endOfData() ) { + StreamHolder tmp = _queue.pop(); + if (tmp._disposeAfterUse == DisposeAfterUse::YES) + delete stream; + } } -*/ - // Verify that the stream has not yet been finalized (by a call to finish()) - assert(!_finalized); - // Queue the buffer - Buffer buf = {data, data+size}; - _bufferQueue.push_back(buf); - - -#if 0 - // Output some stats - uint totalSize = 0; - Common::List::iterator iter; - for (iter = _bufferQueue.begin(); iter != _bufferQueue.end(); ++iter) - totalSize += iter->end - iter->start; - printf("AppendableMemoryStream::queueBuffer: added a %d byte buf, a total of %d bytes are queued\n", - size, totalSize); -#endif + return samplesDecoded; } -#define MAKE_WRAPPED(STEREO, UNSIGNED) \ - if (is16Bit) { \ - if (isLE) \ - return new AppendableMemoryStream(rate); \ - else \ - return new AppendableMemoryStream(rate); \ - } else \ - return new AppendableMemoryStream(rate) -AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags) { - const bool isStereo = (_flags & Audio::Mixer::FLAG_STEREO) != 0; - const bool is16Bit = (_flags & Audio::Mixer::FLAG_16BITS) != 0; - const bool isUnsigned = (_flags & Audio::Mixer::FLAG_UNSIGNED) != 0; - const bool isLE = (_flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0; - - if (isStereo) { - if (isUnsigned) { - MAKE_WRAPPED(true, true); - } else { - MAKE_WRAPPED(true, false); - } - } else { - if (isUnsigned) { - MAKE_WRAPPED(false, true); - } else { - MAKE_WRAPPED(false, false); - } - } +QueuingAudioStream *makeQueuingAudioStream(int rate, bool stereo) { + return new QueuingAudioStreamImpl(rate, stereo); } + } // End of namespace Audio diff --git a/sound/audiostream.h b/sound/audiostream.h index 74cea62e8d9..9709ef155bb 100644 --- a/sound/audiostream.h +++ b/sound/audiostream.h @@ -28,10 +28,14 @@ #include "common/util.h" #include "common/sys.h" -#include "common/stream.h" +#include "common/types.h" + +#include "sound/timestamp.h" namespace Audio { +class SeekableAudioStream; + /** * Generic audio input stream. Subclasses of this are used to feed arbitrary * sampled audio data into ScummVM's audio mixer. @@ -41,18 +45,17 @@ public: virtual ~AudioStream() {} /** - * Fill the given buffer with up to numSamples samples. - * Returns the actual number of samples read, or -1 if - * a critical error occured (note: you *must* check if - * this value is less than what you requested, this can + * Fill the given buffer with up to numSamples samples. Returns the actual + * number of samples read, or -1 if a critical error occured (note: you + * *must* check if this value is less than what you requested, this can * happen when the stream is fully used up). * - * Data has to be in native endianess, 16 bit per sample, signed. - * For stereo stream, buffer will be filled with interleaved - * left and right channel samples, starting with a left sample. - * Furthermore, the samples in the left and right are summed up. - * So if you request 4 samples from a stereo stream, you will get - * a total of two left channel and two right channel samples. + * Data has to be in native endianess, 16 bit per sample, signed. For stereo + * stream, buffer will be filled with interleaved left and right channel + * samples, starting with a left sample. Furthermore, the samples in the + * left and right are summed up. So if you request 4 samples from a stereo + * stream, you will get a total of two left channel and two right channel + * samples. */ virtual int readBuffer(int16 *buffer, const int numSamples) = 0; @@ -80,93 +83,270 @@ public: * By default this maps to endOfData() */ virtual bool endOfStream() const { return endOfData(); } +}; +/** + * A rewindable audio stream. This allows for restting the AudioStream + * to its initial state. Note that rewinding itself is not required to + * be working when the stream is being played by Mixer! + */ +class RewindableAudioStream : public AudioStream { +public: + /** + * Rewinds the stream to its start. + * + * @return true on success, false otherwise. + */ + virtual bool rewind() = 0; +}; + +/** + * A looping audio stream. This object does nothing beides using + * a RewindableAudioStream to play a stream in a loop. + */ +class LoopingAudioStream : public AudioStream { +public: + /** + * Creates a looping audio stream object. + * + * @see makeLoopingAudioStream + * + * @param stream Stream to loop + * @param loops How often to loop (0 = infinite) + * @param disposeAfteruse Destroy the stream after the LoopingAudioStream has finished playback. + */ + LoopingAudioStream(RewindableAudioStream *stream, uint loops, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + ~LoopingAudioStream(); + + int readBuffer(int16 *buffer, const int numSamples); + bool endOfData() const; + + bool isStereo() const { return _parent->isStereo(); } + int getRate() const { return _parent->getRate(); } + + /** + * Returns number of loops the stream has played. + * @param numLoops number of loops to play, 0 - infinite + */ + uint getCompleteIterations() const { return _completeIterations; } +private: + RewindableAudioStream *_parent; + DisposeAfterUse::Flag _disposeAfterUse; + + uint _loops; + uint _completeIterations; +}; + +/** + * Wrapper functionallity to efficiently create a stream, which might be looped. + * + * Note that this function does not return a LoopingAudioStream, because it does + * not create one, when the loop count is "1". This allows to keep the runtime + * overhead down, when the code does not require any functionallity only offered + * by LoopingAudioStream. + * + * @param stream Stream to loop (will be automatically destroyed, when the looping is done) + * @param loops How often to loop (0 = infinite) + * @return A new AudioStream, which offers the desired functionallity. + */ +AudioStream *makeLoopingAudioStream(RewindableAudioStream *stream, uint loops); + +/** + * A seekable audio stream. Subclasses of this class implement an + * interface for seeking. The seeking itself is not required to be + * working while the stream is being played by Mixer! + */ +class SeekableAudioStream : public RewindableAudioStream { +public: /** * Tries to load a file by trying all available formats. * In case of an error, the file handle will be closed, but deleting * it is still the responsibilty of the caller. * @param basename a filename without an extension - * @param startTime the (optional) time offset in milliseconds from which to start playback - * @param duration the (optional) time in milliseconds specifying how long to play - * @param numLoops how often the data shall be looped (0 = infinite) - * @return an Audiostream ready to use in case of success; + * @return an SeekableAudioStream ready to use in case of success; * NULL in case of an error (e.g. invalid/nonexisting file) */ - static AudioStream* openStreamFile(const Common::String &basename, uint32 startTime = 0, uint32 duration = 0, uint numLoops = 1); - - enum { - kUnknownPlayTime = -1 - }; + static SeekableAudioStream *openStreamFile(const Common::String &basename); /** - * Returns total playtime of the AudioStream object. - * Note that this does not require to return an playtime, if the - * playtime of the AudioStream is unknown it returns 'kUnknownPlayTime'. - * @see kUnknownPlayTime + * Seeks to a given offset in the stream. * - * @return playtime in milliseconds + * @param where offset in milliseconds + * @return true on success, false on failure. */ - virtual int32 getTotalPlayTime() const { return kUnknownPlayTime; } + bool seek(uint32 where) { + return seek(Timestamp(where, getRate())); + } + + /** + * Seeks to a given offset in the stream. + * + * @param where offset as timestamp + * @return true on success, false on failure. + */ + virtual bool seek(const Timestamp &where) = 0; + + /** + * Returns the length of the stream. + * + * @return length as Timestamp. + */ + virtual Timestamp getLength() const = 0; + + virtual bool rewind() { return seek(0); } }; +/** + * Wrapper functionallity to efficiently create a stream, which might be looped + * in a certain interval. + * + * This automatically starts the stream at time "start"! + * + * Note that this function does not return a LoopingAudioStream, because it does + * not create one, when the loop count is "1". This allows to keep the runtime + * overhead down, when the code does not require any functionallity only offered + * by LoopingAudioStream. + * + * @param stream Stream to loop (will be automatically destroyed, when the looping is done) + * @param start Starttime of the stream interval to be looped + * @param end End of the stream interval to be looped (a zero time, means till end) + * @param loops How often to loop (0 = infinite) + * @return A new AudioStream, which offers the desired functionallity. + */ +AudioStream *makeLoopingAudioStream(SeekableAudioStream *stream, Timestamp start, Timestamp end, uint loops); /** - * Factory function for a raw linear AudioStream, which will simply treat all data - * in the buffer described by ptr and len as raw sample data in the specified - * format. It will then simply pass this data directly to the mixer, after converting - * it to the sample format used by the mixer (i.e. 16 bit signed native endian). - * Optionally supports (infinite) looping of a portion of the data. + * A looping audio stream, which features looping of a nested part of the + * stream. + * + * NOTE: + * Currently this implementation stops after the nested loop finished + * playback. + * + * IMPORTANT: + * This might be merged with SubSeekableAudioStream for playback purposes. + * (After extending it to accept a start time). */ -AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte flags, uint loopStart, uint loopEnd); +class SubLoopingAudioStream : public AudioStream { +public: + SubLoopingAudioStream(SeekableAudioStream *stream, uint loops, + const Timestamp loopStart, + const Timestamp loopEnd, + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + ~SubLoopingAudioStream(); + int readBuffer(int16 *buffer, const int numSamples); + bool endOfData() const { return _done; } -/** Struct used to define the audio data to be played by a LinearDiskStream */ + bool isStereo() const { return _parent->isStereo(); } + int getRate() const { return _parent->getRate(); } +private: + SeekableAudioStream *_parent; + DisposeAfterUse::Flag _disposeAfterUse; -struct LinearDiskStreamAudioBlock { - int32 pos; ///< Position in stream of the block - int32 len; ///< Length of the block (in samples) + uint _loops; + Timestamp _pos; + Timestamp _loopStart, _loopEnd; + + bool _done; }; - -/** Factory function for a Linear Disk Stream. This can stream linear (PCM) audio from disk. The - * function takes an pointer to an array of LinearDiskStreamAudioBlock which defines the - * start position and length of each block of uncompressed audio in the stream. - */ - -AudioStream *makeLinearDiskStream(Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, int - numBlocks, int rate, byte flags, bool disposeStream, uint loopStart, uint loopEnd); - /** - * An audio stream to which additional data can be appended on-the-fly. - * Used by SMUSH, iMuseDigital, the Kyrandia 3 VQA player, etc. + * A SubSeekableAudioStream provides access to a SeekableAudioStream + * just in the range [start, end). + * The same caveats apply to SubSeekableAudioStream as do to SeekableAudioStream. + * + * Manipulating the parent stream directly /will/ mess up a substream. + * + * IMPORTANT: + * Note for engine authors. This object is currently under inspection. In case + * we need to revise the looping API we might drop this. So if you really need + * something like this object, please drop a mail to LordHoto. */ -class AppendableAudioStream : public Audio::AudioStream { +class SubSeekableAudioStream : public SeekableAudioStream { +public: + /** + * Creates a new SubSeekableAudioStream. + * + * @param parent parent stream object. + * @param start Start time. + * @param end End time. + * @param disposeAfterUse Whether the parent stream object should be destroyed on destruction of the SubSeekableAudioStream. + */ + SubSeekableAudioStream(SeekableAudioStream *parent, const Timestamp start, const Timestamp end, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + ~SubSeekableAudioStream(); + + int readBuffer(int16 *buffer, const int numSamples); + + bool isStereo() const { return _parent->isStereo(); } + + int getRate() const { return _parent->getRate(); } + + bool endOfData() const { return (_pos >= _length) || _parent->endOfStream(); } + + bool seek(const Timestamp &where); + + Timestamp getLength() const { return _length; } +private: + SeekableAudioStream *_parent; + DisposeAfterUse::Flag _disposeAfterUse; + + const Timestamp _start; + const Timestamp _length; + Timestamp _pos; +}; + +class QueuingAudioStream : public Audio::AudioStream { public: /** - * Queue another audio data buffer for playback. The stream - * will playback all queued buffers, in the order they were + * Queue an audio stream for playback. This stream will + * play all queued streams, in the order they were queued. + * If the disposeAfterUse is true, then the stream is + * deleted after all data contained in it has been played. + */ + virtual void queueAudioStream(Audio::AudioStream *audStream, + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES) = 0; + + /** + * Queue a block of raw audio data for playback. This stream + * will play all queued buffers, in the order they were * queued. After all data contained in them has been played, * the buffer will be delete[]'d (so make sure to allocate them * with new[], not with malloc). */ - virtual void queueBuffer(byte *data, uint32 size) = 0; + void queueBuffer(byte *data, uint32 size, DisposeAfterUse::Flag disposeAfterUse, byte flags); /** * Mark the stream as finished, that is, signal that no further data - * will be appended to it. Only after this has been done can the - * AppendableAudioStream ever 'end' + * will be appended to it. Only after this has been done can this + * stream ever 'end'. */ virtual void finish() = 0; + + /** + * Return the number of streams still queued for playback (including + * the currently playing stream). + */ + virtual uint32 numQueuedStreams() const = 0; }; /** - * Factory function for an AppendableAudioStream. The rate and flags - * parameters are analog to those used in makeLinearInputStream. + * Factory function for an QueuingAudioStream. */ -AppendableAudioStream *makeAppendableAudioStream(int rate, byte flags); +QueuingAudioStream *makeQueuingAudioStream(int rate, bool stereo); +/** + * Calculates the sample, which the timestamp describes in a + * AudioStream with the given framerate. + * + * @param where point in time + * @param rate rate of the AudioStream + * @return sample index + */ +uint32 calculateSampleOffset(const Timestamp &where, int rate); + } // End of namespace Audio #endif diff --git a/sound/flac.cpp b/sound/flac.cpp index 931219dfb0f..cb689a5652b 100644 --- a/sound/flac.cpp +++ b/sound/flac.cpp @@ -82,26 +82,21 @@ namespace Audio { static const uint MAX_OUTPUT_CHANNELS = 2; -class FlacInputStream : public AudioStream { +class FlacInputStream : public SeekableAudioStream { protected: Common::SeekableReadStream *_inStream; bool _disposeAfterUse; - uint _numLoops; - ::FLAC__SeekableStreamDecoder *_decoder; /** Header of the stream */ FLAC__StreamMetadata_StreamInfo _streaminfo; - /** index of the first sample to be played */ - FLAC__uint64 _firstSample; - - /** index + 1(!) of the last sample to be played - 0 is end of stream */ + /** index + 1(!) of the last sample to be played */ FLAC__uint64 _lastSample; /** total play time */ - int32 _totalPlayTime; + Timestamp _length; /** true if the last sample was decoded from the FLAC-API - there might still be data in the buffer */ bool _lastSampleWritten; @@ -129,7 +124,7 @@ protected: public: - FlacInputStream(Common::SeekableReadStream *inStream, bool dispose, uint startTime = 0, uint endTime = 0, uint numLoops = 1); + FlacInputStream(Common::SeekableReadStream *inStream, bool dispose); virtual ~FlacInputStream(); int readBuffer(int16 *buffer, const int numSamples); @@ -142,10 +137,10 @@ public: return _streaminfo.channels == 0 || (_lastSampleWritten && _sampleCache.bufFill == 0); } - int32 getTotalPlayTime() const { return _totalPlayTime; } + bool seek(const Timestamp &where); + Timestamp getLength() const { return _length; } bool isStreamDecoderReady() const { return getStreamDecoderState() == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC ; } - protected: uint getChannels() const { return MIN(_streaminfo.channels, MAX_OUTPUT_CHANNELS); } @@ -184,7 +179,7 @@ private: static void convertBuffersMono8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits); }; -FlacInputStream::FlacInputStream(Common::SeekableReadStream *inStream, bool dispose, uint startTime, uint endTime, uint numLoops) +FlacInputStream::FlacInputStream(Common::SeekableReadStream *inStream, bool dispose) #ifdef LEGACY_FLAC : _decoder(::FLAC__seekable_stream_decoder_new()), #else @@ -192,8 +187,7 @@ FlacInputStream::FlacInputStream(Common::SeekableReadStream *inStream, bool disp #endif _inStream(inStream), _disposeAfterUse(dispose), - _numLoops(numLoops), - _firstSample(0), _lastSample(0), + _length(0, 1000), _lastSample(0), _outBuffer(NULL), _requestedSamples(0), _lastSampleWritten(false), _methodConvertBuffers(&FlacInputStream::convertBuffersGeneric) { @@ -234,34 +228,9 @@ FlacInputStream::FlacInputStream(Common::SeekableReadStream *inStream, bool disp #endif if (success) { if (processUntilEndOfMetadata() && _streaminfo.channels > 0) { - // Compute the start/end sample (we use floating point arithmetics here to - // avoid overflows). - _firstSample = (FLAC__uint64)(startTime * (_streaminfo.sample_rate / 1000.0)); - _lastSample = (FLAC__uint64)(endTime * (_streaminfo.sample_rate / 1000.0)); - - if (_firstSample == 0 || seekAbsolute(_firstSample)) { - int32 samples = kUnknownPlayTime; - - if (!_lastSample) { - if (_streaminfo.total_samples) - samples = _streaminfo.total_samples - _firstSample; - } else { - samples = _lastSample - _firstSample - 1; - } - - if (samples != kUnknownPlayTime && samples >= 0 && numLoops) { - const int32 rate = _streaminfo.sample_rate; - - int32 seconds = samples / rate; - int32 milliseconds = (1000 * (samples % rate)) / rate; - - _totalPlayTime = (seconds * 1000 + milliseconds) * numLoops; - } else { - _totalPlayTime = kUnknownPlayTime; - } - - return; // no error occured - } + _lastSample = _streaminfo.total_samples + 1; + _length = Timestamp(0, _lastSample - 1, getRate()); + return; // no error occured } } @@ -322,6 +291,12 @@ bool FlacInputStream::seekAbsolute(FLAC__uint64 sample) { return result; } +bool FlacInputStream::seek(const Timestamp &where) { + _sampleCache.bufFill = 0; + _sampleCache.bufReadPos = NULL; + return seekAbsolute((FLAC__uint64)calculateSampleOffset(where, _streaminfo.sample_rate)); +} + int FlacInputStream::readBuffer(int16 *buffer, const int numSamples) { const uint numChannels = getChannels(); @@ -365,17 +340,8 @@ int FlacInputStream::readBuffer(int16 *buffer, const int numSamples) { processSingleBlock(); state = getStreamDecoderState(); - if (state == FLAC__STREAM_DECODER_END_OF_STREAM) { + if (state == FLAC__STREAM_DECODER_END_OF_STREAM) _lastSampleWritten = true; - } - - // If we reached the end of the stream, and looping is enabled: Try to rewind - if (_lastSampleWritten && _numLoops != 1) { - if (_numLoops != 0) - _numLoops--; - seekAbsolute(_firstSample); - state = getStreamDecoderState(); - } } // Error handling @@ -758,22 +724,10 @@ void FlacInputStream::callWrapError(const ::FLAC__SeekableStreamDecoder *decoder #pragma mark --- Flac factory functions --- #pragma mark - - -AudioStream *makeFlacStream( +SeekableAudioStream *makeFlacStream( Common::SeekableReadStream *stream, - bool disposeAfterUse, - uint32 startTime, - uint32 duration, - uint numLoops) { - - uint32 endTime = duration ? (startTime + duration) : 0; - - FlacInputStream *input = new FlacInputStream(stream, disposeAfterUse, startTime, endTime, numLoops); - if (!input->isStreamDecoderReady()) { - delete input; - return 0; - } - return input; + DisposeAfterUse::Flag disposeAfterUse) { + return new FlacInputStream(stream, disposeAfterUse); } } // End of namespace Audio diff --git a/sound/flac.h b/sound/flac.h index d7156c9b336..8aaed21cc0f 100644 --- a/sound/flac.h +++ b/sound/flac.h @@ -24,6 +24,7 @@ */ /** + * @file * Sound decoder used in engines: * - agos * - kyra @@ -40,6 +41,7 @@ #ifndef SOUND_FLAC_H #define SOUND_FLAC_H +#include "common/types.h" #include "common/sys.h" #ifdef USE_FLAC @@ -51,26 +53,19 @@ namespace Common { namespace Audio { class AudioStream; +class SeekableAudioStream; /** - * Create a new AudioStream from the FLAC data in the given stream. - * Allows for looping (which is why we require a SeekableReadStream), - * and specifying only a portion of the data to be played, based - * on time offsets. + * Create a new SeekableAudioStream from the FLAC data in the given stream. + * Allows for seeking (which is why we require a SeekableReadStream). * * @param stream the SeekableReadStream from which to read the FLAC data * @param disposeAfterUse whether to delete the stream after use - * @param startTime the (optional) time offset in milliseconds from which to start playback - * @param duration the (optional) time in milliseconds specifying how long to play - * @param numLoops how often the data shall be looped (0 = infinite) - * @return a new AudioStream, or NULL, if an error occured + * @return a new SeekableAudioStream, or NULL, if an error occured */ -AudioStream *makeFlacStream( +SeekableAudioStream *makeFlacStream( Common::SeekableReadStream *stream, - bool disposeAfterUse, - uint32 startTime = 0, - uint32 duration = 0, - uint numLoops = 1); + DisposeAfterUse::Flag disposeAfterUse); } // End of namespace Audio diff --git a/sound/mixer.cpp b/sound/mixer.cpp index 18e8877095b..20746fcd6ba 100644 --- a/sound/mixer.cpp +++ b/sound/mixer.cpp @@ -29,6 +29,7 @@ #include "sound/mixer_intern.h" #include "sound/rate.h" #include "sound/audiostream.h" +#include "sound/timestamp.h" namespace Audio { @@ -39,71 +40,121 @@ namespace Audio { /** - * Channels used by the sound mixer. + * Channel used by the default Mixer implementation. */ class Channel { public: - const Mixer::SoundType _type; - SoundHandle _handle; + Channel(Mixer *mixer, Mixer::SoundType type, AudioStream *input, DisposeAfterUse::Flag autofreeStream, bool reverseStereo, int id, bool permanent); + ~Channel(); + + /** + * Mixes the channel's samples into the given buffer. + * + * @param data buffer where to mix the data + * @param len number of sample *pairs*. So a value of + * 10 means that the buffer contains twice 10 sample, each + * 16 bits, for a total of 40 bytes. + */ + void mix(int16 *data, uint len); + + /** + * Queries whether the channel is still playing or not. + */ + bool isFinished() const { return _input->endOfStream(); } + + /** + * Queries whether the channel is a permanent channel. + * A permanent channel is not affected by a Mixer::stopAll + * call. + */ + bool isPermanent() const { return _permanent; } + + /** + * Returns the id of the channel. + */ + int getId() const { return _id; } + + /** + * Pauses or unpaused the channel in a recursive fashion. + * + * @param paused true, when the channel should be paused. + * false when it should be unpaused. + */ + void pause(bool paused); + + /** + * Queries whether the channel is currently paused. + */ + bool isPaused() const { return (_pauseLevel != 0); } + + /** + * Sets the channel's own volume. + * + * @param volume new volume + */ + void setVolume(const byte volume); + + /** + * Sets the channel's balance setting. + * + * @param balance new balance + */ + void setBalance(const int8 balance); + + /** + * Notifies the channel that the global sound type + * volume settings changed. + */ + void notifyGlobalVolChange() { updateChannelVolumes(); } + + /** + * Queries how long the channel has been playing. + */ + Timestamp getElapsedTime(); + + /** + * Queries the channel's sound type. + */ + Mixer::SoundType getType() const { return _type; } + + /** + * Sets the channel's sound handle. + * + * @param handle new handle + */ + void setHandle(const SoundHandle handle) { _handle = handle; } + + /** + * Queries the channel's sound handle. + */ + SoundHandle getHandle() const { return _handle; } + private: - Mixer *_mixer; - bool _autofreeStream; + const Mixer::SoundType _type; + SoundHandle _handle; bool _permanent; - byte _volume; - int8 _balance; int _pauseLevel; int _id; + + byte _volume; + int8 _balance; + + void updateChannelVolumes(); + st_volume_t _volL, _volR; + + Mixer *_mixer; + uint32 _samplesConsumed; uint32 _samplesDecoded; uint32 _mixerTimeStamp; uint32 _pauseStartTime; uint32 _pauseTime; -protected: + DisposeAfterUse::Flag _autofreeStream; RateConverter *_converter; AudioStream *_input; - -public: - Channel(Mixer *mixer, Mixer::SoundType type, AudioStream *input, bool autofreeStream, bool reverseStereo = false, int id = -1, bool permanent = false); - virtual ~Channel(); - - void mix(int16 *data, uint len); - - bool isPermanent() const { - return _permanent; - } - bool isFinished() const { - return _input->endOfStream(); - } - void pause(bool paused) { - //assert((paused && _pauseLevel >= 0) || (!paused && _pauseLevel)); - - if (paused) - _pauseLevel++; - else if (_pauseLevel > 0) - _pauseLevel--; - - if (_pauseLevel > 0) - _pauseStartTime = g_system->getMillis(); - else - _pauseTime += (g_system->getMillis() - _pauseStartTime); - } - bool isPaused() { - return _pauseLevel != 0; - } - void setVolume(const byte volume) { - _volume = volume; - } - void setBalance(const int8 balance) { - _balance = balance; - } - int getId() const { - return _id; - } - uint32 getElapsedTime(); }; - #pragma mark - #pragma mark --- Mixer --- #pragma mark - @@ -141,7 +192,6 @@ void MixerImpl::setOutputRate(uint sampleRate) { } void MixerImpl::insertChannel(SoundHandle *handle, Channel *chan) { - int index = -1; for (int i = 0; i != NUM_CHANNELS; i++) { if (_channels[i] == 0) { @@ -156,26 +206,14 @@ void MixerImpl::insertChannel(SoundHandle *handle, Channel *chan) { } _channels[index] = chan; - chan->_handle._val = index + (_handleSeed * NUM_CHANNELS); + + SoundHandle chanHandle; + chanHandle._val = index + (_handleSeed * NUM_CHANNELS); + + chan->setHandle(chanHandle); _handleSeed++; - if (handle) { - *handle = chan->_handle; - } -} - -void MixerImpl::playRaw( - SoundType type, - SoundHandle *handle, - void *sound, - uint32 size, uint rate, byte flags, - int id, byte volume, int8 balance, - uint32 loopStart, uint32 loopEnd) { - - // Create the input stream - AudioStream *input = makeLinearInputStream((byte *)sound, size, rate, flags, loopStart, loopEnd); - - // Play it - playInputStream(type, handle, input, id, volume, balance, true, false, ((flags & Mixer::FLAG_REVERSE_STEREO) != 0)); + if (handle) + *handle = chanHandle; } void MixerImpl::playInputStream( @@ -183,7 +221,7 @@ void MixerImpl::playInputStream( SoundHandle *handle, AudioStream *input, int id, byte volume, int8 balance, - bool autofreeStream, + DisposeAfterUse::Flag autofreeStream, bool permanent, bool reverseStereo) { Common::StackLock lock(_mutex); @@ -197,7 +235,7 @@ void MixerImpl::playInputStream( if (id != -1) { for (int i = 0; i != NUM_CHANNELS; i++) if (_channels[i] != 0 && _channels[i]->getId() == id) { - if (autofreeStream) + if (autofreeStream == DisposeAfterUse::YES) delete input; return; } @@ -260,7 +298,7 @@ void MixerImpl::stopHandle(SoundHandle handle) { // Simply ignore stop requests for handles of sounds that already terminated const int index = handle._val % NUM_CHANNELS; - if (!_channels[index] || _channels[index]->_handle._val != handle._val) + if (!_channels[index] || _channels[index]->getHandle()._val != handle._val) return; delete _channels[index]; @@ -271,7 +309,7 @@ void MixerImpl::setChannelVolume(SoundHandle handle, byte volume) { Common::StackLock lock(_mutex); const int index = handle._val % NUM_CHANNELS; - if (!_channels[index] || _channels[index]->_handle._val != handle._val) + if (!_channels[index] || _channels[index]->getHandle()._val != handle._val) return; _channels[index]->setVolume(volume); @@ -281,18 +319,22 @@ void MixerImpl::setChannelBalance(SoundHandle handle, int8 balance) { Common::StackLock lock(_mutex); const int index = handle._val % NUM_CHANNELS; - if (!_channels[index] || _channels[index]->_handle._val != handle._val) + if (!_channels[index] || _channels[index]->getHandle()._val != handle._val) return; _channels[index]->setBalance(balance); } uint32 MixerImpl::getSoundElapsedTime(SoundHandle handle) { + return getElapsedTime(handle).msecs(); +} + +Timestamp MixerImpl::getElapsedTime(SoundHandle handle) { Common::StackLock lock(_mutex); const int index = handle._val % NUM_CHANNELS; - if (!_channels[index] || _channels[index]->_handle._val != handle._val) - return 0; + if (!_channels[index] || _channels[index]->getHandle()._val != handle._val) + return Timestamp(0, _sampleRate); return _channels[index]->getElapsedTime(); } @@ -321,7 +363,7 @@ void MixerImpl::pauseHandle(SoundHandle handle, bool paused) { // Simply ignore (un)pause requests for sounds that already terminated const int index = handle._val % NUM_CHANNELS; - if (!_channels[index] || _channels[index]->_handle._val != handle._val) + if (!_channels[index] || _channels[index]->getHandle()._val != handle._val) return; _channels[index]->pause(paused); @@ -338,7 +380,7 @@ bool MixerImpl::isSoundIDActive(int id) { int MixerImpl::getSoundID(SoundHandle handle) { Common::StackLock lock(_mutex); const int index = handle._val % NUM_CHANNELS; - if (_channels[index] && _channels[index]->_handle._val == handle._val) + if (_channels[index] && _channels[index]->getHandle()._val == handle._val) return _channels[index]->getId(); return 0; } @@ -346,13 +388,13 @@ int MixerImpl::getSoundID(SoundHandle handle) { bool MixerImpl::isSoundHandleActive(SoundHandle handle) { Common::StackLock lock(_mutex); const int index = handle._val % NUM_CHANNELS; - return _channels[index] && _channels[index]->_handle._val == handle._val; + return _channels[index] && _channels[index]->getHandle()._val == handle._val; } bool MixerImpl::hasActiveChannelOfType(SoundType type) { Common::StackLock lock(_mutex); for (int i = 0; i != NUM_CHANNELS; i++) - if (_channels[i] && _channels[i]->_type == type) + if (_channels[i] && _channels[i]->getType() == type) return true; return false; } @@ -369,7 +411,13 @@ void MixerImpl::setVolumeForSoundType(SoundType type, int volume) { // TODO: Maybe we should do logarithmic (not linear) volume // scaling? See also Player_V2::setMasterVolume + Common::StackLock lock(_mutex); _volumeForSoundType[type] = volume; + + for (int i = 0; i != NUM_CHANNELS; ++i) { + if (_channels[i] && _channels[i]->getType() == type) + _channels[i]->notifyGlobalVolChange(); + } } int MixerImpl::getVolumeForSoundType(SoundType type) const { @@ -383,13 +431,12 @@ int MixerImpl::getVolumeForSoundType(SoundType type) const { #pragma mark --- Channel implementations --- #pragma mark - - Channel::Channel(Mixer *mixer, Mixer::SoundType type, AudioStream *input, - bool autofreeStream, bool reverseStereo, int id, bool permanent) - : _type(type), _mixer(mixer), _autofreeStream(autofreeStream), - _volume(Mixer::kMaxChannelVolume), _balance(0), _pauseLevel(0), _id(id), _samplesConsumed(0), - _samplesDecoded(0), _mixerTimeStamp(0), _pauseStartTime(0), _pauseTime(0), _converter(0), - _input(input), _permanent(permanent) { + DisposeAfterUse::Flag autofreeStream, bool reverseStereo, int id, bool permanent) + : _type(type), _mixer(mixer), _id(id), _permanent(permanent), _volume(Mixer::kMaxChannelVolume), + _balance(0), _pauseLevel(0), _samplesConsumed(0), _samplesDecoded(0), _mixerTimeStamp(0), + _pauseStartTime(0), _pauseTime(0), _autofreeStream(autofreeStream), _converter(0), + _input(input) { assert(mixer); assert(input); @@ -399,14 +446,89 @@ Channel::Channel(Mixer *mixer, Mixer::SoundType type, AudioStream *input, Channel::~Channel() { delete _converter; - if (_autofreeStream) + if (_autofreeStream == DisposeAfterUse::YES) delete _input; } -/* len indicates the number of sample *pairs*. So a value of - 10 means that the buffer contains twice 10 sample, each - 16 bits, for a total of 40 bytes. - */ +void Channel::setVolume(const byte volume) { + _volume = volume; + updateChannelVolumes(); +} + +void Channel::setBalance(const int8 balance) { + _balance = balance; + updateChannelVolumes(); +} + +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 + // slightly odd divisor: the 255 reflects the fact that the maximal + // value for _volume is 255, while the 127 is there because the + // balance value ranges from -127 to 127. The mixer (music/sound) + // volume is in the range 0 - kMaxMixerVolume. + // Hence, the vol_l/vol_r values will be in that range, too + + int vol = _mixer->getVolumeForSoundType(_type) * _volume; + + if (_balance == 0) { + _volL = vol / Mixer::kMaxChannelVolume; + _volR = vol / Mixer::kMaxChannelVolume; + } else if (_balance < 0) { + _volL = vol / Mixer::kMaxChannelVolume; + _volR = ((127 + _balance) * vol) / (Mixer::kMaxChannelVolume * 127); + } else { + _volL = ((127 - _balance) * vol) / (Mixer::kMaxChannelVolume * 127); + _volR = vol / Mixer::kMaxChannelVolume; + } +} + +void Channel::pause(bool paused) { + //assert((paused && _pauseLevel >= 0) || (!paused && _pauseLevel)); + + if (paused) { + _pauseLevel++; + + if (_pauseLevel == 1) + _pauseStartTime = g_system->getMillis(); + } else if (_pauseLevel > 0) { + _pauseLevel--; + + if (!_pauseLevel) { + _pauseTime = (g_system->getMillis() - _pauseStartTime); + _pauseStartTime = 0; + } + } +} + +Timestamp Channel::getElapsedTime() { + const uint32 rate = _mixer->getOutputRate(); + uint32 delta = 0; + + Audio::Timestamp ts(0, rate); + + if (_mixerTimeStamp == 0) + return ts; + + if (isPaused()) + delta = _pauseStartTime - _mixerTimeStamp; + else + delta = g_system->getMillis() - _mixerTimeStamp - _pauseTime; + + // Convert the number of samples into a time duration. + + ts = ts.addFrames(_samplesConsumed); + ts = ts.addMsecs(delta); + + // In theory it would seem like a good idea to limit the approximation + // so that it never exceeds the theoretical upper bound set by + // _samplesDecoded. Meanwhile, back in the real world, doing so makes + // the Broken Sword cutscenes noticeably jerkier. I guess the mixer + // isn't invoked at the regular intervals that I first imagined. + + return ts; +} + void Channel::mix(int16 *data, uint len) { assert(_input); @@ -415,60 +537,11 @@ void Channel::mix(int16 *data, uint len) { } else { assert(_converter); - // From the channel balance/volume and the global volume, we compute - // the effective volume for the left and right channel. Note the - // slightly odd divisor: the 255 reflects the fact that the maximal - // value for _volume is 255, while the 127 is there because the - // balance value ranges from -127 to 127. The mixer (music/sound) - // volume is in the range 0 - kMaxMixerVolume. - // Hence, the vol_l/vol_r values will be in that range, too - - int vol = _mixer->getVolumeForSoundType(_type) * _volume; - st_volume_t vol_l, vol_r; - - if (_balance == 0) { - vol_l = vol / Mixer::kMaxChannelVolume; - vol_r = vol / Mixer::kMaxChannelVolume; - } else if (_balance < 0) { - vol_l = vol / Mixer::kMaxChannelVolume; - vol_r = ((127 + _balance) * vol) / (Mixer::kMaxChannelVolume * 127); - } else { - vol_l = ((127 - _balance) * vol) / (Mixer::kMaxChannelVolume * 127); - vol_r = vol / Mixer::kMaxChannelVolume; - } - _samplesConsumed = _samplesDecoded; _mixerTimeStamp = g_system->getMillis(); _pauseTime = 0; - - _converter->flow(*_input, data, len, vol_l, vol_r); - - _samplesDecoded += len; + _samplesDecoded += _converter->flow(*_input, data, len, _volL, _volR); } } -uint32 Channel::getElapsedTime() { - if (_mixerTimeStamp == 0) - return 0; - - // Convert the number of samples into a time duration. To avoid - // overflow, this has to be done in a somewhat non-obvious way. - - uint32 rate = _mixer->getOutputRate(); - - uint32 seconds = _samplesConsumed / rate; - uint32 milliseconds = (1000 * (_samplesConsumed % rate)) / rate; - - uint32 delta = g_system->getMillis() - _mixerTimeStamp - _pauseTime; - - // In theory it would seem like a good idea to limit the approximation - // so that it never exceeds the theoretical upper bound set by - // _samplesDecoded. Meanwhile, back in the real world, doing so makes - // the Broken Sword cutscenes noticeably jerkier. I guess the mixer - // isn't invoked at the regular intervals that I first imagined. - - return 1000 * seconds + milliseconds + delta; -} - - } // End of namespace Audio diff --git a/sound/mixer.h b/sound/mixer.h index f74821e9663..1c1d62e9939 100644 --- a/sound/mixer.h +++ b/sound/mixer.h @@ -26,9 +26,10 @@ #ifndef SOUND_MIXER_H #define SOUND_MIXER_H -#include "common/sys.h" +#include "common/types.h" #include "common/mutex.h" +#include "sound/timestamp.h" class OSystem; @@ -36,6 +37,8 @@ class OSystem; namespace Audio { class AudioStream; +class RewindableAudioStream; +class SeekableAudioStream; class Channel; class Mixer; class MixerImpl; @@ -60,38 +63,6 @@ public: */ class Mixer { public: - /** - * Various flags which can be bit-ORed and then passed to - * Mixer::playRaw resp. makeLinearInputStream to control their - * behavior. - * - * Engine authors are advised not to rely on a certain value or - * order of these flags (in particular, do not store them verbatim - * in savestates). - */ - enum RawFlags { - /** unsigned samples (default: signed) */ - FLAG_UNSIGNED = 1 << 0, - - /** sound is 16 bits wide (default: 8bit) */ - FLAG_16BITS = 1 << 1, - - /** samples are little endian (default: big endian) */ - FLAG_LITTLE_ENDIAN = 1 << 2, - - /** sound is in stereo (default: mono) */ - FLAG_STEREO = 1 << 3, - - /** reverse the left and right stereo channel */ - FLAG_REVERSE_STEREO = 1 << 4, - - /** sound buffer is freed automagically at the end of playing */ - FLAG_AUTOFREE = 1 << 5, - - /** loop the audio */ - FLAG_LOOP = 1 << 6 - }; - enum SoundType { kPlainSoundType = 0, @@ -124,20 +95,6 @@ public: virtual bool isReady() const = 0; - - /** - * Start playing the given raw sound data. - * Internally, this simply creates an audio input stream wrapping the data - * (using the makeLinearInputStream factory function), which is then - * passed on to playInputStream. - */ - virtual void playRaw( - SoundType type, - SoundHandle *handle, - void *sound, uint32 size, uint rate, byte flags, - int id = -1, byte volume = kMaxChannelVolume, int8 balance = 0, - uint32 loopStart = 0, uint32 loopEnd = 0) = 0; - /** * Start playing the given audio input stream. * @@ -162,13 +119,13 @@ public: SoundType type, SoundHandle *handle, AudioStream *input, - int id = -1, byte volume = kMaxChannelVolume, int8 balance = 0, - bool autofreeStream = true, + int id = -1, + byte volume = kMaxChannelVolume, + int8 balance = 0, + DisposeAfterUse::Flag autofreeStream = DisposeAfterUse::YES, bool permanent = false, bool reverseStereo = false) = 0; - - /** * Stop all currently playing sounds. */ @@ -264,6 +221,11 @@ public: */ virtual uint32 getSoundElapsedTime(SoundHandle handle) = 0; + /** + * Get approximation of for how long the channel has been playing. + */ + virtual Timestamp getElapsedTime(SoundHandle handle) = 0; + /** * Check whether any channel of the given sound type is active. * For example, this can be used to check whether any SFX sound diff --git a/sound/mixer_intern.h b/sound/mixer_intern.h index f59d2bbbf9b..c927b4cda25 100644 --- a/sound/mixer_intern.h +++ b/sound/mixer_intern.h @@ -74,23 +74,14 @@ public: virtual bool isReady() const { return _mixerReady; } - virtual void playRaw( - SoundType type, - SoundHandle *handle, - void *sound, uint32 size, uint rate, byte flags, - int id = -1, byte volume = 255, int8 balance = 0, - uint32 loopStart = 0, uint32 loopEnd = 0); - virtual void playInputStream( SoundType type, SoundHandle *handle, AudioStream *input, - int id = -1, byte volume = 255, int8 balance = 0, - bool autofreeStream = true, - bool permanent = false, - bool reverseStereo = false); - - + int id, byte volume, int8 balance, + DisposeAfterUse::Flag autofreeStream, + bool permanent, + bool reverseStereo); virtual void stopAll(); virtual void stopID(int id); @@ -109,6 +100,7 @@ public: virtual void setChannelBalance(SoundHandle handle, int8 balance); virtual uint32 getSoundElapsedTime(SoundHandle handle); + virtual Timestamp getElapsedTime(SoundHandle handle); virtual bool hasActiveChannelOfType(SoundType type); diff --git a/sound/module.mk b/sound/module.mk index 70a0df126fc..99ae1088c20 100644 --- a/sound/module.mk +++ b/sound/module.mk @@ -1,13 +1,31 @@ MODULE := sound MODULE_OBJS := \ - audiostream.o \ audiocd.o \ + audiostream.o \ flac.o \ + fmopl.o \ + mididrv.o \ + midiparser.o \ + midiparser_smf.o \ + midiparser_xmidi.o \ mixer.o \ mp3.o \ - rate.o \ - vorbis.o + mpu401.o \ + musicplugin.o \ + null.o \ + raw.o \ + timestamp.o \ + vorbis.o \ + softsynth/adlib.o \ + softsynth/opl/dosbox.o \ + softsynth/opl/mame.o \ + softsynth/ym2612.o \ + softsynth/fluidsynth.o \ + softsynth/mt32.o \ + softsynth/pcspk.o \ + softsynth/sid.o \ + softsynth/wave6581.o ifndef USE_ARM_SOUND_ASM MODULE_OBJS += \ diff --git a/sound/mp3.cpp b/sound/mp3.cpp index 5f272bb6928..10ab30d6280 100644 --- a/sound/mp3.cpp +++ b/sound/mp3.cpp @@ -45,7 +45,7 @@ namespace Audio { #pragma mark - -class MP3InputStream : public AudioStream { +class MP3InputStream : public SeekableAudioStream { protected: enum State { MP3_STATE_INIT, // Need to init the decoder @@ -54,18 +54,14 @@ protected: }; Common::SeekableReadStream *_inStream; - bool _disposeAfterUse; + DisposeAfterUse::Flag _disposeAfterUse; - uint _numLoops; uint _posInFrame; State _state; - const mad_timer_t _startTime; - const mad_timer_t _endTime; + Timestamp _length; mad_timer_t _totalTime; - int32 _totalPlayTime; - mad_stream _stream; mad_frame _frame; mad_synth _synth; @@ -79,10 +75,7 @@ protected: public: MP3InputStream(Common::SeekableReadStream *inStream, - bool dispose, - mad_timer_t start = mad_timer_zero, - mad_timer_t end = mad_timer_zero, - uint numLoops = 1); + DisposeAfterUse::Flag dispose); ~MP3InputStream(); int readBuffer(int16 *buffer, const int numSamples); @@ -90,97 +83,43 @@ public: bool endOfData() const { return _state == MP3_STATE_EOS; } bool isStereo() const { return MAD_NCHANNELS(&_frame.header) == 2; } int getRate() const { return _frame.header.samplerate; } - int32 getTotalPlayTime() const { return _totalPlayTime; } + bool seek(const Timestamp &where); + Timestamp getLength() const { return _length; } protected: void decodeMP3Data(); void readMP3Data(); + + void initStream(); + void readHeader(); + void deinitStream(); }; -MP3InputStream::MP3InputStream(Common::SeekableReadStream *inStream, bool dispose, mad_timer_t start, mad_timer_t end, uint numLoops) : +MP3InputStream::MP3InputStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) : _inStream(inStream), _disposeAfterUse(dispose), - _numLoops(numLoops), _posInFrame(0), _state(MP3_STATE_INIT), - _startTime(start), - _endTime(end), + _length(0, 1000), _totalTime(mad_timer_zero) { - // Make sure that either start < end, or end is zero (indicating "play until end") - assert(mad_timer_compare(_startTime, _endTime) < 0 || mad_timer_sign(_endTime) == 0); - // The MAD_BUFFER_GUARD must always contain zeros (the reason // for this is that the Layer III Huffman decoder of libMAD // may read a few bytes beyond the end of the input buffer). memset(_buf + BUFFER_SIZE, 0, MAD_BUFFER_GUARD); - // Calculate play time - mad_timer_t length; + // Calculate the length of the stream + initStream(); - mad_timer_set(&length, 0, 0, 1000); - mad_timer_add(&length, start); - mad_timer_negate(&length); + while (_state != MP3_STATE_EOS) + readHeader(); - if (mad_timer_sign(end) != 0) { - mad_timer_add(&length, end); - } else { - mad_stream_init(&_stream); - mad_frame_init(&_frame); + _length = Timestamp(mad_timer_count(_totalTime, MAD_UNITS_MILLISECONDS), getRate()); - // Reset the stream data - _inStream->seek(0, SEEK_SET); + deinitStream(); - // Update state - _state = MP3_STATE_READY; - - // Read the first few sample bytes - readMP3Data(); - - do { - // If necessary, load more data into the stream decoder - if (_stream.error == MAD_ERROR_BUFLEN) - readMP3Data(); - - while (_state == MP3_STATE_READY) { - _stream.error = MAD_ERROR_NONE; - - // Decode the next header. Note: mad_frame_decode would do this for us, too. - // However, for seeking we don't want to decode the full frame (else it would - // be far too slow). - if (mad_header_decode(&_frame.header, &_stream) == -1) { - if (_stream.error == MAD_ERROR_BUFLEN) { - break; // Read more data - } else if (MAD_RECOVERABLE(_stream.error)) { - debug(6, "MP3InputStream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); - continue; - } else { - warning("MP3InputStream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); - break; - } - } - - // Sum up the total playback time so far - mad_timer_add(&length, _frame.header.duration); - } - } while (_state != MP3_STATE_EOS); - - mad_synth_finish(&_synth); - mad_frame_finish(&_frame); - - // Reinit stream - _state = MP3_STATE_INIT; - - // Reset the stream data - _inStream->seek(0, SEEK_SET); - } - - _totalPlayTime = mad_timer_count(length, MAD_UNITS_MILLISECONDS); - - if (numLoops && mad_timer_sign(length) >= 0) - _totalPlayTime *= numLoops; - else - _totalPlayTime = kUnknownPlayTime; + // Reinit stream + _state = MP3_STATE_INIT; // Decode the first chunk of data. This is necessary so that _frame // is setup and isStereo() and getRate() return correct results. @@ -188,37 +127,16 @@ MP3InputStream::MP3InputStream(Common::SeekableReadStream *inStream, bool dispos } MP3InputStream::~MP3InputStream() { - if (_state != MP3_STATE_INIT) { - // Deinit MAD - mad_synth_finish(&_synth); - mad_frame_finish(&_frame); - mad_stream_finish(&_stream); - } + deinitStream(); - if (_disposeAfterUse) + if (_disposeAfterUse == DisposeAfterUse::YES) delete _inStream; } void MP3InputStream::decodeMP3Data() { - do { - if (_state == MP3_STATE_INIT) { - // Init MAD - mad_stream_init(&_stream); - mad_frame_init(&_frame); - mad_synth_init(&_synth); - - // Reset the stream data - _inStream->seek(0, SEEK_SET); - _totalTime = mad_timer_zero; - _posInFrame = 0; - - // Update state - _state = MP3_STATE_READY; - - // Read the first few sample bytes - readMP3Data(); - } + if (_state == MP3_STATE_INIT) + initStream(); if (_state == MP3_STATE_EOS) return; @@ -228,35 +146,8 @@ void MP3InputStream::decodeMP3Data() { readMP3Data(); while (_state == MP3_STATE_READY) { - _stream.error = MAD_ERROR_NONE; - - // Decode the next header. Note: mad_frame_decode would do this for us, too. - // However, for seeking we don't want to decode the full frame (else it would - // be far too slow). Hence we perform this explicitly in a separate step. - if (mad_header_decode(&_frame.header, &_stream) == -1) { - if (_stream.error == MAD_ERROR_BUFLEN) { - break; // Read more data - } else if (MAD_RECOVERABLE(_stream.error)) { - debug(6, "MP3InputStream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); - continue; - } else { - warning("MP3InputStream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); - break; - } - } - - // Sum up the total playback time so far - mad_timer_add(&_totalTime, _frame.header.duration); - - // If we have not yet reached the start point, skip to the next frame - if (mad_timer_compare(_totalTime, _startTime) < 0) - continue; - - // If an end time is specified and we are past it, stop - if (mad_timer_sign(_endTime) > 0 && mad_timer_compare(_totalTime, _endTime) >= 0) { - _state = MP3_STATE_EOS; - break; - } + // TODO: Do we need to use readHeader, when we do not do any seeking here? + readHeader(); // Decode the next frame if (mad_frame_decode(&_frame, &_stream) == -1) { @@ -279,21 +170,6 @@ void MP3InputStream::decodeMP3Data() { _posInFrame = 0; break; } - - if (_state == MP3_STATE_EOS && _numLoops != 1) { - // If looping is on and there are loops left, rewind to the start - if (_numLoops != 0) - _numLoops--; - - // Deinit MAD - mad_synth_finish(&_synth); - mad_frame_finish(&_frame); - mad_stream_finish(&_stream); - - // Reset the decoder state to indicate we should start over - _state = MP3_STATE_INIT; - } - } while (_state != MP3_STATE_EOS && _stream.error == MAD_ERROR_BUFLEN); if (_stream.error != MAD_ERROR_NONE) @@ -330,6 +206,96 @@ void MP3InputStream::readMP3Data() { mad_stream_buffer(&_stream, _buf, size + remaining); } +bool MP3InputStream::seek(const Timestamp &where) { + if (where == _length) { + _state = MP3_STATE_EOS; + return true; + } else if (where > _length) { + return false; + } + + const uint32 time = where.msecs(); + + mad_timer_t destination; + mad_timer_set(&destination, time / 1000, time % 1000, 1000); + + if (_state != MP3_STATE_READY || mad_timer_compare(destination, _totalTime) < 0) + initStream(); + + while (mad_timer_compare(destination, _totalTime) > 0 && _state != MP3_STATE_EOS) + readHeader(); + + return (_state != MP3_STATE_EOS); +} + +void MP3InputStream::initStream() { + if (_state != MP3_STATE_INIT) + deinitStream(); + + // Init MAD + mad_stream_init(&_stream); + mad_frame_init(&_frame); + mad_synth_init(&_synth); + + // Reset the stream data + _inStream->seek(0, SEEK_SET); + _totalTime = mad_timer_zero; + _posInFrame = 0; + + // Update state + _state = MP3_STATE_READY; + + // Read the first few sample bytes + readMP3Data(); +} + +void MP3InputStream::readHeader() { + if (_state != MP3_STATE_READY) + return; + + // If necessary, load more data into the stream decoder + if (_stream.error == MAD_ERROR_BUFLEN) + readMP3Data(); + + while (_state != MP3_STATE_EOS) { + _stream.error = MAD_ERROR_NONE; + + // Decode the next header. Note: mad_frame_decode would do this for us, too. + // However, for seeking we don't want to decode the full frame (else it would + // be far too slow). Hence we perform this explicitly in a separate step. + if (mad_header_decode(&_frame.header, &_stream) == -1) { + if (_stream.error == MAD_ERROR_BUFLEN) { + readMP3Data(); // Read more data + continue; + } else if (MAD_RECOVERABLE(_stream.error)) { + debug(6, "MP3InputStream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); + continue; + } else { + warning("MP3InputStream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); + break; + } + } + + // Sum up the total playback time so far + mad_timer_add(&_totalTime, _frame.header.duration); + break; + } + + if (_stream.error != MAD_ERROR_NONE) + _state = MP3_STATE_EOS; +} + +void MP3InputStream::deinitStream() { + if (_state == MP3_STATE_INIT) + return; + + // Deinit MAD + mad_synth_finish(&_synth); + mad_frame_finish(&_frame); + mad_stream_finish(&_stream); + + _state = MP3_STATE_EOS; +} static inline int scale_sample(mad_fixed_t sample) { // round @@ -372,28 +338,10 @@ int MP3InputStream::readBuffer(int16 *buffer, const int numSamples) { #pragma mark --- MP3 factory functions --- #pragma mark - - -AudioStream *makeMP3Stream( +SeekableAudioStream *makeMP3Stream( Common::SeekableReadStream *stream, - bool disposeAfterUse, - uint32 startTime, - uint32 duration, - uint numLoops) { - - mad_timer_t start; - mad_timer_t end; - - // Both startTime and duration are given in milliseconds. - // Calculate the appropriate mad_timer_t values from them. - mad_timer_set(&start, startTime / 1000, startTime % 1000, 1000); - if (duration == 0) { - end = mad_timer_zero; - } else { - int endTime = startTime + duration; - mad_timer_set(&end, endTime / 1000, endTime % 1000, 1000); - } - - return new MP3InputStream(stream, disposeAfterUse, start, end, numLoops); + DisposeAfterUse::Flag disposeAfterUse) { + return new MP3InputStream(stream, disposeAfterUse); } } // End of namespace Audio diff --git a/sound/mp3.h b/sound/mp3.h index e138930cf39..5abd3ba4b1c 100644 --- a/sound/mp3.h +++ b/sound/mp3.h @@ -24,6 +24,7 @@ */ /** + * @file * Sound decoder used in engines: * - agos * - kyra @@ -40,6 +41,7 @@ #ifndef SOUND_MP3_H #define SOUND_MP3_H +#include "common/types.h" #include "common/sys.h" #ifdef USE_MAD @@ -51,26 +53,19 @@ namespace Common { namespace Audio { class AudioStream; +class SeekableAudioStream; /** - * Create a new AudioStream from the MP3 data in the given stream. - * Allows for looping (which is why we require a SeekableReadStream), - * and specifying only a portion of the data to be played, based - * on time offsets. + * Create a new SeekableAudioStream from the MP3 data in the given stream. + * Allows for seeking (which is why we require a SeekableReadStream). * * @param stream the SeekableReadStream from which to read the MP3 data * @param disposeAfterUse whether to delete the stream after use - * @param startTime the (optional) time offset in milliseconds from which to start playback - * @param duration the (optional) time in milliseconds specifying how long to play - * @param numLoops how often the data shall be looped (0 = infinite) - * @return a new AudioStream, or NULL, if an error occured + * @return a new SeekableAudioStream, or NULL, if an error occured */ -AudioStream *makeMP3Stream( +SeekableAudioStream *makeMP3Stream( Common::SeekableReadStream *stream, - bool disposeAfterUse, - uint32 startTime = 0, - uint32 duration = 0, - uint numLoops = 1); + DisposeAfterUse::Flag disposeAfterUse); } // End of namespace Audio diff --git a/sound/rate.cpp b/sound/rate.cpp index a7080b57e06..3505f9933ca 100644 --- a/sound/rate.cpp +++ b/sound/rate.cpp @@ -101,7 +101,7 @@ SimpleRateConverter::SimpleRateConverter(st_rate_t inrate /* * Processed signed long samples from ibuf to obuf. - * Return number of samples processed. + * Return number of sample pairs processed. */ template int SimpleRateConverter::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) { @@ -119,7 +119,7 @@ int SimpleRateConverter::flow(AudioStream &input, st_samp inPtr = inBuf; inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf)); if (inLen <= 0) - return ST_EOF; + return (obuf - ostart) / 2; } inLen -= (stereo ? 2 : 1); opos--; @@ -143,7 +143,7 @@ int SimpleRateConverter::flow(AudioStream &input, st_samp obuf += 2; } - return ST_SUCCESS; + return (obuf - ostart) / 2; } /** @@ -210,7 +210,7 @@ LinearRateConverter::LinearRateConverter(st_rate_t inrate /* * Processed signed long samples from ibuf to obuf. - * Return number of samples processed. + * Return number of sample pairs processed. */ template int LinearRateConverter::flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) { @@ -228,7 +228,7 @@ int LinearRateConverter::flow(AudioStream &input, st_samp inPtr = inBuf; inLen = input.readBuffer(inBuf, ARRAYSIZE(inBuf)); if (inLen <= 0) - return ST_EOF; + return (obuf - ostart) / 2; } inLen -= (stereo ? 2 : 1); ilast0 = icur0; @@ -262,7 +262,7 @@ int LinearRateConverter::flow(AudioStream &input, st_samp opos += opos_inc; } } - return ST_SUCCESS; + return (obuf - ostart) / 2; } @@ -288,6 +288,8 @@ public: st_sample_t *ptr; st_size_t len; + st_sample_t *ostart = obuf; + if (stereo) osamp *= 2; @@ -316,7 +318,7 @@ public: obuf += 2; } - return ST_SUCCESS; + return (obuf - ostart) / 2; } virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) { diff --git a/sound/rate.h b/sound/rate.h index 127779dd4a6..860e2b53171 100644 --- a/sound/rate.h +++ b/sound/rate.h @@ -74,7 +74,12 @@ class RateConverter { public: RateConverter() {} virtual ~RateConverter() {} + + /** + * @return Number of sample pairs written into the buffer. + */ virtual int flow(AudioStream &input, st_sample_t *obuf, st_size_t osamp, st_volume_t vol_l, st_volume_t vol_r) = 0; + virtual int drain(st_sample_t *obuf, st_size_t osamp, st_volume_t vol) = 0; }; diff --git a/sound/raw.cpp b/sound/raw.cpp new file mode 100644 index 00000000000..f94b950e903 --- /dev/null +++ b/sound/raw.cpp @@ -0,0 +1,466 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/endian.h" +#include "common/stream.h" + +#include "sound/audiostream.h" +#include "sound/mixer.h" +#include "sound/raw.h" + +namespace Audio { + +// This used to be an inline template function, but +// buggy template function handling in MSVC6 forced +// us to go with the macro approach. So far this is +// the only template function that MSVC6 seemed to +// compile incorrectly. Knock on wood. +#define READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, ptr, isLE) \ + ((is16Bit ? (isLE ? READ_LE_UINT16(ptr) : READ_BE_UINT16(ptr)) : (*ptr << 8)) ^ (isUnsigned ? 0x8000 : 0)) + + +// TODO: Get rid of this +uint32 calculateSampleOffset(const Timestamp &where, int rate) { + return where.convertToFramerate(rate).totalNumberOfFrames(); +} + + + +#pragma mark - +#pragma mark --- RawMemoryStream --- +#pragma mark - + +/** + * A simple raw audio stream, purely memory based. It operates on a single + * block of data, which is passed to it upon creation. + * Optionally supports looping the sound. + * + * Design note: This code tries to be as efficient as possible (without + * resorting to assembly, that is). To this end, it is written as a template + * class. This way the compiler can create optimized code for each special + * case. This results in a total of 12 versions of the code being generated. + */ +template +class RawMemoryStream : public SeekableAudioStream { +protected: + const byte *_ptr; + const byte *_end; + const int _rate; + const byte *_origPtr; + const DisposeAfterUse::Flag _disposeAfterUse; + const Timestamp _playtime; + +public: + RawMemoryStream(int rate, const byte *ptr, uint len, DisposeAfterUse::Flag autoFreeMemory) + : _ptr(ptr), _end(ptr+len), _rate(rate), _origPtr(ptr), + _disposeAfterUse(autoFreeMemory), + _playtime(0, len / (is16Bit ? 2 : 1) / (stereo ? 2 : 1), rate) { + } + + virtual ~RawMemoryStream() { + if (_disposeAfterUse == DisposeAfterUse::YES) + free(const_cast(_origPtr)); + } + + int readBuffer(int16 *buffer, const int numSamples); + + bool isStereo() const { return stereo; } + bool endOfData() const { return _ptr >= _end; } + + int getRate() const { return _rate; } + bool seek(const Timestamp &where); + Timestamp getLength() const { return _playtime; } +}; + +template +int RawMemoryStream::readBuffer(int16 *buffer, const int numSamples) { + int samples = numSamples; + while (samples > 0 && _ptr < _end) { + int len = MIN(samples, (int)(_end - _ptr) / (is16Bit ? 2 : 1)); + samples -= len; + do { + *buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE); + _ptr += (is16Bit ? 2 : 1); + } while (--len); + } + return numSamples-samples; +} + +template +bool RawMemoryStream::seek(const Timestamp &where) { + const uint8 *ptr = _origPtr + calculateSampleOffset(where, getRate()) * (is16Bit ? 2 : 1) * (stereo ? 2 : 1); + if (ptr > _end) { + _ptr = _end; + return false; + } else if (ptr == _end) { + _ptr = _end; + return true; + } else { + _ptr = ptr; + return true; + } +} + +#pragma mark - +#pragma mark --- RawDiskStream --- +#pragma mark - + + + +/** + * RawDiskStream. This can stream raw PCM audio data from disk. The + * function takes an pointer to an array of RawDiskStreamAudioBlock which defines the + * start position and length of each block of uncompressed audio in the stream. + */ +template +class RawDiskStream : public SeekableAudioStream { + +// Allow backends to override buffer size +#ifdef CUSTOM_AUDIO_BUFFER_SIZE + static const int32 BUFFER_SIZE = CUSTOM_AUDIO_BUFFER_SIZE; +#else + static const int32 BUFFER_SIZE = 16384; +#endif + +protected: + byte* _buffer; ///< Streaming buffer + const byte *_ptr; ///< Pointer to current position in stream buffer + const int _rate; ///< Sample rate of stream + + Timestamp _playtime; ///< Calculated total play time + Common::SeekableReadStream *_stream; ///< Stream to read data from + int32 _filePos; ///< Current position in stream + int32 _diskLeft; ///< Samples left in stream in current block not yet read to buffer + int32 _bufferLeft; ///< Samples left in buffer in current block + const DisposeAfterUse::Flag _disposeAfterUse; ///< Indicates whether the stream object should be deleted when this RawDiskStream is destructed + + RawDiskStreamAudioBlock *_audioBlock; ///< Audio block list + const int _audioBlockCount; ///< Number of blocks in _audioBlock + int _currentBlock; ///< Current audio block number +public: + RawDiskStream(int rate, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream, RawDiskStreamAudioBlock *block, uint numBlocks) + : _rate(rate), _playtime(0, rate), _stream(stream), _disposeAfterUse(disposeStream), + _audioBlockCount(numBlocks) { + + assert(numBlocks > 0); + + // Allocate streaming buffer + if (is16Bit) { + _buffer = (byte *)malloc(BUFFER_SIZE * sizeof(int16)); + } else { + _buffer = (byte *)malloc(BUFFER_SIZE * sizeof(byte)); + } + + _ptr = _buffer; + _bufferLeft = 0; + + // Copy audio block data to our buffer + // TODO: Replace this with a Common::Array or Common::List to + // make it a little friendlier. + _audioBlock = new RawDiskStreamAudioBlock[numBlocks]; + memcpy(_audioBlock, block, numBlocks * sizeof(RawDiskStreamAudioBlock)); + + // Set current buffer state, playing first block + _currentBlock = 0; + _filePos = _audioBlock[_currentBlock].pos; + _diskLeft = _audioBlock[_currentBlock].len; + + // Add up length of all blocks in order to caluclate total play time + int len = 0; + for (int r = 0; r < _audioBlockCount; r++) { + len += _audioBlock[r].len; + } + _playtime = Timestamp(0, len / (is16Bit ? 2 : 1) / (stereo ? 2 : 1), rate); + } + + + virtual ~RawDiskStream() { + if (_disposeAfterUse == DisposeAfterUse::YES) { + delete _stream; + } + + delete[] _audioBlock; + free(_buffer); + } + int readBuffer(int16 *buffer, const int numSamples); + + bool isStereo() const { return stereo; } + bool endOfData() const { return (_currentBlock == _audioBlockCount - 1) && (_diskLeft == 0) && (_bufferLeft == 0); } + + int getRate() const { return _rate; } + Timestamp getLength() const { return _playtime; } + + bool seek(const Timestamp &where); +}; + +template +int RawDiskStream::readBuffer(int16 *buffer, const int numSamples) { + int oldPos = _stream->pos(); + bool restoreFilePosition = false; + + int samples = numSamples; + + while (samples > 0 && ((_diskLeft > 0 || _bufferLeft > 0) || (_currentBlock != _audioBlockCount - 1)) ) { + // Output samples in the buffer to the output + int len = MIN(samples, _bufferLeft); + samples -= len; + _bufferLeft -= len; + + while (len > 0) { + *buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE); + _ptr += (is16Bit ? 2 : 1); + len--; + } + + // Have we now finished this block? If so, read the next block + if ((_bufferLeft == 0) && (_diskLeft == 0) && (_currentBlock != _audioBlockCount - 1)) { + // Next block + _currentBlock++; + + _filePos = _audioBlock[_currentBlock].pos; + _diskLeft = _audioBlock[_currentBlock].len; + } + + // Now read more data from disk if there is more to be read + if ((_bufferLeft == 0) && (_diskLeft > 0)) { + int32 readAmount = MIN(_diskLeft, BUFFER_SIZE); + + _stream->seek(_filePos, SEEK_SET); + _stream->read(_buffer, readAmount * (is16Bit? 2: 1)); + + // Amount of data in buffer is now the amount read in, and + // the amount left to read on disk is decreased by the same amount + _bufferLeft = readAmount; + _diskLeft -= readAmount; + _ptr = (byte *)_buffer; + _filePos += readAmount * (is16Bit ? 2 : 1); + + // Set this flag now we've used the file, it restores it's + // original position. + restoreFilePosition = true; + } + } + + // In case calling code relies on the position of this stream staying + // constant, I restore the location if I've changed it. This is probably + // not necessary. + if (restoreFilePosition) { + _stream->seek(oldPos, SEEK_SET); + } + + return numSamples - samples; +} + +template +bool RawDiskStream::seek(const Timestamp &where) { + const uint32 seekSample = calculateSampleOffset(where, getRate()) * (stereo ? 2 : 1); + uint32 curSample = 0; + + // Search for the disk block in which the specific sample is placed + _currentBlock = 0; + while (_currentBlock < _audioBlockCount) { + uint32 nextBlockSample = curSample + _audioBlock[_currentBlock].len; + + if (nextBlockSample > seekSample) + break; + + curSample = nextBlockSample; + ++_currentBlock; + } + + _filePos = 0; + _diskLeft = 0; + _bufferLeft = 0; + + if (_currentBlock == _audioBlockCount) { + return ((seekSample - curSample) == (uint32)_audioBlock[_currentBlock - 1].len); + } else { + const uint32 offset = seekSample - curSample; + + _filePos = _audioBlock[_currentBlock].pos + offset * (is16Bit ? 2 : 1); + _diskLeft = _audioBlock[_currentBlock].len - offset; + + return true; + } +} + +#pragma mark - +#pragma mark --- Raw stream factories --- +#pragma mark - + +/* In the following, we use preprocessor / macro tricks to simplify the code + * which instantiates the input streams. We used to use template functions for + * this, but MSVC6 / EVC 3-4 (used for WinCE builds) are extremely buggy when it + * comes to this feature of C++... so as a compromise we use macros to cut down + * on the (source) code duplication a bit. + * So while normally macro tricks are said to make maintenance harder, in this + * particular case it should actually help it :-) + */ + +#define MAKE_LINEAR(STEREO, UNSIGNED) \ + if (is16Bit) { \ + if (isLE) \ + return new RawMemoryStream(rate, ptr, len, autoFree); \ + else \ + return new RawMemoryStream(rate, ptr, len, autoFree); \ + } else \ + return new RawMemoryStream(rate, ptr, len, autoFree) + +SeekableAudioStream *makeRawMemoryStream(const byte *ptr, uint32 len, + DisposeAfterUse::Flag autoFree, + int rate, byte flags) { + const bool isStereo = (flags & Audio::FLAG_STEREO) != 0; + const bool is16Bit = (flags & Audio::FLAG_16BITS) != 0; + const bool isUnsigned = (flags & Audio::FLAG_UNSIGNED) != 0; + const bool isLE = (flags & Audio::FLAG_LITTLE_ENDIAN) != 0; + + // Verify the buffer sizes are sane + if (is16Bit && isStereo) { + assert((len & 3) == 0); + } else if (is16Bit || isStereo) { + assert((len & 1) == 0); + } + + if (isStereo) { + if (isUnsigned) { + MAKE_LINEAR(true, true); + } else { + MAKE_LINEAR(true, false); + } + } else { + if (isUnsigned) { + MAKE_LINEAR(false, true); + } else { + MAKE_LINEAR(false, false); + } + } +} + + +AudioStream *makeRawMemoryStream(const byte *ptr, uint32 len, + DisposeAfterUse::Flag autoFree, + int rate, byte flags, + uint loopStart, uint loopEnd) { + SeekableAudioStream *stream = makeRawMemoryStream(ptr, len, autoFree, rate, flags); + + const bool isStereo = (flags & Audio::FLAG_STEREO) != 0; + const bool is16Bit = (flags & Audio::FLAG_16BITS) != 0; + const bool isLooping = (flags & Audio::FLAG_LOOP) != 0; + + if (isLooping) { + uint loopOffset = 0, loopLen = 0; + if (loopEnd == 0) + loopEnd = len; + assert(loopStart <= loopEnd); + assert(loopEnd <= len); + + loopOffset = loopStart; + loopLen = loopEnd - loopStart; + + // Verify the buffer sizes are sane + if (is16Bit && isStereo) + assert((loopLen & 3) == 0 && (loopStart & 3) == 0 && (loopEnd & 3) == 0); + else if (is16Bit || isStereo) + assert((loopLen & 1) == 0 && (loopStart & 1) == 0 && (loopEnd & 1) == 0); + + const uint32 extRate = stream->getRate() * (is16Bit ? 2 : 1) * (isStereo ? 2 : 1); + + return new SubLoopingAudioStream(stream, 0, Timestamp(0, loopStart, extRate), Timestamp(0, loopEnd, extRate)); + } else { + return stream; + } +} + + + +#define MAKE_LINEAR_DISK(STEREO, UNSIGNED) \ + if (is16Bit) { \ + if (isLE) \ + return new RawDiskStream(rate, disposeStream, stream, block, numBlocks); \ + else \ + return new RawDiskStream(rate, disposeStream, stream, block, numBlocks); \ + } else \ + return new RawDiskStream(rate, disposeStream, stream, block, numBlocks) + + +SeekableAudioStream *makeRawDiskStream(Common::SeekableReadStream *stream, RawDiskStreamAudioBlock *block, int numBlocks, + int rate, byte flags, DisposeAfterUse::Flag disposeStream) { + const bool isStereo = (flags & Audio::FLAG_STEREO) != 0; + const bool is16Bit = (flags & Audio::FLAG_16BITS) != 0; + const bool isUnsigned = (flags & Audio::FLAG_UNSIGNED) != 0; + const bool isLE = (flags & Audio::FLAG_LITTLE_ENDIAN) != 0; + + if (isStereo) { + if (isUnsigned) { + MAKE_LINEAR_DISK(true, true); + } else { + MAKE_LINEAR_DISK(true, false); + } + } else { + if (isUnsigned) { + MAKE_LINEAR_DISK(false, true); + } else { + MAKE_LINEAR_DISK(false, false); + } + } +} + +AudioStream *makeRawDiskStream(Common::SeekableReadStream *stream, RawDiskStreamAudioBlock *block, + int numBlocks, int rate, byte flags, DisposeAfterUse::Flag disposeStream, uint loopStart, uint loopEnd) { + SeekableAudioStream *s = makeRawDiskStream(stream, block, numBlocks, rate, flags, disposeStream); + + const bool isStereo = (flags & Audio::FLAG_STEREO) != 0; + const bool is16Bit = (flags & Audio::FLAG_16BITS) != 0; + const bool isLooping = (flags & Audio::FLAG_LOOP) != 0; + + if (isLooping) { + uint loopOffset = 0, loopLen = 0; + const uint len = s->getLength().totalNumberOfFrames() / (is16Bit ? 2 : 1) / (isStereo ? 2 : 1); + + if (loopEnd == 0) + loopEnd = len; + assert(loopStart <= loopEnd); + assert(loopEnd <= len); + + loopOffset = loopStart; + loopLen = loopEnd - loopStart; + + // Verify the buffer sizes are sane + if (is16Bit && isStereo) + assert((loopLen & 3) == 0 && (loopStart & 3) == 0 && (loopEnd & 3) == 0); + else if (is16Bit || isStereo) + assert((loopLen & 1) == 0 && (loopStart & 1) == 0 && (loopEnd & 3) == 0); + + const uint32 extRate = s->getRate() * (is16Bit ? 2 : 1) * (isStereo ? 2 : 1); + + return new SubLoopingAudioStream(s, 0, Timestamp(0, loopStart, extRate), Timestamp(0, loopEnd, extRate)); + } else { + return s; + } +} + + + +} // End of namespace Audio diff --git a/sound/raw.h b/sound/raw.h new file mode 100644 index 00000000000..a00541de53f --- /dev/null +++ b/sound/raw.h @@ -0,0 +1,144 @@ +/* Residual - A 3D game interpreter + * + * Residual is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the AUTHORS + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef SOUND_RAW_H +#define SOUND_RAW_H + +#include "common/sys.h" +#include "common/types.h" + + +namespace Common { class SeekableReadStream; } + + +namespace Audio { + +class AudioStream; +class SeekableAudioStream; + +/** + * Various flags which can be bit-ORed and then passed to + * makeRawMemoryStream and some other AudioStream factories + * to control their behavior. + * + * Engine authors are advised not to rely on a certain value or + * order of these flags (in particular, do not store them verbatim + * in savestates). + */ +enum RawFlags { + /** unsigned samples (default: signed) */ + FLAG_UNSIGNED = 1 << 0, + + /** sound is 16 bits wide (default: 8bit) */ + FLAG_16BITS = 1 << 1, + + /** samples are little endian (default: big endian) */ + FLAG_LITTLE_ENDIAN = 1 << 2, + + /** sound is in stereo (default: mono) */ + FLAG_STEREO = 1 << 3, + + /** loop the audio */ + FLAG_LOOP = 1 << 6 +}; + + +/** + * Creates a audio stream, which plays the given raw data. + * + * @param ptr Data + * @param len Length of the data (in bytes!) + * @param rate The sample rate of the data. + * @param flags Flags combination. + * @see Mixer::RawFlags + * @return The new SeekableAudioStream (or 0 on failure). + */ +SeekableAudioStream *makeRawMemoryStream(const byte *ptr, uint32 len, + DisposeAfterUse::Flag autofreeBuffer, + int rate, byte flags); + +/** + * NOTE: + * This API is considered deprecated. + * + * Factory function for a raw PCM AudioStream, which will simply treat all + * data in the buffer described by ptr and len as raw sample data in the + * specified format. It will then simply pass this data directly to the mixer, + * after converting it to the sample format used by the mixer (i.e. 16 bit + * signed native endian). Optionally supports (infinite) looping of a portion + * of the data. + */ +AudioStream *makeRawMemoryStream(const byte *ptr, uint32 len, + DisposeAfterUse::Flag autofreeBuffer, + int rate, byte flags, + uint loopStart, uint loopEnd); + + +/** + * Struct used to define the audio data to be played by a RawDiskStream. + */ +struct RawDiskStreamAudioBlock { + int32 pos; ///< Position in stream of the block + int32 len; ///< Length of the block (in samples) +}; + +/** + * Creates a audio stream, which plays from given stream. + * + * @param stream Stream to play from + * @param block Pointer to an RawDiskStreamAudioBlock array + * @see RawDiskStreamAudioBlock + * @param numBlocks Number of blocks. + * @param rate The rate + * @param len Length of the data (in bytes!) + * @param flags Flags combination. + * @see Mixer::RawFlags + * @param disposeStream Whether the "stream" object should be destroyed after playback. + * @return The new SeekableAudioStream (or 0 on failure). + */ +SeekableAudioStream *makeRawDiskStream(Common::SeekableReadStream *stream, + RawDiskStreamAudioBlock *block, int numBlocks, + int rate, byte flags, + DisposeAfterUse::Flag disposeStream); + +/** + * NOTE: + * This API is considered deprecated. + * + * Factory function for a Raw Disk Stream. This can stream raw PCM + * audio data from disk. The function takes an pointer to an array of + * RawDiskStreamAudioBlock which defines the start position and length of + * each block of uncompressed audio in the stream. + */ +AudioStream *makeRawDiskStream(Common::SeekableReadStream *stream, + RawDiskStreamAudioBlock *block, int numBlocks, + int rate, byte flags, + DisposeAfterUse::Flag disposeStream, + uint loopStart, uint loopEnd); + + +} // End of namespace Audio + +#endif diff --git a/sound/vorbis.cpp b/sound/vorbis.cpp index 119306071df..3fa27711f48 100644 --- a/sound/vorbis.cpp +++ b/sound/vorbis.cpp @@ -85,23 +85,15 @@ static ov_callbacks g_stream_wrap = { #pragma mark - -class VorbisInputStream : public AudioStream { +class VorbisInputStream : public SeekableAudioStream { protected: Common::SeekableReadStream *_inStream; - bool _disposeAfterUse; + DisposeAfterUse::Flag _disposeAfterUse; bool _isStereo; int _rate; - uint _numLoops; - const uint _totalNumLoops; -#ifdef USE_TREMOR - ogg_int64_t _startTime; - ogg_int64_t _endTime; -#else - double _startTime; - double _endTime; -#endif + Timestamp _length; OggVorbis_File _ovFile; @@ -111,7 +103,7 @@ protected: public: // startTime / duration are in milliseconds - VorbisInputStream(Common::SeekableReadStream *inStream, bool dispose, uint startTime = 0, uint endTime = 0, uint numLoops = 1); + VorbisInputStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose); ~VorbisInputStream(); int readBuffer(int16 *buffer, const int numSamples); @@ -120,26 +112,16 @@ public: bool isStereo() const { return _isStereo; } int getRate() const { return _rate; } - int32 getTotalPlayTime() const { - if (!_totalNumLoops) - return AudioStream::kUnknownPlayTime; - -#ifdef USE_TREMOR - return (_endTime - _startTime) * _totalNumLoops; -#else - return (int32)((_endTime - _startTime) * 1000.0) * _totalNumLoops; -#endif - } - + bool seek(const Timestamp &where); + Timestamp getLength() const { return _length; } protected: bool refill(); }; -VorbisInputStream::VorbisInputStream(Common::SeekableReadStream *inStream, bool dispose, uint startTime, uint endTime, uint numLoops) : +VorbisInputStream::VorbisInputStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) : _inStream(inStream), _disposeAfterUse(dispose), - _numLoops(numLoops), - _totalNumLoops(numLoops), + _length(0, 1000), _bufferEnd(_buffer + ARRAYSIZE(_buffer)) { int res = ov_open_callbacks(inStream, &_ovFile, NULL, 0, g_stream_wrap); @@ -149,42 +131,6 @@ VorbisInputStream::VorbisInputStream(Common::SeekableReadStream *inStream, bool return; } -#ifdef USE_TREMOR - /* TODO: Symbian may have to use scumm_fixdfdi here? To quote: - "SumthinWicked says: fixing "relocation truncated to fit: ARM_26 __fixdfdi" during linking on GCC, see portdefs.h" - */ - - // In Tremor, the ov_time_seek() and ov_time_seek_page() calls take seeking - // positions in milliseconds as 64 bit integers, rather than in seconds as - // doubles as in Vorbisfile. - ogg_int64_t totalTime; - _startTime = startTime; - _endTime = endTime; -#else - double totalTime; - _startTime = startTime / 1000.0; - _endTime = endTime / 1000.0; -#endif - - // If endTime was 0, or is past the end of the file, set it to the maximal time possible - totalTime = ov_time_total(&_ovFile, -1); - if (_endTime == 0 || _endTime > totalTime) - _endTime = totalTime; - - // If the specified time range is empty, abort early. - if (_startTime >= _endTime) { - _pos = _bufferEnd; - return; - } - - // Seek to the start position - res = ov_time_seek(&_ovFile, _startTime); - if (res < 0) { - warning("Error seeking in Vorbis stream (%d)", res); - _pos = _bufferEnd; - return; - } - // Read in initial data if (!refill()) return; @@ -192,16 +138,22 @@ VorbisInputStream::VorbisInputStream(Common::SeekableReadStream *inStream, bool // Setup some header information _isStereo = ov_info(&_ovFile, -1)->channels >= 2; _rate = ov_info(&_ovFile, -1)->rate; + +#ifdef USE_TREMOR + _length = Timestamp(ov_time_total(&_ovFile, -1), getRate()); +#else + _length = Timestamp(uint32(ov_time_total(&_ovFile, -1) * 1000.0), getRate()); +#endif } VorbisInputStream::~VorbisInputStream() { ov_clear(&_ovFile); - if (_disposeAfterUse) + if (_disposeAfterUse == DisposeAfterUse::YES) delete _inStream; } int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) { - int res, samples = 0; + int samples = 0; while (samples < numSamples && _pos < _bufferEnd) { const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos)); memcpy(buffer, _pos, len * 2); @@ -211,53 +163,30 @@ int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) { if (_pos >= _bufferEnd) { if (!refill()) break; - - // If we are still out of data, and also past the end of specified - // time range, check whether looping is enabled... - if (_pos >= _bufferEnd && ov_time_tell(&_ovFile) >= _endTime) { - if (_numLoops != 1) { - // If looping is on and there are loops left, rewind to the start - if (_numLoops != 0) - _numLoops--; - - res = ov_time_seek(&_ovFile, _startTime); - if (res < 0) { - warning("Error seeking in Vorbis stream (%d)", res); - _pos = _bufferEnd; - break; - } - - if (!refill()) - break; - } - } } } return samples; } +bool VorbisInputStream::seek(const Timestamp &where) { + int res = ov_pcm_seek(&_ovFile, calculateSampleOffset(where, getRate())); + if (res) { + warning("Error seeking in Vorbis stream (%d)", res); + _pos = _bufferEnd; + return false; + } + + return refill(); +} + bool VorbisInputStream::refill() { // Read the samples - int res; uint len_left = sizeof(_buffer); char *read_pos = (char *)_buffer; while (len_left > 0) { - if (ov_time_tell(&_ovFile) >= _endTime) { - // If looping is on and there are loops left, rewind to the start - if (_numLoops == 1) - break; // Last loop, abort - if (_numLoops != 0) - _numLoops--; - res = ov_time_seek(&_ovFile, _startTime); - if (res < 0) { - warning("Error seeking in Vorbis stream (%d)", res); - _pos = _bufferEnd; - return false; - } - } - long result; + #ifdef USE_TREMOR // Tremor ov_read() always returns data as signed 16 bit interleaved PCM // in host byte order. As such, it does not take arguments to request @@ -283,9 +212,10 @@ bool VorbisInputStream::refill() { // Possibly recoverable, just warn about it warning("Corrupted data in Vorbis file"); } else if (result == 0) { - warning("End of file while reading from Vorbis file"); - _pos = _bufferEnd; - return false; + //warning("End of file while reading from Vorbis file"); + //_pos = _bufferEnd; + //return false; + break; } else if (result < 0) { warning("Error reading from Vorbis stream (%d)", int(result)); _pos = _bufferEnd; @@ -309,27 +239,12 @@ bool VorbisInputStream::refill() { #pragma mark --- Ogg Vorbis factory functions --- #pragma mark - - -AudioStream *makeVorbisStream( +SeekableAudioStream *makeVorbisStream( Common::SeekableReadStream *stream, - bool disposeAfterUse, - uint32 startTime, - uint32 duration, - uint numLoops) { - - uint32 endTime = duration ? (startTime + duration) : 0; - - VorbisInputStream *input = new VorbisInputStream(stream, disposeAfterUse, startTime, endTime, numLoops); - - if (input->endOfData()) { - delete input; - return 0; - } - - return input; + DisposeAfterUse::Flag disposeAfterUse) { + return new VorbisInputStream(stream, disposeAfterUse); } - } // End of namespace Audio #endif // #ifdef USE_VORBIS diff --git a/sound/vorbis.h b/sound/vorbis.h index 554414f7ba6..64508856a0e 100644 --- a/sound/vorbis.h +++ b/sound/vorbis.h @@ -24,6 +24,7 @@ */ /** + * @file * Sound decoder used in engines: * - agos * - kyra @@ -40,6 +41,7 @@ #ifndef SOUND_VORBIS_H #define SOUND_VORBIS_H +#include "common/types.h" #include "common/sys.h" #ifdef USE_VORBIS @@ -51,26 +53,19 @@ namespace Common { namespace Audio { class AudioStream; +class SeekableAudioStream; /** - * Create a new AudioStream from the Ogg Vorbis data in the given stream. - * Allows for looping (which is why we require a SeekableReadStream), - * and specifying only a portion of the data to be played, based - * on time offsets. + * Create a new SeekableAudioStream from the Ogg Vorbis data in the given stream. + * Allows for seeking (which is why we require a SeekableReadStream). * * @param stream the SeekableReadStream from which to read the Ogg Vorbis data * @param disposeAfterUse whether to delete the stream after use - * @param startTime the (optional) time offset in milliseconds from which to start playback - * @param duration the (optional) time in milliseconds specifying how long to play - * @param numLoops how often the data shall be looped (0 = infinite) - * @return a new AudioStream, or NULL, if an error occured + * @return a new SeekableAudioStream, or NULL, if an error occured */ -AudioStream *makeVorbisStream( +SeekableAudioStream *makeVorbisStream( Common::SeekableReadStream *stream, - bool disposeAfterUse, - uint32 startTime = 0, - uint32 duration = 0, - uint numLoops = 1); + DisposeAfterUse::Flag disposeAfterUse); } // End of namespace Audio