mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-03 17:33:05 +00:00
synced rest files, missed with last sync rev 47951
This commit is contained in:
parent
41be88f8b7
commit
bccbcae43c
4
Makefile
4
Makefile
@ -9,7 +9,7 @@ srcdir ?= .
|
||||
|
||||
DEFINES := -DHAVE_CONFIG_H
|
||||
LDFLAGS :=
|
||||
INCLUDES := -I. -I$(srcdir)
|
||||
INCLUDES := -I. -I$(srcdir) -I$(srcdir)/engines
|
||||
LIBS :=
|
||||
OBJS :=
|
||||
DEPDIR := .deps
|
||||
@ -34,7 +34,7 @@ ifeq "$(HAVE_GCC)" "1"
|
||||
# being helpful.
|
||||
#CXXFLAGS+= -Wmissing-format-attribute
|
||||
|
||||
# Disable RTTI and exceptions, and enabled checking of pointers returned by "new"
|
||||
# Disable RTTI, and enable checking of pointers returned by "new"
|
||||
CXXFLAGS+= -fno-exceptions -fcheck-new
|
||||
|
||||
# There is a nice extra warning that flags variables that are potentially
|
||||
|
@ -226,7 +226,5 @@ DIST_FILES_THEMES:=$(addprefix $(srcdir)/gui/themes/,modern.zip)
|
||||
DIST_FILES_ENGINEDATA=
|
||||
DIST_FILES_ENGINEDATA:=$(addprefix $(srcdir)/dists/engine-data/,$(DIST_FILES_ENGINEDATA))
|
||||
|
||||
# Plugin files
|
||||
DIST_FILES_PLUGINS:=$(addprefix $(srcdir)/,$(PLUGINS))
|
||||
|
||||
.PHONY: all clean distclean plugins dist-src
|
||||
|
@ -66,19 +66,23 @@ static const char HELP_STRING[] =
|
||||
" -c, --config=CONFIG Use alternate configuration file\n"
|
||||
" -p, --path=PATH Path to where the game is installed\n"
|
||||
" -f, --fullscreen Force full-screen mode\n"
|
||||
" -q, --language=LANG Select language (en,de,fr,it,pt,es,jp,zh,kr,se,gb,\n"
|
||||
" hb,ru,cz)\n"
|
||||
" -F, --no-fullscreen Force windowed mode\n"
|
||||
" --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"
|
||||
" -e, --music-driver=MODE Select music driver (see README for details)\n"
|
||||
" -q, --language=LANG Select language (en,de,fr,it,pt,es,jp,zh,kr,se,gb,\n"
|
||||
" hb,ru,cz)\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"
|
||||
" --speech-mode=NUM Set the mode of speech 1-Text only, 2-Speech Only, 3-Speech and Text\n"
|
||||
" --soft-renderer=BOOL Set the turn on/off software 3D renderer: true/false\n"
|
||||
" --midi-gain=NUM Set the gain for MIDI playback, 0-1000 (default:\n"
|
||||
" 100) (only supported by some MIDI drivers)\n"
|
||||
" -d, --debuglevel=NUM Set debug verbosity level\n"
|
||||
" --debugflags=FLAGS Enables engine specific debug flags\n"
|
||||
" --debugflags=FLAGS Enable engine specific debug flags\n"
|
||||
" (separated by commas)\n"
|
||||
"\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"
|
||||
@ -89,8 +93,10 @@ static const char HELP_STRING[] =
|
||||
" --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"
|
||||
|
||||
" --soft-renderer=BOOL Set the turn on/off software 3D renderer: true/false\n"
|
||||
"\n"
|
||||
" --dimuse-tempo=NUM Set internal Digital iMuse tempo (10 - 100) per second\n"
|
||||
" (default: 10)\n"
|
||||
;
|
||||
#endif
|
||||
|
||||
@ -116,26 +122,37 @@ static void usage(const char *s, ...) {
|
||||
|
||||
|
||||
void registerDefaults() {
|
||||
ConfMan.registerDefault("platform", Common::kPlatformPC);
|
||||
ConfMan.registerDefault("language", "en");
|
||||
ConfMan.registerDefault("autosave_period", 5 * 60); // By default, trigger autosave every 5 minutes
|
||||
// Graphics
|
||||
ConfMan.registerDefault("fullscreen", false);
|
||||
ConfMan.registerDefault("soft_renderer", "true");
|
||||
ConfMan.registerDefault("fullscreen", "false");
|
||||
ConfMan.registerDefault("show_fps", "false");
|
||||
|
||||
// Sound & Music
|
||||
ConfMan.registerDefault("music_volume", 127);
|
||||
ConfMan.registerDefault("sfx_volume", 127);
|
||||
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", ".");
|
||||
// Game specific
|
||||
ConfMan.registerDefault("path", "");
|
||||
ConfMan.registerDefault("platform", Common::kPlatformPC);
|
||||
ConfMan.registerDefault("language", "en");
|
||||
ConfMan.registerDefault("autosave_period", 5 * 60); // By default, trigger autosave every 5 minutes
|
||||
|
||||
ConfMan.registerDefault("soft_renderer", "true");
|
||||
ConfMan.registerDefault("fullscreen", "false");
|
||||
ConfMan.registerDefault("show_fps", "false");
|
||||
|
||||
|
||||
|
||||
|
||||
ConfMan.registerDefault("dimuse_tempo", 10);
|
||||
|
||||
|
||||
|
||||
// Miscellaneous
|
||||
ConfMan.registerDefault("confirm_exit", false);
|
||||
ConfMan.registerDefault("disable_sdl_parachute", false);
|
||||
|
||||
@ -397,10 +414,10 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha
|
||||
|
||||
DO_LONG_OPTION("text-speed")
|
||||
END_OPTION
|
||||
#ifdef ENABLE_SCUMM
|
||||
|
||||
DO_LONG_OPTION_INT("dimuse-tempo")
|
||||
END_OPTION
|
||||
#endif
|
||||
|
||||
|
||||
DO_LONG_OPTION("speech-mode")
|
||||
END_OPTION
|
||||
|
@ -87,7 +87,7 @@ static const EnginePlugin *detectPlugin() {
|
||||
assert(!gameid.empty());
|
||||
if (ConfMan.hasKey("gameid")) {
|
||||
gameid = ConfMan.get("gameid");
|
||||
|
||||
|
||||
// Set last selected game, that the game will be highlighted
|
||||
// on RTL
|
||||
ConfMan.set("lastselectedgame", ConfMan.getActiveDomainName(), Common::ConfigManager::kApplicationDomain);
|
||||
|
@ -65,6 +65,7 @@ namespace Common {
|
||||
enum PluginType {
|
||||
PLUGIN_TYPE_ENGINE = 0,
|
||||
PLUGIN_TYPE_MUSIC,
|
||||
/* PLUGIN_TYPE_SCALER, */ // TODO: Add graphics scaler plugins
|
||||
|
||||
PLUGIN_TYPE_MAX
|
||||
};
|
||||
|
@ -100,7 +100,7 @@ private:
|
||||
Common::String _recordTimeFileName;
|
||||
};
|
||||
|
||||
} // end of namespace Common
|
||||
} // End of namespace Common
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -27,15 +27,11 @@
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-str.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
|
||||
#include <stdarg.h> // For va_list etc.
|
||||
|
||||
|
||||
#ifdef __PLAYSTATION2__
|
||||
// for those replaced fopen/fread/etc functions
|
||||
typedef unsigned long uint64;
|
||||
typedef signed long int64;
|
||||
#include "backends/platform/ps2/fileio.h"
|
||||
|
||||
#define fputs(str, file) ps2_fputs(str, file)
|
||||
@ -52,16 +48,14 @@
|
||||
// TODO: Move gDebugLevel into namespace Common.
|
||||
int gDebugLevel = -1;
|
||||
|
||||
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace {
|
||||
|
||||
typedef HashMap<String, DebugChannel, IgnoreCase_Hash, IgnoreCase_EqualTo> DebugLevelMap;
|
||||
typedef HashMap<String, DebugChannel, IgnoreCase_Hash, IgnoreCase_EqualTo> DebugChannelMap;
|
||||
|
||||
static DebugLevelMap gDebugLevels;
|
||||
static uint32 gDebugLevelsEnabled = 0;
|
||||
static DebugChannelMap gDebugChannels;
|
||||
static uint32 gDebugChannelsEnabled = 0;
|
||||
|
||||
struct DebugLevelComperator {
|
||||
bool operator()(const DebugChannel &l, const DebugChannel &r) {
|
||||
@ -69,27 +63,27 @@ struct DebugLevelComperator {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // end of anonymous namespace
|
||||
|
||||
bool addDebugChannel(uint32 level, const String &name, const String &description) {
|
||||
if (gDebugLevels.contains(name)) {
|
||||
warning("Duplicate declaration of engine debug level '%s'", name.c_str());
|
||||
}
|
||||
gDebugLevels[name] = DebugChannel(level, name, description);
|
||||
bool addDebugChannel(uint32 channel, const String &name, const String &description) {
|
||||
if (gDebugChannels.contains(name))
|
||||
warning("Duplicate declaration of engine debug channel '%s'", name.c_str());
|
||||
|
||||
gDebugChannels[name] = DebugChannel(channel, name, description);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void clearAllDebugChannels() {
|
||||
gDebugLevelsEnabled = 0;
|
||||
gDebugLevels.clear();
|
||||
gDebugChannelsEnabled = 0;
|
||||
gDebugChannels.clear();
|
||||
}
|
||||
|
||||
bool enableDebugChannel(const String &name) {
|
||||
DebugLevelMap::iterator i = gDebugLevels.find(name);
|
||||
DebugChannelMap::iterator i = gDebugChannels.find(name);
|
||||
|
||||
if (i != gDebugLevels.end()) {
|
||||
gDebugLevelsEnabled |= i->_value.level;
|
||||
if (i != gDebugChannels.end()) {
|
||||
gDebugChannelsEnabled |= i->_value.channel;
|
||||
i->_value.enabled = true;
|
||||
|
||||
return true;
|
||||
@ -99,10 +93,10 @@ bool enableDebugChannel(const String &name) {
|
||||
}
|
||||
|
||||
bool disableDebugChannel(const String &name) {
|
||||
DebugLevelMap::iterator i = gDebugLevels.find(name);
|
||||
DebugChannelMap::iterator i = gDebugChannels.find(name);
|
||||
|
||||
if (i != gDebugLevels.end()) {
|
||||
gDebugLevelsEnabled &= ~i->_value.level;
|
||||
if (i != gDebugChannels.end()) {
|
||||
gDebugChannelsEnabled &= ~i->_value.channel;
|
||||
i->_value.enabled = false;
|
||||
|
||||
return true;
|
||||
@ -114,19 +108,19 @@ bool disableDebugChannel(const String &name) {
|
||||
|
||||
DebugChannelList listDebugChannels() {
|
||||
DebugChannelList tmp;
|
||||
for (DebugLevelMap::iterator i = gDebugLevels.begin(); i != gDebugLevels.end(); ++i)
|
||||
for (DebugChannelMap::iterator i = gDebugChannels.begin(); i != gDebugChannels.end(); ++i)
|
||||
tmp.push_back(i->_value);
|
||||
sort(tmp.begin(), tmp.end(), DebugLevelComperator());
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool isDebugChannelEnabled(uint32 level) {
|
||||
bool isDebugChannelEnabled(uint32 channel) {
|
||||
// Debug level 11 turns on all special debug level messages
|
||||
if (gDebugLevel == 11)
|
||||
return true;
|
||||
// return gDebugLevelsEnabled & (1 << level);
|
||||
return gDebugLevelsEnabled & level;
|
||||
else
|
||||
return (gDebugChannelsEnabled & channel) != 0;
|
||||
}
|
||||
|
||||
bool isDebugChannelEnabled(const String &name) {
|
||||
@ -135,10 +129,11 @@ bool isDebugChannelEnabled(const String &name) {
|
||||
return true;
|
||||
|
||||
// Search for the debug level with the given name and check if it is enabled
|
||||
DebugLevelMap::iterator i = gDebugLevels.find(name);
|
||||
if (i != gDebugLevels.end())
|
||||
DebugChannelMap::iterator i = gDebugChannels.find(name);
|
||||
if (i != gDebugChannels.end())
|
||||
return i->_value.enabled;
|
||||
return false;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -224,7 +219,7 @@ void debugC(int level, uint32 debugChannels, const char *s, ...) {
|
||||
|
||||
// Debug level 11 turns on all special debug level messages
|
||||
if (gDebugLevel != 11)
|
||||
if (level > gDebugLevel || !(Common::gDebugLevelsEnabled & debugChannels))
|
||||
if (level > gDebugLevel || !(Common::gDebugChannelsEnabled & debugChannels))
|
||||
return;
|
||||
|
||||
va_start(va, s);
|
||||
@ -237,7 +232,7 @@ void debugCN(int level, uint32 debugChannels, const char *s, ...) {
|
||||
|
||||
// Debug level 11 turns on all special debug level messages
|
||||
if (gDebugLevel != 11)
|
||||
if (level > gDebugLevel || !(Common::gDebugLevelsEnabled & debugChannels))
|
||||
if (level > gDebugLevel || !(Common::gDebugChannelsEnabled & debugChannels))
|
||||
return;
|
||||
|
||||
va_start(va, s);
|
||||
@ -250,7 +245,7 @@ void debugC(uint32 debugChannels, const char *s, ...) {
|
||||
|
||||
// Debug level 11 turns on all special debug level messages
|
||||
if (gDebugLevel != 11)
|
||||
if (!(Common::gDebugLevelsEnabled & debugChannels))
|
||||
if (!(Common::gDebugChannelsEnabled & debugChannels))
|
||||
return;
|
||||
|
||||
va_start(va, s);
|
||||
@ -263,7 +258,7 @@ void debugCN(uint32 debugChannels, const char *s, ...) {
|
||||
|
||||
// Debug level 11 turns on all special debug level messages
|
||||
if (gDebugLevel != 11)
|
||||
if (!(Common::gDebugLevelsEnabled & debugChannels))
|
||||
if (!(Common::gDebugChannelsEnabled & debugChannels))
|
||||
return;
|
||||
|
||||
va_start(va, s);
|
||||
|
@ -35,42 +35,58 @@ namespace Common {
|
||||
|
||||
|
||||
struct DebugChannel {
|
||||
DebugChannel() : level(0), enabled(false) {}
|
||||
DebugChannel(uint32 l, const String &n, const String &d)
|
||||
: name(n), description(d), level(l), enabled(false) {}
|
||||
DebugChannel() : channel(0), enabled(false) {}
|
||||
DebugChannel(uint32 c, const String &n, const String &d)
|
||||
: name(n), description(d), channel(c), enabled(false) {}
|
||||
|
||||
String name;
|
||||
String description;
|
||||
|
||||
uint32 level;
|
||||
uint32 channel;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a engine debug level.
|
||||
* @param level the level flag (should be OR-able i.e. first one should be 1 than 2,4,...)
|
||||
* Adds a debug channel.
|
||||
*
|
||||
* A debug channel is considered roughly similar to what our debug levels described by
|
||||
* gDebugLevel try to achieve:
|
||||
*
|
||||
* Debug channels should only affect the display of additional debug output, based on
|
||||
* their state. That is if they are enabled, channel specific debug messages should
|
||||
* be shown. If they are disabled on the other hand, those messages will be hidden.
|
||||
*
|
||||
* @see gDebugLevel.
|
||||
*
|
||||
* Note that we have debug* functions which depend both on the debug level set and
|
||||
* specific debug channels. Those functions will only show output, when *both* criteria
|
||||
* are satisfied.
|
||||
*
|
||||
* @param channel the channel flag (should be OR-able i.e. first one should be 1 then 2, 4, etc.)
|
||||
* @param name the option name which is used in the debugger/on the command line to enable
|
||||
* this special debug level (case will be ignored)
|
||||
* this special debug level (case will be ignored)
|
||||
* @param description the description which shows up in the debugger
|
||||
* @return true on success false on failure
|
||||
*/
|
||||
bool addDebugChannel(uint32 level, const String &name, const String &description);
|
||||
bool addDebugChannel(uint32 channel, const String &name, const String &description);
|
||||
|
||||
/**
|
||||
* Resets all engine debug levels.
|
||||
* Resets all engine specific debug channels.
|
||||
*/
|
||||
void clearAllDebugChannels();
|
||||
|
||||
/**
|
||||
* Enables an engine debug level.
|
||||
* @param name the name of the debug level to enable
|
||||
* Enables an debug channel.
|
||||
*
|
||||
* @param name the name of the debug channel to enable
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool enableDebugChannel(const String &name);
|
||||
|
||||
/**
|
||||
* Disables an engine debug level
|
||||
* @param name the name of the debug level to disable
|
||||
* Disables an debug channel.
|
||||
*
|
||||
* @param name the name of the debug channel to disable
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool disableDebugChannel(const String &name);
|
||||
@ -80,19 +96,20 @@ bool disableDebugChannel(const String &name);
|
||||
typedef List<DebugChannel> DebugChannelList;
|
||||
|
||||
/**
|
||||
* Lists all debug levels
|
||||
* @return returns a arry with all debug levels
|
||||
* Lists all engine specific debug channels.
|
||||
*
|
||||
* @return returns a arry with all debug channels
|
||||
*/
|
||||
DebugChannelList listDebugChannels();
|
||||
|
||||
|
||||
/**
|
||||
* Test whether the given debug level is enabled.
|
||||
* Test whether the given debug channel is enabled.
|
||||
*/
|
||||
bool isDebugChannelEnabled(uint32 level);
|
||||
bool isDebugChannelEnabled(uint32 channel);
|
||||
|
||||
/**
|
||||
* Test whether the given debug level is enabled.
|
||||
* Test whether the given debug channel is enabled.
|
||||
*/
|
||||
bool isDebugChannelEnabled(const String &name);
|
||||
|
||||
@ -111,10 +128,10 @@ void setDebugOutputFormatter(OutputFormatter f);
|
||||
inline void debug(const char *s, ...) {}
|
||||
inline void debug(int level, const char *s, ...) {}
|
||||
inline void debugN(int level, const char *s, ...) {}
|
||||
inline void debugC(int level, uint32 engine_level, const char *s, ...) {}
|
||||
inline void debugC(uint32 engine_level, const char *s, ...) {}
|
||||
inline void debugCN(int level, uint32 engine_level, const char *s, ...) {}
|
||||
inline void debugCN(uint32 engine_level, const char *s, ...) {}
|
||||
inline void debugC(int level, uint32 engineChannel, const char *s, ...) {}
|
||||
inline void debugC(uint32 engineChannel, const char *s, ...) {}
|
||||
inline void debugCN(int level, uint32 engineChannel, const char *s, ...) {}
|
||||
inline void debugCN(uint32 engineChannel, const char *s, ...) {}
|
||||
|
||||
|
||||
#else
|
||||
|
@ -44,7 +44,7 @@
|
||||
* FROM_??_??(a) - convert LE/BE value v to native
|
||||
* CONSTANT_??_??(a) - convert LE/BE value v to native, implemented as macro.
|
||||
* Use with compiletime-constants only, the result will be a compiletime-constant aswell.
|
||||
* Unlike most other functions these can be used for eg. switch-case labels
|
||||
* Unlike most other functions these can be used for eg. switch-case labels
|
||||
*/
|
||||
|
||||
// Sanity check
|
||||
@ -103,7 +103,7 @@
|
||||
}
|
||||
|
||||
// generic fallback
|
||||
#else
|
||||
#else
|
||||
|
||||
inline uint32 SWAP_BYTES_32(uint32 a) {
|
||||
const uint16 low = (uint16)a, high = (uint16)(a >> 16);
|
||||
@ -184,7 +184,7 @@
|
||||
*(uint32 *)(ptr) = value;
|
||||
}
|
||||
|
||||
// test for GCC >= 4.0. these implementations will automatically use CPU-specific
|
||||
// test for GCC >= 4.0. these implementations will automatically use CPU-specific
|
||||
// instructions for unaligned data when they are available (eg. MIPS)
|
||||
#elif defined(__GNUC__) && (__GNUC__ >= 4)
|
||||
|
||||
@ -326,7 +326,7 @@
|
||||
inline void WRITE_BE_UINT32(void *ptr, uint32 value) {
|
||||
WRITE_UINT32(ptr, SWAP_BYTES_32(value));
|
||||
}
|
||||
|
||||
|
||||
# endif // if defined(SYSTEM_NEED_ALIGNMENT)
|
||||
|
||||
#elif defined(SYSTEM_BIG_ENDIAN)
|
||||
@ -394,7 +394,7 @@
|
||||
inline void WRITE_LE_UINT32(void *ptr, uint32 value) {
|
||||
WRITE_UINT32(ptr, SWAP_BYTES_32(value));
|
||||
}
|
||||
|
||||
|
||||
# endif // if defined(SYSTEM_NEED_ALIGNMENT)
|
||||
|
||||
#endif // if defined(SYSTEM_LITTLE_ENDIAN)
|
||||
|
@ -40,9 +40,9 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
// The sgi IRIX MIPSpro Compiler has difficulties with nested templates.
|
||||
// The sgi IRIX MIPSpro Compiler has difficulties with nested templates.
|
||||
// This and the other __sgi conditionals below work around these problems.
|
||||
#if defined(__sgi) && !defined(__GNUC__)
|
||||
#ifdef __sgi
|
||||
template<class T> class IteratorImpl;
|
||||
#endif
|
||||
|
||||
@ -131,7 +131,7 @@ public:
|
||||
int lookupAndCreateIfMissing(const Key &key);
|
||||
void expandStorage(uint newCapacity);
|
||||
|
||||
#if !defined(__sgi) || defined(__GNUC__)
|
||||
#ifndef __sgi
|
||||
template<class T> friend class IteratorImpl;
|
||||
#endif
|
||||
|
||||
@ -141,7 +141,7 @@ public:
|
||||
template<class NodeType>
|
||||
class IteratorImpl {
|
||||
friend class HashMap;
|
||||
#if defined(__sgi) && !defined(__GNUC__)
|
||||
#ifdef __sgi
|
||||
template<class T> friend class Common::IteratorImpl;
|
||||
#else
|
||||
template<class T> friend class IteratorImpl;
|
||||
|
334
common/libz.cpp
334
common/libz.cpp
@ -1,334 +0,0 @@
|
||||
/* 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/libz.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#if defined(USE_ZLIB)
|
||||
#ifdef __SYMBIAN32__
|
||||
#include <zlib\zlib.h>
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#if ZLIB_VERNUM < 0x1204
|
||||
#error Version 1.2.0.4 or newer of zlib is required for this code
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if defined(USE_ZLIB)
|
||||
|
||||
bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long srcLen) {
|
||||
return Z_OK == ::uncompress(dst, dstLen, src, srcLen);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple wrapper class which can be used to wrap around an arbitrary
|
||||
* other SeekableReadStream and will then provide on-the-fly decompression support.
|
||||
* Assumes the compressed data to be in gzip format.
|
||||
*/
|
||||
class GZipReadStream : public Common::SeekableReadStream {
|
||||
protected:
|
||||
enum {
|
||||
BUFSIZE = 16384 // 1 << MAX_WBITS
|
||||
};
|
||||
|
||||
byte _buf[BUFSIZE];
|
||||
|
||||
Common::SeekableReadStream *_wrapped;
|
||||
z_stream _stream;
|
||||
int _zlibErr;
|
||||
uint32 _pos;
|
||||
uint32 _origSize;
|
||||
bool _eos;
|
||||
|
||||
public:
|
||||
|
||||
GZipReadStream(Common::SeekableReadStream *w) : _wrapped(w) {
|
||||
assert(w != 0);
|
||||
|
||||
_stream.zalloc = Z_NULL;
|
||||
_stream.zfree = Z_NULL;
|
||||
_stream.opaque = Z_NULL;
|
||||
|
||||
// Verify file header is correct
|
||||
w->seek(0, SEEK_SET);
|
||||
uint16 header = w->readUint16BE();
|
||||
assert(header == 0x1F8B ||
|
||||
((header & 0x0F00) == 0x0800 && header % 31 == 0));
|
||||
|
||||
if (header == 0x1F8B) {
|
||||
// Retrieve the original file size
|
||||
w->seek(-4, SEEK_END);
|
||||
_origSize = w->readUint32LE();
|
||||
} else {
|
||||
// Original size not available in zlib format
|
||||
_origSize = 0;
|
||||
}
|
||||
_pos = 0;
|
||||
w->seek(0, SEEK_SET);
|
||||
_eos = false;
|
||||
|
||||
// Adding 32 to windowBits indicates to zlib that it is supposed to
|
||||
// automatically detect whether gzip or zlib headers are used for
|
||||
// the compressed file. This feature was added in zlib 1.2.0.4,
|
||||
// released 10 August 2003.
|
||||
// Note: This is *crucial* for savegame compatibility, do *not* remove!
|
||||
_zlibErr = inflateInit2(&_stream, MAX_WBITS + 32);
|
||||
if (_zlibErr != Z_OK)
|
||||
return;
|
||||
|
||||
// Setup input buffer
|
||||
_stream.next_in = _buf;
|
||||
_stream.avail_in = 0;
|
||||
}
|
||||
|
||||
~GZipReadStream() {
|
||||
inflateEnd(&_stream);
|
||||
delete _wrapped;
|
||||
}
|
||||
|
||||
bool err() const { return (_zlibErr != Z_OK) && (_zlibErr != Z_STREAM_END); }
|
||||
void clearErr() {
|
||||
// only reset _eos; I/O errors are not recoverable
|
||||
_eos = false;
|
||||
}
|
||||
|
||||
uint32 read(void *dataPtr, uint32 dataSize) {
|
||||
_stream.next_out = (byte *)dataPtr;
|
||||
_stream.avail_out = dataSize;
|
||||
|
||||
// Keep going while we get no error
|
||||
while (_zlibErr == Z_OK && _stream.avail_out) {
|
||||
if (_stream.avail_in == 0 && !_wrapped->eos()) {
|
||||
// If we are out of input data: Read more data, if available.
|
||||
_stream.next_in = _buf;
|
||||
_stream.avail_in = _wrapped->read(_buf, BUFSIZE);
|
||||
}
|
||||
_zlibErr = inflate(&_stream, Z_NO_FLUSH);
|
||||
}
|
||||
|
||||
// Update the position counter
|
||||
_pos += dataSize - _stream.avail_out;
|
||||
|
||||
if (_zlibErr == Z_STREAM_END && _stream.avail_out > 0)
|
||||
_eos = true;
|
||||
|
||||
return dataSize - _stream.avail_out;
|
||||
}
|
||||
|
||||
bool eos() const {
|
||||
return _eos;
|
||||
}
|
||||
int32 pos() const {
|
||||
return _pos;
|
||||
}
|
||||
int32 size() const {
|
||||
return _origSize;
|
||||
}
|
||||
bool seek(int32 offset, int whence = SEEK_SET) {
|
||||
int32 newPos = 0;
|
||||
assert(whence != SEEK_END); // SEEK_END not supported
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
newPos = offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
newPos = _pos + offset;
|
||||
}
|
||||
|
||||
assert(newPos >= 0);
|
||||
|
||||
if ((uint32)newPos < _pos) {
|
||||
// To search backward, we have to restart the whole decompression
|
||||
// from the start of the file. A rather wasteful operation, best
|
||||
// to avoid it. :/
|
||||
#if DEBUG
|
||||
warning("Backward seeking in GZipReadStream detected");
|
||||
#endif
|
||||
_pos = 0;
|
||||
_wrapped->seek(0, SEEK_SET);
|
||||
_zlibErr = inflateReset(&_stream);
|
||||
if (_zlibErr != Z_OK)
|
||||
return false; // FIXME: STREAM REWRITE
|
||||
_stream.next_in = _buf;
|
||||
_stream.avail_in = 0;
|
||||
}
|
||||
|
||||
offset = newPos - _pos;
|
||||
|
||||
// Skip the given amount of data (very inefficient if one tries to skip
|
||||
// huge amounts of data, but usually client code will only skip a few
|
||||
// bytes, so this should be fine.
|
||||
byte tmpBuf[1024];
|
||||
while (!err() && offset > 0) {
|
||||
offset -= read(tmpBuf, MIN((int32)sizeof(tmpBuf), offset));
|
||||
}
|
||||
|
||||
_eos = false;
|
||||
return true; // FIXME: STREAM REWRITE
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A simple wrapper class which can be used to wrap around an arbitrary
|
||||
* other WriteStream and will then provide on-the-fly compression support.
|
||||
* The compressed data is written in the gzip format.
|
||||
*/
|
||||
class GZipWriteStream : public Common::WriteStream {
|
||||
protected:
|
||||
enum {
|
||||
BUFSIZE = 16384 // 1 << MAX_WBITS
|
||||
};
|
||||
|
||||
byte _buf[BUFSIZE];
|
||||
Common::WriteStream *_wrapped;
|
||||
z_stream _stream;
|
||||
int _zlibErr;
|
||||
|
||||
void processData(int flushType) {
|
||||
// This function is called by both write() and finalize().
|
||||
while (_zlibErr == Z_OK && (_stream.avail_in || flushType == Z_FINISH)) {
|
||||
if (_stream.avail_out == 0) {
|
||||
if (_wrapped->write(_buf, BUFSIZE) != BUFSIZE) {
|
||||
_zlibErr = Z_ERRNO;
|
||||
break;
|
||||
}
|
||||
_stream.next_out = _buf;
|
||||
_stream.avail_out = BUFSIZE;
|
||||
}
|
||||
_zlibErr = deflate(&_stream, flushType);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
GZipWriteStream(Common::WriteStream *w) : _wrapped(w) {
|
||||
assert(w != 0);
|
||||
_stream.zalloc = Z_NULL;
|
||||
_stream.zfree = Z_NULL;
|
||||
_stream.opaque = Z_NULL;
|
||||
|
||||
// Adding 16 to windowBits indicates to zlib that it is supposed to
|
||||
// write gzip headers. This feature was added in zlib 1.2.0.4,
|
||||
// released 10 August 2003.
|
||||
// Note: This is *crucial* for savegame compatibility, do *not* remove!
|
||||
_zlibErr = deflateInit2(&_stream,
|
||||
Z_DEFAULT_COMPRESSION,
|
||||
Z_DEFLATED,
|
||||
MAX_WBITS + 16,
|
||||
8,
|
||||
Z_DEFAULT_STRATEGY);
|
||||
assert(_zlibErr == Z_OK);
|
||||
|
||||
_stream.next_out = _buf;
|
||||
_stream.avail_out = BUFSIZE;
|
||||
_stream.avail_in = 0;
|
||||
_stream.next_in = 0;
|
||||
}
|
||||
|
||||
~GZipWriteStream() {
|
||||
finalize();
|
||||
deflateEnd(&_stream);
|
||||
delete _wrapped;
|
||||
}
|
||||
|
||||
bool err() const {
|
||||
// CHECKME: does Z_STREAM_END make sense here?
|
||||
return (_zlibErr != Z_OK && _zlibErr != Z_STREAM_END) || _wrapped->err();
|
||||
}
|
||||
|
||||
void clearErr() {
|
||||
// Note: we don't reset the _zlibErr here, as it is not
|
||||
// clear in general how
|
||||
_wrapped->clearErr();
|
||||
}
|
||||
|
||||
void finalize() {
|
||||
if (_zlibErr != Z_OK)
|
||||
return;
|
||||
|
||||
// Process whatever remaining data there is.
|
||||
processData(Z_FINISH);
|
||||
|
||||
// Since processData only writes out blocks of size BUFSIZE,
|
||||
// we may have to flush some stragglers.
|
||||
uint remainder = BUFSIZE - _stream.avail_out;
|
||||
if (remainder > 0) {
|
||||
if (_wrapped->write(_buf, remainder) != remainder) {
|
||||
_zlibErr = Z_ERRNO;
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the wrapped savefile, too
|
||||
_wrapped->finalize();
|
||||
}
|
||||
|
||||
uint32 write(const void *dataPtr, uint32 dataSize) {
|
||||
if (err())
|
||||
return 0;
|
||||
|
||||
// Hook in the new data ...
|
||||
// Note: We need to make a const_cast here, as zlib is not aware
|
||||
// of the const keyword.
|
||||
_stream.next_in = const_cast<byte *>((const byte *)dataPtr);
|
||||
_stream.avail_in = dataSize;
|
||||
|
||||
// ... and flush it to disk
|
||||
processData(Z_NO_FLUSH);
|
||||
|
||||
return dataSize - _stream.avail_in;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // USE_ZLIB
|
||||
|
||||
Common::SeekableReadStream *wrapCompressedReadStream(Common::SeekableReadStream *toBeWrapped) {
|
||||
#if defined(USE_ZLIB)
|
||||
if (toBeWrapped) {
|
||||
uint16 header = toBeWrapped->readUint16BE();
|
||||
bool isCompressed = (header == 0x1F8B ||
|
||||
((header & 0x0F00) == 0x0800 &&
|
||||
header % 31 == 0));
|
||||
toBeWrapped->seek(-2, SEEK_CUR);
|
||||
if (isCompressed)
|
||||
return new GZipReadStream(toBeWrapped);
|
||||
}
|
||||
#endif
|
||||
return toBeWrapped;
|
||||
}
|
||||
|
||||
Common::WriteStream *wrapCompressedWriteStream(Common::WriteStream *toBeWrapped) {
|
||||
#if defined(USE_ZLIB)
|
||||
if (toBeWrapped)
|
||||
return new GZipWriteStream(toBeWrapped);
|
||||
#endif
|
||||
return toBeWrapped;
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Common
|
@ -1,72 +0,0 @@
|
||||
/* 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_ZLIB_H
|
||||
#define COMMON_ZLIB_H
|
||||
|
||||
#include "common/sys.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if defined(USE_ZLIB)
|
||||
|
||||
/**
|
||||
* Thin wrapper around zlib's uncompress() function. This wrapper makes
|
||||
* it possible to uncompress data in engines without being forced to link
|
||||
* them against zlib, thus simplifying the build system.
|
||||
*
|
||||
* @return true on success (i.e. Z_OK), false otherwise
|
||||
*/
|
||||
bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long srcLen);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Take an arbitrary SeekableReadStream and wrap it in a custom stream which
|
||||
* provides transparent on-the-fly decompression. Assumes the data it
|
||||
* retrieves from the wrapped stream to be either uncompressed or in gzip
|
||||
* format. In the former case, the original stream is returned unmodified
|
||||
* (and in particular, not wrapped).
|
||||
*
|
||||
* It is safe to call this with a NULL parameter (in this case, NULL is
|
||||
* returned).
|
||||
*/
|
||||
Common::SeekableReadStream *wrapCompressedReadStream(Common::SeekableReadStream *toBeWrapped);
|
||||
|
||||
/**
|
||||
* Take an arbitrary WriteStream and wrap it in a custom stream which provides
|
||||
* transparent on-the-fly compression. The compressed data is written in the
|
||||
* gzip format, unless ZLIB support has been disabled, in which case the given
|
||||
* stream is returned unmodified (and in particular, not wrapped).
|
||||
*
|
||||
* It is safe to call this with a NULL parameter (in this case, NULL is
|
||||
* returned).
|
||||
*/
|
||||
Common::WriteStream *wrapCompressedWriteStream(Common::WriteStream *toBeWrapped);
|
||||
|
||||
} // End of namespace Common
|
||||
|
||||
#endif
|
@ -4,23 +4,23 @@ MODULE_OBJS := \
|
||||
archive.o \
|
||||
config-file.o \
|
||||
config-manager.o \
|
||||
textconsole.o \
|
||||
debug.o \
|
||||
EventDispatcher.o \
|
||||
EventRecorder.o \
|
||||
file.o \
|
||||
fs.o \
|
||||
hashmap.o \
|
||||
libz.o \
|
||||
memorypool.o \
|
||||
md5.o \
|
||||
mutex.o \
|
||||
str.o \
|
||||
stream.o \
|
||||
textconsole.o \
|
||||
util.o \
|
||||
system.o \
|
||||
unzip.o \
|
||||
xmlparser.o
|
||||
xmlparser.o \
|
||||
zlib.o
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
18
common/ptr.h
18
common/ptr.h
@ -180,15 +180,15 @@ public:
|
||||
_pointer = 0;
|
||||
}
|
||||
|
||||
template<class T2>
|
||||
bool operator==(const Common::SharedPtr<T2> &r) const {
|
||||
return _pointer == r.get();
|
||||
}
|
||||
template<class T2>
|
||||
bool operator==(const Common::SharedPtr<T2> &r) const {
|
||||
return _pointer == r.get();
|
||||
}
|
||||
|
||||
template<class T2>
|
||||
bool operator!=(const Common::SharedPtr<T2> &r) const {
|
||||
return _pointer != r.get();
|
||||
}
|
||||
template<class T2>
|
||||
bool operator!=(const Common::SharedPtr<T2> &r) const {
|
||||
return _pointer != r.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of references to the assigned pointer.
|
||||
@ -216,6 +216,6 @@ private:
|
||||
T *_pointer;
|
||||
};
|
||||
|
||||
} // end of namespace Common
|
||||
} // End of namespace Common
|
||||
|
||||
#endif
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now
|
||||
MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now
|
||||
|
||||
static uint32 computeCapacity(uint32 len) {
|
||||
// By default, for the capacity we use the next multiple of 32
|
||||
@ -84,7 +84,7 @@ void String::initWithCStr(const char *str, uint32 len) {
|
||||
}
|
||||
|
||||
String::String(const String &str)
|
||||
: _size(str._size) {
|
||||
: _size(str._size) {
|
||||
if (str.isStorageIntern()) {
|
||||
// String in internal storage: just copy it
|
||||
memcpy(_storage, str._storage, _builtinCapacity);
|
||||
@ -100,7 +100,7 @@ String::String(const String &str)
|
||||
}
|
||||
|
||||
String::String(char c)
|
||||
: _size(0), _str(_storage) {
|
||||
: _size(0), _str(_storage) {
|
||||
|
||||
_storage[0] = c;
|
||||
_storage[1] = 0;
|
||||
@ -219,7 +219,7 @@ void String::decRefCount(int *oldRefCount) {
|
||||
}
|
||||
}
|
||||
|
||||
String& String::operator =(const char *str) {
|
||||
String &String::operator=(const char *str) {
|
||||
uint32 len = strlen(str);
|
||||
ensureCapacity(len, false);
|
||||
_size = len;
|
||||
@ -227,7 +227,7 @@ String& String::operator =(const char *str) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
String &String::operator =(const String &str) {
|
||||
String &String::operator=(const String &str) {
|
||||
if (&str == this)
|
||||
return *this;
|
||||
|
||||
@ -249,7 +249,7 @@ String &String::operator =(const String &str) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator =(char c) {
|
||||
String &String::operator=(char c) {
|
||||
decRefCount(_extern._refCount);
|
||||
_str = _storage;
|
||||
_size = 1;
|
||||
@ -258,7 +258,7 @@ String& String::operator =(char c) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
String &String::operator +=(const char *str) {
|
||||
String &String::operator+=(const char *str) {
|
||||
if (_str <= str && str <= _str + _size)
|
||||
return operator+=(Common::String(str));
|
||||
|
||||
@ -272,7 +272,7 @@ String &String::operator +=(const char *str) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
String &String::operator +=(const String &str) {
|
||||
String &String::operator+=(const String &str) {
|
||||
if (&str == this)
|
||||
return operator+=(Common::String(str));
|
||||
|
||||
@ -286,7 +286,7 @@ String &String::operator +=(const String &str) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
String &String::operator +=(char c) {
|
||||
String &String::operator+=(char c) {
|
||||
ensureCapacity(_size + 1, true);
|
||||
|
||||
_str[_size++] = c;
|
||||
@ -363,7 +363,7 @@ void String::deleteChar(uint32 p) {
|
||||
|
||||
makeUnique();
|
||||
while (p++ < _size)
|
||||
_str[p-1] = _str[p];
|
||||
_str[p - 1] = _str[p];
|
||||
_size--;
|
||||
}
|
||||
|
||||
@ -388,7 +388,7 @@ void String::insertChar(char c, uint32 p) {
|
||||
ensureCapacity(_size + 1, true);
|
||||
_size++;
|
||||
for (uint32 i = _size; i > p; --i)
|
||||
_str[i] = _str[i-1];
|
||||
_str[i] = _str[i - 1];
|
||||
_str[p] = c;
|
||||
}
|
||||
|
||||
@ -411,8 +411,8 @@ void String::trim() {
|
||||
makeUnique();
|
||||
|
||||
// Trim trailing whitespace
|
||||
while (_size >= 1 && isspace(_str[_size-1]))
|
||||
_size--;
|
||||
while (_size >= 1 && isspace(_str[_size - 1]))
|
||||
--_size;
|
||||
_str[_size] = 0;
|
||||
|
||||
// Trim leading whitespace
|
||||
@ -449,7 +449,7 @@ String String::printf(const char *fmt, ...) {
|
||||
int size = _builtinCapacity;
|
||||
do {
|
||||
size *= 2;
|
||||
output.ensureCapacity(size-1, false);
|
||||
output.ensureCapacity(size - 1, false);
|
||||
assert(!output.isStorageIntern());
|
||||
size = output._extern._capacity;
|
||||
|
||||
@ -477,16 +477,16 @@ String String::printf(const char *fmt, ...) {
|
||||
|
||||
#pragma mark -
|
||||
|
||||
bool String::operator ==(const String &x) const {
|
||||
bool String::operator==(const String &x) const {
|
||||
return equals(x);
|
||||
}
|
||||
|
||||
bool String::operator ==(const char *x) const {
|
||||
bool String::operator==(const char *x) const {
|
||||
assert(x != 0);
|
||||
return equals(x);
|
||||
}
|
||||
|
||||
bool String::operator !=(const String &x) const {
|
||||
bool String::operator!=(const String &x) const {
|
||||
return !equals(x);
|
||||
}
|
||||
|
||||
@ -495,29 +495,29 @@ bool String::operator !=(const char *x) const {
|
||||
return !equals(x);
|
||||
}
|
||||
|
||||
bool String::operator < (const String &x) const {
|
||||
bool String::operator<(const String &x) const {
|
||||
return compareTo(x) < 0;
|
||||
}
|
||||
|
||||
bool String::operator <= (const String &x) const {
|
||||
bool String::operator<=(const String &x) const {
|
||||
return compareTo(x) <= 0;
|
||||
}
|
||||
|
||||
bool String::operator > (const String &x) const {
|
||||
bool String::operator>(const String &x) const {
|
||||
return compareTo(x) > 0;
|
||||
}
|
||||
|
||||
bool String::operator >= (const String &x) const {
|
||||
bool String::operator>=(const String &x) const {
|
||||
return compareTo(x) >= 0;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
bool operator == (const char* y, const String &x) {
|
||||
bool operator==(const char* y, const String &x) {
|
||||
return (x == y);
|
||||
}
|
||||
|
||||
bool operator != (const char* y, const String &x) {
|
||||
bool operator!=(const char* y, const String &x) {
|
||||
return x != y;
|
||||
}
|
||||
|
||||
@ -561,31 +561,31 @@ int String::compareToIgnoreCase(const char *x) const {
|
||||
|
||||
#pragma mark -
|
||||
|
||||
String operator +(const String &x, const String &y) {
|
||||
String operator+(const String &x, const String &y) {
|
||||
String temp(x);
|
||||
temp += y;
|
||||
return temp;
|
||||
}
|
||||
|
||||
String operator +(const char *x, const String &y) {
|
||||
String operator+(const char *x, const String &y) {
|
||||
String temp(x);
|
||||
temp += y;
|
||||
return temp;
|
||||
}
|
||||
|
||||
String operator +(const String &x, const char *y) {
|
||||
String operator+(const String &x, const char *y) {
|
||||
String temp(x);
|
||||
temp += y;
|
||||
return temp;
|
||||
}
|
||||
|
||||
String operator +(char x, const String &y) {
|
||||
String operator+(char x, const String &y) {
|
||||
String temp(x);
|
||||
temp += y;
|
||||
return temp;
|
||||
}
|
||||
|
||||
String operator +(const String &x, char y) {
|
||||
String operator+(const String &x, char y) {
|
||||
String temp(x);
|
||||
temp += y;
|
||||
return temp;
|
||||
|
116
common/str.h
116
common/str.h
@ -54,20 +54,20 @@ protected:
|
||||
* than 8 makes no sense, since that's the size of member _extern
|
||||
* (on 32 bit machines; 12 bytes on systems with 64bit pointers).
|
||||
*/
|
||||
static const uint32 _builtinCapacity = 32 - sizeof(uint32) - sizeof(char*);
|
||||
static const uint32 _builtinCapacity = 32 - sizeof(uint32) - sizeof(char *);
|
||||
|
||||
/**
|
||||
* Length of the string. Stored to avoid having to call strlen
|
||||
* a lot. Yes, we limit ourselves to strings shorter than 4GB --
|
||||
* on purpose :-).
|
||||
*/
|
||||
uint32 _size;
|
||||
uint32 _size;
|
||||
|
||||
/**
|
||||
* Pointer to the actual string storage. Either points to _storage,
|
||||
* or to a block allocated on the heap via malloc.
|
||||
*/
|
||||
char *_str;
|
||||
char *_str;
|
||||
|
||||
|
||||
union {
|
||||
@ -81,7 +81,7 @@ protected:
|
||||
*/
|
||||
struct {
|
||||
mutable int *_refCount;
|
||||
uint32 _capacity;
|
||||
uint32 _capacity;
|
||||
} _extern;
|
||||
};
|
||||
|
||||
@ -110,32 +110,32 @@ public:
|
||||
|
||||
~String();
|
||||
|
||||
String &operator =(const char *str);
|
||||
String &operator =(const String &str);
|
||||
String &operator =(char c);
|
||||
String &operator +=(const char *str);
|
||||
String &operator +=(const String &str);
|
||||
String &operator +=(char c);
|
||||
String &operator=(const char *str);
|
||||
String &operator=(const String &str);
|
||||
String &operator=(char c);
|
||||
String &operator+=(const char *str);
|
||||
String &operator+=(const String &str);
|
||||
String &operator+=(char c);
|
||||
|
||||
bool operator ==(const String &x) const;
|
||||
bool operator ==(const char *x) const;
|
||||
bool operator !=(const String &x) const;
|
||||
bool operator !=(const char *x) const;
|
||||
bool operator==(const String &x) const;
|
||||
bool operator==(const char *x) const;
|
||||
bool operator!=(const String &x) const;
|
||||
bool operator!=(const char *x) const;
|
||||
|
||||
bool operator <(const String &x) const;
|
||||
bool operator <=(const String &x) const;
|
||||
bool operator >(const String &x) const;
|
||||
bool operator >=(const String &x) const;
|
||||
bool operator<(const String &x) const;
|
||||
bool operator<=(const String &x) const;
|
||||
bool operator>(const String &x) const;
|
||||
bool operator>=(const String &x) const;
|
||||
|
||||
bool equals(const String &x) const;
|
||||
bool equalsIgnoreCase(const String &x) const;
|
||||
int compareTo(const String &x) const; // strcmp clone
|
||||
int compareToIgnoreCase(const String &x) const; // stricmp clone
|
||||
int compareTo(const String &x) const; // strcmp clone
|
||||
int compareToIgnoreCase(const String &x) const; // stricmp clone
|
||||
|
||||
bool equals(const char *x) const;
|
||||
bool equalsIgnoreCase(const char *x) const;
|
||||
int compareTo(const char *x) const; // strcmp clone
|
||||
int compareToIgnoreCase(const char *x) const; // stricmp clone
|
||||
int compareTo(const char *x) const; // strcmp clone
|
||||
int compareToIgnoreCase(const char *x) const; // stricmp clone
|
||||
|
||||
bool hasSuffix(const String &x) const;
|
||||
bool hasSuffix(const char *x) const;
|
||||
@ -152,15 +152,15 @@ public:
|
||||
* Taken from exult/files/listfiles.cc
|
||||
*
|
||||
* Token meaning:
|
||||
* "*": any character, any amount of times.
|
||||
* "?": any character, only once.
|
||||
* "*": any character, any amount of times.
|
||||
* "?": any character, only once.
|
||||
*
|
||||
* Example strings/patterns:
|
||||
* String: monkey.s01 Pattern: monkey.s?? => true
|
||||
* String: monkey.s101 Pattern: monkey.s?? => false
|
||||
* String: monkey.s99 Pattern: monkey.s?1 => false
|
||||
* String: monkey.s101 Pattern: monkey.s* => true
|
||||
* String: monkey.s99 Pattern: monkey.s*1 => false
|
||||
* String: monkey.s01 Pattern: monkey.s?? => true
|
||||
* String: monkey.s101 Pattern: monkey.s?? => false
|
||||
* String: monkey.s99 Pattern: monkey.s?1 => false
|
||||
* String: monkey.s101 Pattern: monkey.s* => true
|
||||
* String: monkey.s99 Pattern: monkey.s*1 => false
|
||||
*
|
||||
* @param str Text to be matched against the given pattern.
|
||||
* @param pat Glob pattern.
|
||||
@ -173,11 +173,11 @@ public:
|
||||
bool matchString(const String &pat, bool ignoreCase = false, bool pathMode = false) const;
|
||||
|
||||
|
||||
inline const char *c_str() const { return _str; }
|
||||
inline uint size() const { return _size; }
|
||||
inline const char *c_str() const { return _str; }
|
||||
inline uint size() const { return _size; }
|
||||
|
||||
inline bool empty() const { return (_size == 0); }
|
||||
char lastChar() const { return (_size > 0) ? _str[_size-1] : 0; }
|
||||
inline bool empty() const { return (_size == 0); }
|
||||
char lastChar() const { return (_size > 0) ? _str[_size - 1] : 0; }
|
||||
|
||||
char operator[](int idx) const {
|
||||
assert(_str && idx >= 0 && idx < (int)_size);
|
||||
@ -222,19 +222,19 @@ public:
|
||||
typedef char * iterator;
|
||||
typedef const char * const_iterator;
|
||||
|
||||
iterator begin() {
|
||||
iterator begin() {
|
||||
return _str;
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
iterator end() {
|
||||
return begin() + size();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
const_iterator begin() const {
|
||||
return _str;
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
const_iterator end() const {
|
||||
return begin() + size();
|
||||
}
|
||||
|
||||
@ -247,17 +247,17 @@ protected:
|
||||
};
|
||||
|
||||
// Append two strings to form a new (temp) string
|
||||
String operator +(const String &x, const String &y);
|
||||
String operator+(const String &x, const String &y);
|
||||
|
||||
String operator +(const char *x, const String &y);
|
||||
String operator +(const String &x, const char *y);
|
||||
String operator+(const char *x, const String &y);
|
||||
String operator+(const String &x, const char *y);
|
||||
|
||||
String operator +(const String &x, char y);
|
||||
String operator +(char x, const String &y);
|
||||
String operator+(const String &x, char y);
|
||||
String operator+(char x, const String &y);
|
||||
|
||||
// Some useful additional comparison operators for Strings
|
||||
bool operator == (const char *x, const String &y);
|
||||
bool operator != (const char *x, const String &y);
|
||||
bool operator==(const char *x, const String &y);
|
||||
bool operator!=(const char *x, const String &y);
|
||||
|
||||
// Utility functions to remove leading and trailing whitespaces
|
||||
extern char *ltrim(char *t);
|
||||
@ -269,9 +269,9 @@ extern char *trim(char *t);
|
||||
* Returns the last component of a given path.
|
||||
*
|
||||
* Examples:
|
||||
* /foo/bar.txt would return 'bar.txt'
|
||||
* /foo/bar/ would return 'bar'
|
||||
* /foo/./bar// would return 'bar'
|
||||
* /foo/bar.txt would return 'bar.txt'
|
||||
* /foo/bar/ would return 'bar'
|
||||
* /foo/./bar// would return 'bar'
|
||||
*
|
||||
* @param path the path of which we want to know the last component
|
||||
* @param sep character used to separate path components
|
||||
@ -287,9 +287,9 @@ Common::String lastPathComponent(const Common::String &path, const char sep);
|
||||
*
|
||||
* @todo remove double dot components: /foo/baz/../bar -> /foo/bar
|
||||
*
|
||||
* @param path the path to normalize
|
||||
* @param sep the separator token (usually '/' on Unix-style systems, or '\\' on Windows based stuff)
|
||||
* @return the normalized path
|
||||
* @param path the path to normalize
|
||||
* @param sep the separator token (usually '/' on Unix-style systems, or '\\' on Windows based stuff)
|
||||
* @return the normalized path
|
||||
*/
|
||||
Common::String normalizePath(const Common::String &path, const char sep);
|
||||
|
||||
@ -299,15 +299,15 @@ Common::String normalizePath(const Common::String &path, const char sep);
|
||||
* Taken from exult/files/listfiles.cc
|
||||
*
|
||||
* Token meaning:
|
||||
* "*": any character, any amount of times.
|
||||
* "?": any character, only once.
|
||||
* "*": any character, any amount of times.
|
||||
* "?": any character, only once.
|
||||
*
|
||||
* Example strings/patterns:
|
||||
* String: monkey.s01 Pattern: monkey.s?? => true
|
||||
* String: monkey.s101 Pattern: monkey.s?? => false
|
||||
* String: monkey.s99 Pattern: monkey.s?1 => false
|
||||
* String: monkey.s101 Pattern: monkey.s* => true
|
||||
* String: monkey.s99 Pattern: monkey.s*1 => false
|
||||
* String: monkey.s01 Pattern: monkey.s?? => true
|
||||
* String: monkey.s101 Pattern: monkey.s?? => false
|
||||
* String: monkey.s99 Pattern: monkey.s?1 => false
|
||||
* String: monkey.s101 Pattern: monkey.s* => true
|
||||
* String: monkey.s99 Pattern: monkey.s*1 => false
|
||||
*
|
||||
* @param str Text to be matched against the given pattern.
|
||||
* @param pat Glob pattern.
|
||||
@ -321,6 +321,6 @@ bool matchString(const char *str, const char *pat, bool ignoreCase = false, bool
|
||||
|
||||
typedef Array<String> StringList;
|
||||
|
||||
} // End of namespace Common
|
||||
} // End of namespace Common
|
||||
|
||||
#endif
|
||||
|
@ -26,9 +26,8 @@
|
||||
#ifndef COMMON_STREAM_H
|
||||
#define COMMON_STREAM_H
|
||||
|
||||
#include "common/sys.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/types.h"
|
||||
#include "common/endian.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
|
140
common/sys.h
140
common/sys.h
@ -26,49 +26,91 @@
|
||||
#ifndef COMMON_SYS_H
|
||||
#define COMMON_SYS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#if defined(_WIN32_WCE) && _WIN32_WCE < 300
|
||||
#define NONSTANDARD_PORT
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma once
|
||||
#pragma warning( disable : 4068 ) // turn off "unknown pragma" warning
|
||||
#pragma warning( disable : 4100 ) // turn off "unreferenced formal parameter" warning
|
||||
#pragma warning( disable : 4127 ) // turn off "conditional expression is constant" warning
|
||||
#pragma warning( disable : 4189 ) // turn off "local variable is initialized but not referenced" warning
|
||||
#pragma warning( disable : 4244 ) // turn off "conversion type" warning
|
||||
#pragma warning( disable : 4250 ) // turn off "inherits via dominance" warning
|
||||
#pragma warning( disable : 4505 ) // turn off "unreferenced local function has been removed" warning
|
||||
#pragma warning( disable : 4512 ) // turn off "assignment operator could not be generated" warning
|
||||
#pragma warning( disable : 4611 ) // turn off "interaction between '_setjmp' and C++ object destruction is non-portable" warning
|
||||
#pragma warning( disable : 4800 ) // turn off "forcing value to bool 'true' or 'false' (performance warning)"
|
||||
#pragma warning( disable : 4996 ) // turn off "This function or variable may be unsafe" warning
|
||||
#if defined(NONSTANDARD_PORT)
|
||||
|
||||
// Ports which need to perform #includes and #defines visible in
|
||||
// virtually all the source of ScummVM should do so by providing a
|
||||
// "portdefs.h" header file (and not by directly modifying this
|
||||
// header file).
|
||||
#include <portdefs.h>
|
||||
#else // defined(NONSTANDARD_PORT)
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// vsnprintf is already defined in Visual Studio 2008
|
||||
#if (_MSC_VER < 1500)
|
||||
#define vsnprintf _vsnprintf
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32_WCE)
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#define NOGDICAPMASKS
|
||||
#define OEMRESOURCE
|
||||
#define NONLS
|
||||
#define NOICONS
|
||||
#define NOMCX
|
||||
#define NOPROFILER
|
||||
#define NOKANJI
|
||||
#define NOSERVICE
|
||||
#define NOMETAFILE
|
||||
#define NOCOMM
|
||||
#define NOCRYPT
|
||||
#define NOIME
|
||||
#define NOATOM
|
||||
#define NOCTLMGR
|
||||
#define NOCLIPBOARD
|
||||
#define NOMEMMGR
|
||||
#define NOSYSMETRICS
|
||||
#define NOMENUS
|
||||
#define NOOPENFILE
|
||||
#define NOWH
|
||||
#define NOSOUND
|
||||
#define NODRAWTEXT
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(ARRAYSIZE)
|
||||
// VS2005beta2 introduces new stuff in winnt.h
|
||||
#undef ARRAYSIZE
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__QNXNTO__)
|
||||
#include <strings.h> /* For strcasecmp */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
|
||||
// vsnprintf is already defined in Visual Studio 2008
|
||||
#if (_MSC_VER < 1500)
|
||||
#define vsnprintf _vsnprintf
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef LOCAL_PI
|
||||
#define LOCAL_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
|
||||
// Use config.h, generated by configure
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
// make sure we really are compiling for WIN32
|
||||
#ifndef WIN32
|
||||
#undef _MSC_VER
|
||||
#endif
|
||||
|
||||
|
||||
// In the following we configure various targets, in particular those
|
||||
// which can't use our "configure" tool and hence don't use config.h.
|
||||
//
|
||||
@ -270,6 +312,8 @@
|
||||
|
||||
#elif defined(__PSP__)
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
#define SYSTEM_LITTLE_ENDIAN
|
||||
#define SYSTEM_NEED_ALIGNMENT
|
||||
|
||||
@ -286,12 +330,10 @@
|
||||
#define SYSTEM_NEED_ALIGNMENT
|
||||
#define SYSTEM_LITTLE_ENDIAN
|
||||
|
||||
#include "nds/jtypes.h"
|
||||
|
||||
#define SYSTEM_DONT_DEFINE_TYPES
|
||||
|
||||
#define STRINGBUFLEN 256
|
||||
#define printf(fmt, ...) consolePrintf(fmt, ##__VA_ARGS__)
|
||||
// #define printf(fmt, ...) consolePrintf(fmt, ##__VA_ARGS__)
|
||||
|
||||
#elif defined(__WII__)
|
||||
|
||||
@ -308,14 +350,19 @@
|
||||
// GCC specific stuff
|
||||
//
|
||||
#if defined(__GNUC__)
|
||||
#define NORETURN __attribute__((__noreturn__))
|
||||
#define PACKED_STRUCT __attribute__((packed))
|
||||
#define GCC_PRINTF(x,y) __attribute__((format(printf, x, y)))
|
||||
#define NORETURN_POST __attribute__((__noreturn__))
|
||||
#define PACKED_STRUCT __attribute__((__packed__))
|
||||
#define GCC_PRINTF(x,y) __attribute__((__format__(printf, x, y)))
|
||||
|
||||
#if !defined(FORCEINLINE) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
|
||||
#define FORCEINLINE inline __attribute__((__always_inline__))
|
||||
#endif
|
||||
#else
|
||||
#define PACKED_STRUCT
|
||||
#define GCC_PRINTF(x,y)
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
// Fallbacks / default values for various special macros
|
||||
//
|
||||
@ -339,6 +386,10 @@
|
||||
#define STRINGBUFLEN 1024
|
||||
#endif
|
||||
|
||||
#ifndef LOCAL_PI
|
||||
#define LOCAL_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
#define MAXPATHLEN 256
|
||||
#endif
|
||||
@ -359,4 +410,17 @@
|
||||
#endif
|
||||
|
||||
|
||||
#endif // COMMON_SYS_H
|
||||
|
||||
//
|
||||
// Overlay color type (FIXME: shouldn't be declared here)
|
||||
//
|
||||
#if defined(NEWGUI_256)
|
||||
// 256 color only on PalmOS
|
||||
typedef byte OverlayColor;
|
||||
#else
|
||||
// 15/16 bit color mode everywhere else...
|
||||
typedef uint16 OverlayColor;
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -32,8 +32,6 @@
|
||||
|
||||
#include "graphics/pixelformat.h"
|
||||
|
||||
typedef uint16 OverlayColor;
|
||||
|
||||
namespace Audio {
|
||||
class Mixer;
|
||||
}
|
||||
|
@ -35,8 +35,6 @@ extern bool isSmartphone();
|
||||
|
||||
#ifdef __PLAYSTATION2__
|
||||
// for those replaced fopen/fread/etc functions
|
||||
typedef unsigned long uint64;
|
||||
typedef signed long int64;
|
||||
#include "backends/platform/ps2/fileio.h"
|
||||
|
||||
#define fputs(str, file) ps2_fputs(str, file)
|
||||
@ -266,7 +264,6 @@ const PlatformDescription g_platforms[] = {
|
||||
{"segacd", "segacd", "sega", "SegaCD", kPlatformSegaCD},
|
||||
{"windows", "win", "win", "Windows", kPlatformWindows},
|
||||
{"playstation", "psx", "psx", "Sony PlayStation", kPlatformPSX},
|
||||
{"playstation2", "ps2", "ps2", "Sony PlayStation 2", kPlatformPS2},
|
||||
{"cdi", "cdi", "cdi", "Phillips CD-i", kPlatformCDi},
|
||||
|
||||
{0, 0, 0, "Default", kPlatformUnknown}
|
||||
|
@ -57,10 +57,6 @@ template<typename T> inline T CLIP (T v, T amin, T amax)
|
||||
*/
|
||||
template<typename T> inline void SWAP(T &a, T &b) { T tmp = a; a = b; b = tmp; }
|
||||
|
||||
#if defined(ARRAYSIZE)
|
||||
// VS2005beta2 introduces new stuff in winnt.h
|
||||
#undef ARRAYSIZE
|
||||
#endif
|
||||
/**
|
||||
* Macro which determines the number of entries in a fixed size array.
|
||||
*/
|
||||
@ -226,7 +222,6 @@ enum Platform {
|
||||
kPlatformWii,
|
||||
kPlatformPSX,
|
||||
kPlatformCDi,
|
||||
kPlatformPS2,
|
||||
|
||||
kPlatformUnknown = -1
|
||||
};
|
||||
|
@ -247,10 +247,12 @@ bool XMLParser::closeKey() {
|
||||
}
|
||||
|
||||
bool XMLParser::parse() {
|
||||
|
||||
if (_stream == 0)
|
||||
return parserError("XML stream not ready for reading.");
|
||||
|
||||
// Make sure we are at the start of the stream.
|
||||
_stream->seek(0, SEEK_SET);
|
||||
|
||||
if (_XMLkeys == 0)
|
||||
buildLayout();
|
||||
|
||||
|
@ -1,30 +0,0 @@
|
||||
@echo off
|
||||
rem This batch file is used to convert MSVC8 (Visual Studio 2005) project files to MSVC9 (Visual Studio 2008) ones
|
||||
rem You need the Windows version of GNU rpl
|
||||
rem Get it here:
|
||||
rem http://gnuwin32.sourceforge.net/packages/rpl.htm
|
||||
rem Place rpl.exe from the bin folder inside the archive in the folder where
|
||||
rem this batch file resides
|
||||
|
||||
if not exist rpl.exe goto no_rpl
|
||||
|
||||
echo Creating MSVC9 project files from the MSVC8 ones
|
||||
copy /y msvc8\*.vcproj msvc9\
|
||||
copy /y msvc8\*.sln msvc9\
|
||||
copy /y msvc8\*.vsprops msvc9\
|
||||
rpl -e -q "Version=\"8.00\"" "Version=\"9.00\"" msvc9\*.vcproj
|
||||
rpl -e -q "Version=\"8,00\"" "Version=\"9,00\"" msvc9\*.vcproj
|
||||
rpl -e -q "Keyword=\"Win32Proj\"" "Keyword=\"Win32Proj\"\n\tTargetFrameworkVersion=\"131072\"" msvc9\*.vcproj
|
||||
rpl -e -q "Format Version 9.00" "Format Version 10.00" msvc9\residual.sln
|
||||
rpl -e -q "Format Version 9,00" "Format Version 10,00" msvc9\residual.sln
|
||||
goto the_end
|
||||
|
||||
:no_rpl
|
||||
echo You need the Windows version of GNU rpl
|
||||
echo Get it here:
|
||||
echo http://gnuwin32.sourceforge.net/packages/rpl.htm
|
||||
echo Place rpl.exe from the bin folder inside the archive in the folder where
|
||||
echo this batch file resides
|
||||
|
||||
:the_end
|
||||
pause
|
@ -1,30 +0,0 @@
|
||||
@echo off
|
||||
rem This batch file is used to convert MSVC9 (Visual Studio 2008) project files to MSVC8 (Visual Studio 2005) ones
|
||||
rem You need the Windows version of GNU rpl
|
||||
rem Get it here:
|
||||
rem http://gnuwin32.sourceforge.net/packages/rpl.htm
|
||||
rem Place rpl.exe from the bin folder inside the archive in the folder where
|
||||
rem this batch file resides
|
||||
|
||||
if not exist rpl.exe goto no_rpl
|
||||
|
||||
echo Creating MSVC8 project files from the MSVC9 ones
|
||||
copy /y msvc9\*.vcproj msvc8\
|
||||
copy /y msvc9\*.sln msvc8\
|
||||
copy /y msvc9\*.vsprops msvc8\
|
||||
rpl -e -q "Version=\"9.00\"" "Version=\"8.00\"" msvc8\*.vcproj
|
||||
rpl -e -q "Version=\"9,00\"" "Version=\"8,00\"" msvc8\*.vcproj
|
||||
rpl -e -q "\tTargetFrameworkVersion=\"131072\"\n" "" msvc8\*.vcproj
|
||||
rpl -e -q "Format Version 10.00" "Format Version 9.00" msvc8\residual.sln
|
||||
rpl -e -q "Format Version 10,00" "Format Version 9,00" msvc8\residual.sln
|
||||
goto the_end
|
||||
|
||||
:no_rpl
|
||||
echo You need the Windows version of GNU rpl
|
||||
echo Get it here:
|
||||
echo http://gnuwin32.sourceforge.net/packages/rpl.htm
|
||||
echo Place rpl.exe from the bin folder inside the archive in the folder where
|
||||
echo this batch file resides
|
||||
|
||||
:the_end
|
||||
pause
|
@ -1,7 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Name=Residual
|
||||
Exec=residual
|
||||
Icon=residual.xpm
|
||||
Icon=residual
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Game;AdventureGame;
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "winresrc.h"
|
||||
|
||||
#if defined (__MINGW32__) || defined(__CYGWIN32__)
|
||||
#if defined (__MINGW32__) || defined(__CYGWIN32__) || defined(HAS_INCLUDE_SET)
|
||||
IDI_ICON ICON DISCARDABLE "icons/residual.ico"
|
||||
#else
|
||||
IDI_ICON ICON DISCARDABLE "../../icons/residual.ico"
|
||||
@ -24,17 +24,13 @@ BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "Comments", "\0"
|
||||
VALUE "CompanyName", "Residual\0"
|
||||
VALUE "FileDescription", "http://residual.sourceforge.net/\0"
|
||||
VALUE "FileVersion", "0.0.6svn\0"
|
||||
VALUE "InternalName", "residual\0"
|
||||
VALUE "LegalCopyright", "Copyrights information are in AUTHORS file\0"
|
||||
VALUE "LegalTrademarks", "\0"
|
||||
VALUE "OriginalFilename", "residual.exe\0"
|
||||
VALUE "PrivateBuild", "\0"
|
||||
VALUE "ProductName", "Residual\0"
|
||||
VALUE "ProductVersion", "0.0.6svn\0"
|
||||
VALUE "SpecialBuild", "\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "winresrc.h"
|
||||
|
||||
#if defined (__MINGW32__) || defined(__CYGWIN32__)
|
||||
#if defined (__MINGW32__) || defined(__CYGWIN32__) || defined(HAS_INCLUDE_SET)
|
||||
IDI_ICON ICON DISCARDABLE "icons/residual.ico"
|
||||
#else
|
||||
IDI_ICON ICON DISCARDABLE "../../icons/residual.ico"
|
||||
@ -24,17 +24,13 @@ BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "Comments", "\0"
|
||||
VALUE "CompanyName", "Residual\0"
|
||||
VALUE "FileDescription", "http://residual.sourceforge.net/\0"
|
||||
VALUE "FileVersion", "@VERSION@\0"
|
||||
VALUE "InternalName", "residual\0"
|
||||
VALUE "LegalCopyright", "Copyrights information are in AUTHORS file\0"
|
||||
VALUE "LegalTrademarks", "\0"
|
||||
VALUE "OriginalFilename", "residual.exe\0"
|
||||
VALUE "PrivateBuild", "\0"
|
||||
VALUE "ProductName", "Residual\0"
|
||||
VALUE "ProductVersion", "@VERSION@\0"
|
||||
VALUE "SpecialBuild", "\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
@ -48,7 +48,7 @@
|
||||
extern bool isSmartphone();
|
||||
#endif
|
||||
|
||||
// FIXME: HACK for MidiEmu & error()
|
||||
// FIXME: HACK for error()
|
||||
Engine *g_engine = 0;
|
||||
|
||||
// Output formatter for debug() and error() which invokes
|
||||
@ -260,7 +260,7 @@ void Engine::flipMute() {
|
||||
if (ConfMan.hasKey("mute")) {
|
||||
mute = !ConfMan.getBool("mute");
|
||||
}
|
||||
|
||||
|
||||
ConfMan.setBool("mute", mute);
|
||||
|
||||
syncSoundSettings();
|
||||
|
@ -122,7 +122,7 @@ static const GrimGameDescription gameDescriptions[] = {
|
||||
"",
|
||||
AD_ENTRY1s("local.m4b", "00c4eb73f6b6607ba3d4e8d3f956b37b", 3804862),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformPS2,
|
||||
Common::kPlatformWindows,//Common::kPlatformPS2,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO_NONE
|
||||
},
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
#include "sound/audiostream.h"
|
||||
#include "sound/mixer.h"
|
||||
#include "sound/raw.h"
|
||||
#include "sound/decoders/raw.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
#include "sound/audiostream.h"
|
||||
#include "sound/mixer.h"
|
||||
#include "sound/raw.h"
|
||||
#include "sound/decoders/raw.h"
|
||||
|
||||
#include "engines/grim/smush/smush.h"
|
||||
|
||||
|
@ -160,7 +160,7 @@ CursorManager::Cursor::Cursor(const byte *data, uint w, uint h, int hotspotX, in
|
||||
#ifdef USE_RGB_COLOR
|
||||
if (!format)
|
||||
_format = Graphics::PixelFormat::createFormatCLUT8();
|
||||
else
|
||||
else
|
||||
_format = *format;
|
||||
_size = w * h * _format.bytesPerPixel;
|
||||
_keycolor = keycolor & ((1 << (_format.bytesPerPixel << 3)) - 1);
|
||||
|
@ -29,9 +29,9 @@
|
||||
#include "common/stack.h"
|
||||
#include "common/singleton.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#ifdef USE_RGB_COLOR
|
||||
#include "common/system.h"
|
||||
#endif
|
||||
//#ifdef USE_RGB_COLOR
|
||||
//#include "common/system.h"
|
||||
//#endif
|
||||
|
||||
namespace Graphics {
|
||||
|
||||
@ -70,7 +70,7 @@ public:
|
||||
* @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,
|
||||
* @param format a pointer to the pixel format which the cursor graphic uses,
|
||||
* CLUT8 will be used if this is NULL or not specified.
|
||||
* @note It is ok for the buffer to be a NULL pointer. It is sometimes
|
||||
* useful to push a "dummy" cursor and modify it later. The
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include "graphics/imagedec.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/system.h"
|
||||
//#include "common/system.h"
|
||||
|
||||
namespace Graphics {
|
||||
//
|
||||
|
@ -51,13 +51,13 @@ struct PixelFormat {
|
||||
byte rShift, gShift, bShift, aShift; /**< Binary left shift of each color component in the pixel value. */
|
||||
|
||||
inline PixelFormat() {
|
||||
bytesPerPixel =
|
||||
rLoss = gLoss = bLoss = aLoss =
|
||||
bytesPerPixel =
|
||||
rLoss = gLoss = bLoss = aLoss =
|
||||
rShift = gShift = bShift = aShift = 0;
|
||||
}
|
||||
|
||||
inline PixelFormat(byte BytesPerPixel,
|
||||
byte RBits, byte GBits, byte BBits, byte ABits,
|
||||
inline PixelFormat(byte BytesPerPixel,
|
||||
byte RBits, byte GBits, byte BBits, byte ABits,
|
||||
byte RShift, byte GShift, byte BShift, byte AShift) {
|
||||
bytesPerPixel = BytesPerPixel;
|
||||
rLoss = 8 - RBits, gLoss = 8 - GBits, bLoss = 8 - BBits, aLoss = 8 - ABits;
|
||||
|
@ -286,7 +286,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
|
||||
bool dirty = false;
|
||||
int oldSelectedItem = _selectedItem;
|
||||
|
||||
if (!_editMode && state.keycode <= Common::KEYCODE_z && isprint((unsigned char)state.ascii)) {
|
||||
if (!_editMode && 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,33 +351,27 @@ 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:
|
||||
@ -642,7 +636,7 @@ void ListWidget::setFilter(const String &filter, bool redraw) {
|
||||
} else {
|
||||
// Restrict the list to everything which contains all words in _filter
|
||||
// as substrings, ignoring case.
|
||||
|
||||
|
||||
Common::StringTokenizer tok(_filter);
|
||||
String tmp;
|
||||
int n = 0;
|
||||
|
@ -228,19 +228,15 @@ 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:
|
||||
|
@ -58,7 +58,7 @@ enum {
|
||||
|
||||
static const char *copyright_text[] = {
|
||||
"",
|
||||
"C0""Copyright (C) 2003-2010 The Residual project",
|
||||
"C0""Copyright (C) 2003-2011 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.",
|
||||
|
@ -341,8 +341,10 @@ void ConsoleDialog::handleKeyDown(Common::KeyState state) {
|
||||
break;
|
||||
}
|
||||
case Common::KEYCODE_DELETE:
|
||||
killChar();
|
||||
drawLine(pos2line(_currentPos));
|
||||
if (_currentPos < _promptEndPos) {
|
||||
killChar();
|
||||
drawLine(pos2line(_currentPos));
|
||||
}
|
||||
break;
|
||||
case Common::KEYCODE_PAGEUP:
|
||||
if (state.flags == Common::KBD_SHIFT) {
|
||||
@ -386,21 +388,17 @@ 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));
|
||||
@ -472,8 +470,10 @@ void ConsoleDialog::specialKeys(int keycode) {
|
||||
void ConsoleDialog::killChar() {
|
||||
for (int i = _currentPos; i < _promptEndPos; i++)
|
||||
buffer(i) = buffer(i + 1);
|
||||
buffer(_promptEndPos) = ' ';
|
||||
_promptEndPos--;
|
||||
if (_promptEndPos > _promptStartPos) {
|
||||
buffer(_promptEndPos) = ' ';
|
||||
_promptEndPos--;
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleDialog::killLine() {
|
||||
@ -497,8 +497,10 @@ void ConsoleDialog::killLastWord() {
|
||||
|
||||
for (int i = _currentPos; i < _promptEndPos; i++)
|
||||
buffer(i) = buffer(i + cnt);
|
||||
buffer(_promptEndPos) = ' ';
|
||||
_promptEndPos -= cnt;
|
||||
if (_promptEndPos > _promptStartPos) {
|
||||
buffer(_promptEndPos) = ' ';
|
||||
_promptEndPos -= cnt;
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleDialog::addToHistory(const char *str) {
|
||||
|
@ -157,7 +157,7 @@ void Debugger::enter() {
|
||||
|
||||
g_readline_debugger = this;
|
||||
rl_completion_entry_function = &readline_completionFunction;
|
||||
|
||||
|
||||
char *line_read = 0;
|
||||
do {
|
||||
free(line_read);
|
||||
@ -375,7 +375,7 @@ char *Debugger::readlineComplete(const char *input, int state) {
|
||||
char *ret = (char *)malloc(iter->_key.size() + 1);
|
||||
strcpy(ret, iter->_key.c_str());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -124,7 +124,6 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
|
||||
forcecaret = true;
|
||||
break;
|
||||
case Common::KEYCODE_LEFT:
|
||||
case Common::KEYCODE_KP4:
|
||||
if (_caretPos > 0) {
|
||||
dirty = setCaretPos(_caretPos - 1);
|
||||
}
|
||||
@ -132,7 +131,6 @@ 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);
|
||||
}
|
||||
@ -140,12 +138,10 @@ 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;
|
||||
|
@ -821,7 +821,7 @@ void LauncherDialog::editGame(int item) {
|
||||
// default set nothing and use the global ScummVM settings. E.g. the user
|
||||
// can set here an optional alternate music volume, or for specific games
|
||||
// a different music driver etc.
|
||||
// This is useful because e.g. MonkeyVGA needs Adlib music to have decent
|
||||
// This is useful because e.g. MonkeyVGA needs AdLib music to have decent
|
||||
// music support etc.
|
||||
assert(item >= 0);
|
||||
String gameId(ConfMan.get("gameid", _domains[item]));
|
||||
|
@ -732,6 +732,11 @@ void GlobalOptionsDialog::open() {
|
||||
if (value == savePeriodValues[i])
|
||||
_autosavePeriodPopUp->setSelected(i);
|
||||
}
|
||||
|
||||
ThemeEngine::GraphicsMode mode = ThemeEngine::findMode(ConfMan.get("gui_renderer"));
|
||||
if (mode == ThemeEngine::kGfxDisabled)
|
||||
mode = ThemeEngine::_defaultRendererMode;
|
||||
_rendererPopUp->setSelectedTag(mode);
|
||||
}
|
||||
|
||||
void GlobalOptionsDialog::close() {
|
||||
@ -761,6 +766,15 @@ void GlobalOptionsDialog::close() {
|
||||
#endif
|
||||
|
||||
ConfMan.setInt("autosave_period", _autosavePeriodPopUp->getSelectedTag(), _domain);
|
||||
|
||||
GUI::ThemeEngine::GraphicsMode selected = (GUI::ThemeEngine::GraphicsMode)_rendererPopUp->getSelectedTag();
|
||||
const char *cfg = GUI::ThemeEngine::findModeConfigName(selected);
|
||||
if (!ConfMan.get("gui_renderer").equalsIgnoreCase(cfg)) {
|
||||
// FIXME: Actually, any changes (including the theme change) should
|
||||
// only become active *after* the options dialog has closed.
|
||||
g_gui.loadNewTheme(g_gui.theme()->getThemeId(), selected);
|
||||
ConfMan.set("gui_renderer", cfg, _domain);
|
||||
}
|
||||
}
|
||||
OptionsDialog::close();
|
||||
}
|
||||
|
1
ports.mk
1
ports.mk
@ -61,6 +61,7 @@ bundle: residual-static
|
||||
iphonebundle: iphone
|
||||
mkdir -p $(bundle_name)
|
||||
cp $(srcdir)/dists/iphone/Info.plist $(bundle_name)/
|
||||
cp $(DIST_FILES_DOCS) $(bundle_name)/
|
||||
cp $(DIST_FILES_THEMES) $(bundle_name)/
|
||||
#cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/
|
||||
ldid -S residual
|
||||
|
@ -25,9 +25,9 @@
|
||||
|
||||
#include "sound/audiocd.h"
|
||||
#include "sound/audiostream.h"
|
||||
#include "sound/mp3.h"
|
||||
#include "sound/vorbis.h"
|
||||
#include "sound/flac.h"
|
||||
#include "sound/decoders/mp3.h"
|
||||
#include "sound/decoders/vorbis.h"
|
||||
#include "sound/decoders/flac.h"
|
||||
#include "engines/engine.h"
|
||||
#include "common/util.h"
|
||||
#include "common/system.h"
|
||||
|
@ -30,19 +30,19 @@
|
||||
#include "common/util.h"
|
||||
|
||||
#include "sound/audiostream.h"
|
||||
#include "sound/flac.h"
|
||||
#include "sound/decoders/flac.h"
|
||||
#include "sound/mixer.h"
|
||||
#include "sound/mp3.h"
|
||||
#include "sound/raw.h"
|
||||
#include "sound/vorbis.h"
|
||||
#include "sound/decoders/mp3.h"
|
||||
#include "sound/decoders/raw.h"
|
||||
#include "sound/decoders/vorbis.h"
|
||||
|
||||
|
||||
namespace Audio {
|
||||
|
||||
struct StreamFileFormat {
|
||||
/** Decodername */
|
||||
const char* decoderName;
|
||||
const char* fileExtension;
|
||||
const char *decoderName;
|
||||
const char *fileExtension;
|
||||
/**
|
||||
* Pointer to a function which tries to open a file of type StreamFormat.
|
||||
* Return NULL in case of an error (invalid/nonexisting file).
|
||||
@ -51,16 +51,16 @@ struct StreamFileFormat {
|
||||
};
|
||||
|
||||
static const StreamFileFormat STREAM_FILEFORMATS[] = {
|
||||
/* decoderName, fileExt, openStreamFuntion */
|
||||
/* decoderName, fileExt, openStreamFuntion */
|
||||
#ifdef USE_FLAC
|
||||
{ "Flac", ".flac", makeFlacStream },
|
||||
{ "Flac", ".fla", makeFlacStream },
|
||||
{ "FLAC", ".flac", makeFLACStream },
|
||||
{ "FLAC", ".fla", makeFLACStream },
|
||||
#endif
|
||||
#ifdef USE_VORBIS
|
||||
{ "Ogg Vorbis", ".ogg", makeVorbisStream },
|
||||
{ "Ogg Vorbis", ".ogg", makeVorbisStream },
|
||||
#endif
|
||||
#ifdef USE_MAD
|
||||
{ "MPEG Layer 3", ".mp3", makeMP3Stream },
|
||||
{ "MPEG Layer 3", ".mp3", makeMP3Stream },
|
||||
#endif
|
||||
|
||||
{ NULL, NULL, NULL } // Terminator
|
||||
@ -104,6 +104,9 @@ LoopingAudioStream::~LoopingAudioStream() {
|
||||
}
|
||||
|
||||
int LoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
if ((_loops && _completeIterations == _loops) || !numSamples)
|
||||
return 0;
|
||||
|
||||
int samplesRead = _parent->readBuffer(buffer, numSamples);
|
||||
|
||||
if (_parent->endOfStream()) {
|
||||
@ -111,7 +114,7 @@ int LoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
if (_completeIterations == _loops)
|
||||
return samplesRead;
|
||||
|
||||
int remainingSamples = numSamples - samplesRead;
|
||||
const int remainingSamples = numSamples - samplesRead;
|
||||
|
||||
if (!_parent->rewind()) {
|
||||
// TODO: Properly indicate error
|
||||
@ -119,7 +122,7 @@ int LoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
return samplesRead;
|
||||
}
|
||||
|
||||
samplesRead += _parent->readBuffer(buffer + samplesRead, remainingSamples);
|
||||
return samplesRead + readBuffer(buffer + samplesRead, remainingSamples);
|
||||
}
|
||||
|
||||
return samplesRead;
|
||||
@ -164,8 +167,8 @@ SubLoopingAudioStream::SubLoopingAudioStream(SeekableAudioStream *stream,
|
||||
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))),
|
||||
_loopStart(convertTimeToStreamPos(loopStart, getRate(), isStereo())),
|
||||
_loopEnd(convertTimeToStreamPos(loopEnd, getRate(), isStereo())),
|
||||
_done(false) {
|
||||
if (!_parent->rewind())
|
||||
_done = true;
|
||||
@ -212,9 +215,9 @@ int SubLoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
|
||||
SubSeekableAudioStream::SubSeekableAudioStream(SeekableAudioStream *parent, const Timestamp start, const Timestamp end, DisposeAfterUse::Flag disposeAfterUse)
|
||||
: _parent(parent), _disposeAfterUse(disposeAfterUse),
|
||||
_start(start.convertToFramerate(getRate())),
|
||||
_start(convertTimeToStreamPos(start, getRate(), isStereo())),
|
||||
_pos(0, getRate() * (isStereo() ? 2 : 1)),
|
||||
_length((end - start).convertToFramerate(getRate() * (isStereo() ? 2 : 1))) {
|
||||
_length(convertTimeToStreamPos(end - start, getRate(), isStereo())) {
|
||||
|
||||
assert(_length.totalNumberOfFrames() % (isStereo() ? 2 : 1) == 0);
|
||||
_parent->seek(_start);
|
||||
@ -233,7 +236,7 @@ int SubSeekableAudioStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
}
|
||||
|
||||
bool SubSeekableAudioStream::seek(const Timestamp &where) {
|
||||
_pos = where.convertToFramerate(getRate());
|
||||
_pos = convertTimeToStreamPos(where, getRate(), isStereo());
|
||||
if (_pos > _length) {
|
||||
_pos = _length;
|
||||
return false;
|
||||
@ -253,7 +256,7 @@ bool SubSeekableAudioStream::seek(const Timestamp &where) {
|
||||
|
||||
|
||||
void QueuingAudioStream::queueBuffer(byte *data, uint32 size, DisposeAfterUse::Flag disposeAfterUse, byte flags) {
|
||||
AudioStream *stream = makeRawMemoryStream(data, size, disposeAfterUse, getRate(), flags, 0, 0);
|
||||
AudioStream *stream = makeRawStream(data, size, getRate(), flags, disposeAfterUse);
|
||||
queueAudioStream(stream, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
@ -271,8 +274,8 @@ private:
|
||||
AudioStream *_stream;
|
||||
DisposeAfterUse::Flag _disposeAfterUse;
|
||||
StreamHolder(AudioStream *stream, DisposeAfterUse::Flag disposeAfterUse)
|
||||
: _stream(stream),
|
||||
_disposeAfterUse(disposeAfterUse) {}
|
||||
: _stream(stream),
|
||||
_disposeAfterUse(disposeAfterUse) {}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -303,7 +306,7 @@ private:
|
||||
|
||||
public:
|
||||
QueuingAudioStreamImpl(int rate, bool stereo)
|
||||
: _rate(rate), _stereo(stereo), _finished(false) {}
|
||||
: _rate(rate), _stereo(stereo), _finished(false) {}
|
||||
~QueuingAudioStreamImpl();
|
||||
|
||||
// Implement the AudioStream API
|
||||
@ -360,12 +363,19 @@ int QueuingAudioStreamImpl::readBuffer(int16 *buffer, const int numSamples) {
|
||||
return samplesDecoded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QueuingAudioStream *makeQueuingAudioStream(int rate, bool stereo) {
|
||||
return new QueuingAudioStreamImpl(rate, stereo);
|
||||
}
|
||||
|
||||
Timestamp convertTimeToStreamPos(const Timestamp &where, int rate, bool isStereo) {
|
||||
Timestamp result(where.convertToFramerate(rate * (isStereo ? 2 : 1)));
|
||||
|
||||
// When the Stream is a stereo stream, we have to assure
|
||||
// that the sample position is an even number.
|
||||
if (isStereo && (result.totalNumberOfFrames() & 1))
|
||||
return result.addFrames(-1); // We cut off one sample here.
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
|
||||
/**
|
||||
* 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
|
||||
* number of samples read, or -1 if a critical error occurred (note: you
|
||||
* *must* check if this value is less than what you requested, this can
|
||||
* happen when the stream is fully used up).
|
||||
*
|
||||
@ -86,7 +86,7 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* A rewindable audio stream. This allows for restting the AudioStream
|
||||
* A rewindable audio stream. This allows for reseting the AudioStream
|
||||
* to its initial state. Note that rewinding itself is not required to
|
||||
* be working when the stream is being played by Mixer!
|
||||
*/
|
||||
@ -101,7 +101,7 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* A looping audio stream. This object does nothing beides using
|
||||
* A looping audio stream. This object does nothing besides using
|
||||
* a RewindableAudioStream to play a stream in a loop.
|
||||
*/
|
||||
class LoopingAudioStream : public AudioStream {
|
||||
@ -113,7 +113,7 @@ public:
|
||||
*
|
||||
* @param stream Stream to loop
|
||||
* @param loops How often to loop (0 = infinite)
|
||||
* @param disposeAfteruse Destroy the stream after the LoopingAudioStream has finished playback.
|
||||
* @param disposeAfterUse Destroy the stream after the LoopingAudioStream has finished playback.
|
||||
*/
|
||||
LoopingAudioStream(RewindableAudioStream *stream, uint loops, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
~LoopingAudioStream();
|
||||
@ -124,7 +124,7 @@ public:
|
||||
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
|
||||
*/
|
||||
@ -138,16 +138,16 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper functionallity to efficiently create a stream, which might be looped.
|
||||
* Wrapper functionality 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
|
||||
* not create one when the loop count is "1". This allows to keep the runtime
|
||||
* overhead down, when the code does not require any functionality 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.
|
||||
* @return A new AudioStream, which offers the desired functionality.
|
||||
*/
|
||||
AudioStream *makeLoopingAudioStream(RewindableAudioStream *stream, uint loops);
|
||||
|
||||
@ -161,10 +161,10 @@ 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
|
||||
* @return an SeekableAudioStream ready to use in case of success;
|
||||
* NULL in case of an error (e.g. invalid/nonexisting file)
|
||||
* it is still the responsibility of the caller.
|
||||
* @param basename a filename without an extension
|
||||
* @return an SeekableAudioStream ready to use in case of success;
|
||||
* NULL in case of an error (e.g. invalid/nonexisting file)
|
||||
*/
|
||||
static SeekableAudioStream *openStreamFile(const Common::String &basename);
|
||||
|
||||
@ -197,21 +197,21 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper functionallity to efficiently create a stream, which might be looped
|
||||
* Wrapper functionality 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
|
||||
* not create one when the loop count is "1". This allows to keep the runtime
|
||||
* overhead down, when the code does not require any functionality 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.
|
||||
* @return A new AudioStream, which offers the desired functionality.
|
||||
*/
|
||||
AudioStream *makeLoopingAudioStream(SeekableAudioStream *stream, Timestamp start, Timestamp end, uint loops);
|
||||
|
||||
@ -251,11 +251,12 @@ private:
|
||||
bool _done;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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:
|
||||
@ -312,8 +313,8 @@ public:
|
||||
* 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).
|
||||
* the buffer will be released using free(). So make sure to
|
||||
* allocate them with malloc(), not with new[]).
|
||||
*/
|
||||
void queueBuffer(byte *data, uint32 size, DisposeAfterUse::Flag disposeAfterUse, byte flags);
|
||||
|
||||
@ -336,16 +337,15 @@ public:
|
||||
*/
|
||||
QueuingAudioStream *makeQueuingAudioStream(int rate, bool stereo);
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the sample, which the timestamp describes in a
|
||||
* AudioStream with the given framerate.
|
||||
* Converts a point in time to a precise sample offset
|
||||
* with the given parameters.
|
||||
*
|
||||
* @param where point in time
|
||||
* @param rate rate of the AudioStream
|
||||
* @return sample index
|
||||
* @param where Point in time.
|
||||
* @param rate Rate of the stream.
|
||||
* @param isStereo Is the stream a stereo stream?
|
||||
*/
|
||||
uint32 calculateSampleOffset(const Timestamp &where, int rate);
|
||||
Timestamp convertTimeToStreamPos(const Timestamp &where, int rate, bool isStereo);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
|
735
sound/flac.cpp
735
sound/flac.cpp
@ -1,735 +0,0 @@
|
||||
/* 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 "sound/flac.h"
|
||||
|
||||
#ifdef USE_FLAC
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "sound/audiostream.h"
|
||||
#include "sound/audiocd.h"
|
||||
|
||||
#define FLAC__NO_DLL // that MS-magic gave me headaches - just link the library you like
|
||||
#include <FLAC/export.h>
|
||||
|
||||
|
||||
// check if we have FLAC >= 1.1.3; LEGACY_FLAC code can be removed once FLAC-1.1.3 propagates everywhere
|
||||
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT < 8
|
||||
#define LEGACY_FLAC
|
||||
#else
|
||||
#undef LEGACY_FLAC
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef LEGACY_FLAC
|
||||
|
||||
// Before FLAC 1.1.3, we needed to use the stream decoder API.
|
||||
#include <FLAC/seekable_stream_decoder.h>
|
||||
typedef uint FLAC_size_t;
|
||||
|
||||
#else
|
||||
|
||||
// With FLAC 1.1.3, the stream decoder API was merged into the regular
|
||||
// stream API. In order to stay compatible with older FLAC versions, we
|
||||
// simply add some typedefs and #ifdefs to map between the old and new API.
|
||||
// We use the typedefs (instead of only #defines) in order to somewhat
|
||||
// improve the readability of the code.
|
||||
|
||||
#include <FLAC/stream_decoder.h>
|
||||
typedef size_t FLAC_size_t;
|
||||
// Add aliases for the old names
|
||||
typedef FLAC__StreamDecoderState FLAC__SeekableStreamDecoderState;
|
||||
typedef FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus;
|
||||
typedef FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus;
|
||||
typedef FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus;
|
||||
typedef FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus;
|
||||
typedef FLAC__StreamDecoder FLAC__SeekableStreamDecoder;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace Audio {
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- Flac stream ---
|
||||
#pragma mark -
|
||||
|
||||
static const uint MAX_OUTPUT_CHANNELS = 2;
|
||||
|
||||
|
||||
class FlacInputStream : public SeekableAudioStream {
|
||||
protected:
|
||||
Common::SeekableReadStream *_inStream;
|
||||
bool _disposeAfterUse;
|
||||
|
||||
::FLAC__SeekableStreamDecoder *_decoder;
|
||||
|
||||
/** Header of the stream */
|
||||
FLAC__StreamMetadata_StreamInfo _streaminfo;
|
||||
|
||||
/** index + 1(!) of the last sample to be played */
|
||||
FLAC__uint64 _lastSample;
|
||||
|
||||
/** total play time */
|
||||
Timestamp _length;
|
||||
|
||||
/** true if the last sample was decoded from the FLAC-API - there might still be data in the buffer */
|
||||
bool _lastSampleWritten;
|
||||
|
||||
typedef int16 SampleType;
|
||||
enum { BUFTYPE_BITS = 16 };
|
||||
|
||||
enum {
|
||||
// Maximal buffer size. According to the FLAC format specification, the block size is
|
||||
// a 16 bit value (in fact it seems the maximal block size is 32768, but we play it safe).
|
||||
BUFFER_SIZE = 65536
|
||||
};
|
||||
|
||||
struct {
|
||||
SampleType bufData[BUFFER_SIZE];
|
||||
SampleType *bufReadPos;
|
||||
uint bufFill;
|
||||
} _sampleCache;
|
||||
|
||||
SampleType *_outBuffer;
|
||||
uint _requestedSamples;
|
||||
|
||||
typedef void (*PFCONVERTBUFFERS)(SampleType*, const FLAC__int32*[], uint, const uint, const uint8);
|
||||
PFCONVERTBUFFERS _methodConvertBuffers;
|
||||
|
||||
|
||||
public:
|
||||
FlacInputStream(Common::SeekableReadStream *inStream, bool dispose);
|
||||
virtual ~FlacInputStream();
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
bool isStereo() const { return _streaminfo.channels >= 2; }
|
||||
int getRate() const { return _streaminfo.sample_rate; }
|
||||
bool endOfData() const {
|
||||
// End of data is reached if there either is no valid stream data available,
|
||||
// or if we reached the last sample and completely emptied the sample cache.
|
||||
return _streaminfo.channels == 0 || (_lastSampleWritten && _sampleCache.bufFill == 0);
|
||||
}
|
||||
|
||||
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<uint>(_streaminfo.channels, MAX_OUTPUT_CHANNELS); }
|
||||
|
||||
bool allocateBuffer(uint minSamples);
|
||||
|
||||
inline FLAC__StreamDecoderState getStreamDecoderState() const;
|
||||
|
||||
inline bool processSingleBlock();
|
||||
inline bool processUntilEndOfMetadata();
|
||||
bool seekAbsolute(FLAC__uint64 sample);
|
||||
|
||||
inline ::FLAC__SeekableStreamDecoderReadStatus callbackRead(FLAC__byte buffer[], FLAC_size_t *bytes);
|
||||
inline ::FLAC__SeekableStreamDecoderSeekStatus callbackSeek(FLAC__uint64 absoluteByteOffset);
|
||||
inline ::FLAC__SeekableStreamDecoderTellStatus callbackTell(FLAC__uint64 *absoluteByteOffset);
|
||||
inline ::FLAC__SeekableStreamDecoderLengthStatus callbackLength(FLAC__uint64 *streamLength);
|
||||
inline bool callbackEOF();
|
||||
inline ::FLAC__StreamDecoderWriteStatus callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
|
||||
inline void callbackMetadata(const ::FLAC__StreamMetadata *metadata);
|
||||
inline void callbackError(::FLAC__StreamDecoderErrorStatus status);
|
||||
|
||||
private:
|
||||
static ::FLAC__SeekableStreamDecoderReadStatus callWrapRead(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], FLAC_size_t *bytes, void *clientData);
|
||||
static ::FLAC__SeekableStreamDecoderSeekStatus callWrapSeek(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData);
|
||||
static ::FLAC__SeekableStreamDecoderTellStatus callWrapTell(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData);
|
||||
static ::FLAC__SeekableStreamDecoderLengthStatus callWrapLength(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData);
|
||||
static FLAC__bool callWrapEOF(const ::FLAC__SeekableStreamDecoder *decoder, void *clientData);
|
||||
static ::FLAC__StreamDecoderWriteStatus callWrapWrite(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData);
|
||||
static void callWrapMetadata(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData);
|
||||
static void callWrapError(const ::FLAC__SeekableStreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData);
|
||||
|
||||
void setBestConvertBufferMethod();
|
||||
static void convertBuffersGeneric(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||
static void convertBuffersStereoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||
static void convertBuffersStereo8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||
static void convertBuffersMonoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||
static void convertBuffersMono8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||
};
|
||||
|
||||
FlacInputStream::FlacInputStream(Common::SeekableReadStream *inStream, bool dispose)
|
||||
#ifdef LEGACY_FLAC
|
||||
: _decoder(::FLAC__seekable_stream_decoder_new()),
|
||||
#else
|
||||
: _decoder(::FLAC__stream_decoder_new()),
|
||||
#endif
|
||||
_inStream(inStream),
|
||||
_disposeAfterUse(dispose),
|
||||
_length(0, 1000), _lastSample(0),
|
||||
_outBuffer(NULL), _requestedSamples(0), _lastSampleWritten(false),
|
||||
_methodConvertBuffers(&FlacInputStream::convertBuffersGeneric)
|
||||
{
|
||||
assert(_inStream);
|
||||
memset(&_streaminfo, 0, sizeof(_streaminfo));
|
||||
|
||||
_sampleCache.bufReadPos = NULL;
|
||||
_sampleCache.bufFill = 0;
|
||||
|
||||
_methodConvertBuffers = &FlacInputStream::convertBuffersGeneric;
|
||||
|
||||
bool success;
|
||||
#ifdef LEGACY_FLAC
|
||||
::FLAC__seekable_stream_decoder_set_read_callback(_decoder, &FlacInputStream::callWrapRead);
|
||||
::FLAC__seekable_stream_decoder_set_seek_callback(_decoder, &FlacInputStream::callWrapSeek);
|
||||
::FLAC__seekable_stream_decoder_set_tell_callback(_decoder, &FlacInputStream::callWrapTell);
|
||||
::FLAC__seekable_stream_decoder_set_length_callback(_decoder, &FlacInputStream::callWrapLength);
|
||||
::FLAC__seekable_stream_decoder_set_eof_callback(_decoder, &FlacInputStream::callWrapEOF);
|
||||
::FLAC__seekable_stream_decoder_set_write_callback(_decoder, &FlacInputStream::callWrapWrite);
|
||||
::FLAC__seekable_stream_decoder_set_metadata_callback(_decoder, &FlacInputStream::callWrapMetadata);
|
||||
::FLAC__seekable_stream_decoder_set_error_callback(_decoder, &FlacInputStream::callWrapError);
|
||||
::FLAC__seekable_stream_decoder_set_client_data(_decoder, (void*)this);
|
||||
|
||||
success = (::FLAC__seekable_stream_decoder_init(_decoder) == FLAC__SEEKABLE_STREAM_DECODER_OK);
|
||||
#else
|
||||
success = (::FLAC__stream_decoder_init_stream(
|
||||
_decoder,
|
||||
&FlacInputStream::callWrapRead,
|
||||
&FlacInputStream::callWrapSeek,
|
||||
&FlacInputStream::callWrapTell,
|
||||
&FlacInputStream::callWrapLength,
|
||||
&FlacInputStream::callWrapEOF,
|
||||
&FlacInputStream::callWrapWrite,
|
||||
&FlacInputStream::callWrapMetadata,
|
||||
&FlacInputStream::callWrapError,
|
||||
(void*)this
|
||||
) == FLAC__STREAM_DECODER_INIT_STATUS_OK);
|
||||
#endif
|
||||
if (success) {
|
||||
if (processUntilEndOfMetadata() && _streaminfo.channels > 0) {
|
||||
_lastSample = _streaminfo.total_samples + 1;
|
||||
_length = Timestamp(0, _lastSample - 1, getRate());
|
||||
return; // no error occured
|
||||
}
|
||||
}
|
||||
|
||||
warning("FlacInputStream: could not create audio stream");
|
||||
}
|
||||
|
||||
FlacInputStream::~FlacInputStream() {
|
||||
if (_decoder != NULL) {
|
||||
#ifdef LEGACY_FLAC
|
||||
(void) ::FLAC__seekable_stream_decoder_finish(_decoder);
|
||||
::FLAC__seekable_stream_decoder_delete(_decoder);
|
||||
#else
|
||||
(void) ::FLAC__stream_decoder_finish(_decoder);
|
||||
::FLAC__stream_decoder_delete(_decoder);
|
||||
#endif
|
||||
}
|
||||
if (_disposeAfterUse)
|
||||
delete _inStream;
|
||||
}
|
||||
|
||||
inline FLAC__StreamDecoderState FlacInputStream::getStreamDecoderState() const {
|
||||
assert(_decoder != NULL);
|
||||
#ifdef LEGACY_FLAC
|
||||
return ::FLAC__seekable_stream_decoder_get_stream_decoder_state(_decoder);
|
||||
#else
|
||||
return ::FLAC__stream_decoder_get_state(_decoder);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool FlacInputStream::processSingleBlock() {
|
||||
assert(_decoder != NULL);
|
||||
#ifdef LEGACY_FLAC
|
||||
return 0 != ::FLAC__seekable_stream_decoder_process_single(_decoder);
|
||||
#else
|
||||
return 0 != ::FLAC__stream_decoder_process_single(_decoder);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool FlacInputStream::processUntilEndOfMetadata() {
|
||||
assert(_decoder != NULL);
|
||||
#ifdef LEGACY_FLAC
|
||||
return 0 != ::FLAC__seekable_stream_decoder_process_until_end_of_metadata(_decoder);
|
||||
#else
|
||||
return 0 != ::FLAC__stream_decoder_process_until_end_of_metadata(_decoder);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FlacInputStream::seekAbsolute(FLAC__uint64 sample) {
|
||||
assert(_decoder != NULL);
|
||||
#ifdef LEGACY_FLAC
|
||||
const bool result = (0 != ::FLAC__seekable_stream_decoder_seek_absolute(_decoder, sample));
|
||||
#else
|
||||
const bool result = (0 != ::FLAC__stream_decoder_seek_absolute(_decoder, sample));
|
||||
#endif
|
||||
if (result) {
|
||||
_lastSampleWritten = (_lastSample != 0 && sample >= _lastSample); // only set if we are SURE
|
||||
}
|
||||
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();
|
||||
|
||||
if (numChannels == 0) {
|
||||
warning("FlacInputStream: Stream not sucessfully initialised, cant playback");
|
||||
return -1; // streaminfo wasnt read!
|
||||
}
|
||||
|
||||
assert(numSamples % numChannels == 0); // must be multiple of channels!
|
||||
assert(buffer != NULL);
|
||||
assert(_outBuffer == NULL);
|
||||
assert(_requestedSamples == 0);
|
||||
|
||||
_outBuffer = buffer;
|
||||
_requestedSamples = numSamples;
|
||||
|
||||
// If there is still data in our buffer from the last time around,
|
||||
// copy that first.
|
||||
if (_sampleCache.bufFill > 0) {
|
||||
assert(_sampleCache.bufReadPos >= _sampleCache.bufData);
|
||||
assert(_sampleCache.bufFill % numChannels == 0);
|
||||
|
||||
const uint copySamples = MIN((uint)numSamples, _sampleCache.bufFill);
|
||||
memcpy(buffer, _sampleCache.bufReadPos, copySamples*sizeof(buffer[0]));
|
||||
|
||||
_outBuffer = buffer + copySamples;
|
||||
_requestedSamples = numSamples - copySamples;
|
||||
_sampleCache.bufReadPos += copySamples;
|
||||
_sampleCache.bufFill -= copySamples;
|
||||
}
|
||||
|
||||
bool decoderOk = true;
|
||||
|
||||
FLAC__StreamDecoderState state = getStreamDecoderState();
|
||||
|
||||
// Keep poking FLAC to process more samples until we completely satisfied the request
|
||||
// respectively until we run out of data.
|
||||
while (!_lastSampleWritten && _requestedSamples > 0 && state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) {
|
||||
assert(_sampleCache.bufFill == 0);
|
||||
assert(_requestedSamples % numChannels == 0);
|
||||
processSingleBlock();
|
||||
state = getStreamDecoderState();
|
||||
|
||||
if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
|
||||
_lastSampleWritten = true;
|
||||
}
|
||||
|
||||
// Error handling
|
||||
switch (state) {
|
||||
case FLAC__STREAM_DECODER_END_OF_STREAM:
|
||||
_lastSampleWritten = true;
|
||||
break;
|
||||
case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
|
||||
break;
|
||||
default:
|
||||
decoderOk = false;
|
||||
warning("FlacInputStream: An error occured while decoding. DecoderState is: %s",
|
||||
FLAC__StreamDecoderStateString[getStreamDecoderState()]);
|
||||
}
|
||||
|
||||
// Compute how many samples we actually produced
|
||||
const int samples = (int)(_outBuffer - buffer);
|
||||
assert(samples % numChannels == 0);
|
||||
|
||||
_outBuffer = NULL; // basically unnecessary, only for the purpose of the asserts
|
||||
_requestedSamples = 0; // basically unnecessary, only for the purpose of the asserts
|
||||
|
||||
return decoderOk ? samples : -1;
|
||||
}
|
||||
|
||||
inline ::FLAC__SeekableStreamDecoderReadStatus FlacInputStream::callbackRead(FLAC__byte buffer[], FLAC_size_t *bytes) {
|
||||
if (*bytes == 0) {
|
||||
#ifdef LEGACY_FLAC
|
||||
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; /* abort to avoid a deadlock */
|
||||
#else
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */
|
||||
#endif
|
||||
}
|
||||
|
||||
const uint32 bytesRead = _inStream->read(buffer, *bytes);
|
||||
|
||||
if (bytesRead == 0) {
|
||||
#ifdef LEGACY_FLAC
|
||||
return _inStream->eos() ? FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK : FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
|
||||
#else
|
||||
return _inStream->eos() ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||||
#endif
|
||||
}
|
||||
|
||||
*bytes = static_cast<uint>(bytesRead);
|
||||
#ifdef LEGACY_FLAC
|
||||
return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
|
||||
#else
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlacInputStream::setBestConvertBufferMethod() {
|
||||
PFCONVERTBUFFERS tempMethod = &FlacInputStream::convertBuffersGeneric;
|
||||
|
||||
const uint numChannels = getChannels();
|
||||
const uint8 numBits = (uint8)_streaminfo.bits_per_sample;
|
||||
|
||||
assert(numChannels >= 1);
|
||||
assert(numBits >= 4 && numBits <=32);
|
||||
|
||||
if (numChannels == 1) {
|
||||
if (numBits == 8)
|
||||
tempMethod = &FlacInputStream::convertBuffersMono8Bit;
|
||||
if (numBits == BUFTYPE_BITS)
|
||||
tempMethod = &FlacInputStream::convertBuffersMonoNS;
|
||||
} else if (numChannels == 2) {
|
||||
if (numBits == 8)
|
||||
tempMethod = &FlacInputStream::convertBuffersStereo8Bit;
|
||||
if (numBits == BUFTYPE_BITS)
|
||||
tempMethod = &FlacInputStream::convertBuffersStereoNS;
|
||||
} /* else ... */
|
||||
|
||||
_methodConvertBuffers = tempMethod;
|
||||
}
|
||||
|
||||
// 1 channel, no scaling
|
||||
void FlacInputStream::convertBuffersMonoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||
assert(numChannels == 1);
|
||||
assert(numBits == BUFTYPE_BITS);
|
||||
|
||||
FLAC__int32 const* inChannel1 = inChannels[0];
|
||||
|
||||
while (numSamples >= 4) {
|
||||
bufDestination[0] = static_cast<SampleType>(inChannel1[0]);
|
||||
bufDestination[1] = static_cast<SampleType>(inChannel1[1]);
|
||||
bufDestination[2] = static_cast<SampleType>(inChannel1[2]);
|
||||
bufDestination[3] = static_cast<SampleType>(inChannel1[3]);
|
||||
bufDestination += 4;
|
||||
inChannel1 += 4;
|
||||
numSamples -= 4;
|
||||
}
|
||||
|
||||
for (; numSamples > 0; --numSamples) {
|
||||
*bufDestination++ = static_cast<SampleType>(*inChannel1++);
|
||||
}
|
||||
|
||||
inChannels[0] = inChannel1;
|
||||
assert(numSamples == 0); // dint copy too many samples
|
||||
}
|
||||
|
||||
// 1 channel, scaling from 8Bit
|
||||
void FlacInputStream::convertBuffersMono8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||
assert(numChannels == 1);
|
||||
assert(numBits == 8);
|
||||
assert(8 < BUFTYPE_BITS);
|
||||
|
||||
FLAC__int32 const* inChannel1 = inChannels[0];
|
||||
|
||||
while (numSamples >= 4) {
|
||||
bufDestination[0] = static_cast<SampleType>(inChannel1[0]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[1] = static_cast<SampleType>(inChannel1[1]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[2] = static_cast<SampleType>(inChannel1[2]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[3] = static_cast<SampleType>(inChannel1[3]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination += 4;
|
||||
inChannel1 += 4;
|
||||
numSamples -= 4;
|
||||
}
|
||||
|
||||
for (; numSamples > 0; --numSamples) {
|
||||
*bufDestination++ = static_cast<SampleType>(*inChannel1++) << (BUFTYPE_BITS - 8);
|
||||
}
|
||||
|
||||
inChannels[0] = inChannel1;
|
||||
assert(numSamples == 0); // dint copy too many samples
|
||||
}
|
||||
|
||||
// 2 channels, no scaling
|
||||
void FlacInputStream::convertBuffersStereoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||
assert(numChannels == 2);
|
||||
assert(numBits == BUFTYPE_BITS);
|
||||
assert(numSamples % 2 == 0); // must be integral multiply of channels
|
||||
|
||||
|
||||
FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel
|
||||
FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel
|
||||
|
||||
while (numSamples >= 2*2) {
|
||||
bufDestination[0] = static_cast<SampleType>(inChannel1[0]);
|
||||
bufDestination[1] = static_cast<SampleType>(inChannel2[0]);
|
||||
bufDestination[2] = static_cast<SampleType>(inChannel1[1]);
|
||||
bufDestination[3] = static_cast<SampleType>(inChannel2[1]);
|
||||
bufDestination += 2 * 2;
|
||||
inChannel1 += 2;
|
||||
inChannel2 += 2;
|
||||
numSamples -= 2 * 2;
|
||||
}
|
||||
|
||||
while (numSamples > 0) {
|
||||
bufDestination[0] = static_cast<SampleType>(*inChannel1++);
|
||||
bufDestination[1] = static_cast<SampleType>(*inChannel2++);
|
||||
bufDestination += 2;
|
||||
numSamples -= 2;
|
||||
}
|
||||
|
||||
inChannels[0] = inChannel1;
|
||||
inChannels[1] = inChannel2;
|
||||
assert(numSamples == 0); // dint copy too many samples
|
||||
}
|
||||
|
||||
// 2 channels, scaling from 8Bit
|
||||
void FlacInputStream::convertBuffersStereo8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||
assert(numChannels == 2);
|
||||
assert(numBits == 8);
|
||||
assert(numSamples % 2 == 0); // must be integral multiply of channels
|
||||
assert(8 < BUFTYPE_BITS);
|
||||
|
||||
FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel
|
||||
FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel
|
||||
|
||||
while (numSamples >= 2*2) {
|
||||
bufDestination[0] = static_cast<SampleType>(inChannel1[0]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[1] = static_cast<SampleType>(inChannel2[0]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[2] = static_cast<SampleType>(inChannel1[1]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[3] = static_cast<SampleType>(inChannel2[1]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination += 2 * 2;
|
||||
inChannel1 += 2;
|
||||
inChannel2 += 2;
|
||||
numSamples -= 2 * 2;
|
||||
}
|
||||
|
||||
while (numSamples > 0) {
|
||||
bufDestination[0] = static_cast<SampleType>(*inChannel1++) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[1] = static_cast<SampleType>(*inChannel2++) << (BUFTYPE_BITS - 8);
|
||||
bufDestination += 2;
|
||||
numSamples -= 2;
|
||||
}
|
||||
|
||||
inChannels[0] = inChannel1;
|
||||
inChannels[1] = inChannel2;
|
||||
assert(numSamples == 0); // dint copy too many samples
|
||||
}
|
||||
|
||||
// all Purpose-conversion - slowest of em all
|
||||
void FlacInputStream::convertBuffersGeneric(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||
assert(numSamples % numChannels == 0); // must be integral multiply of channels
|
||||
|
||||
if (numBits < BUFTYPE_BITS) {
|
||||
const uint8 kPower = (uint8)(BUFTYPE_BITS - numBits);
|
||||
|
||||
for (; numSamples > 0; numSamples -= numChannels) {
|
||||
for (uint i = 0; i < numChannels; ++i)
|
||||
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++)) << kPower;
|
||||
}
|
||||
} else if (numBits > BUFTYPE_BITS) {
|
||||
const uint8 kPower = (uint8)(numBits - BUFTYPE_BITS);
|
||||
|
||||
for (; numSamples > 0; numSamples -= numChannels) {
|
||||
for (uint i = 0; i < numChannels; ++i)
|
||||
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++) >> kPower) ;
|
||||
}
|
||||
} else {
|
||||
for (; numSamples > 0; numSamples -= numChannels) {
|
||||
for (uint i = 0; i < numChannels; ++i)
|
||||
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++));
|
||||
}
|
||||
}
|
||||
|
||||
assert(numSamples == 0); // dint copy too many samples
|
||||
}
|
||||
|
||||
inline ::FLAC__StreamDecoderWriteStatus FlacInputStream::callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]) {
|
||||
assert(frame->header.channels == _streaminfo.channels);
|
||||
assert(frame->header.sample_rate == _streaminfo.sample_rate);
|
||||
assert(frame->header.bits_per_sample == _streaminfo.bits_per_sample);
|
||||
assert(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER || _streaminfo.min_blocksize == _streaminfo.max_blocksize);
|
||||
|
||||
// We require that either the sample cache is empty, or that no samples were requested
|
||||
assert(_sampleCache.bufFill == 0 || _requestedSamples == 0);
|
||||
|
||||
uint numSamples = frame->header.blocksize;
|
||||
const uint numChannels = getChannels();
|
||||
const uint8 numBits = (uint8)_streaminfo.bits_per_sample;
|
||||
|
||||
assert(_requestedSamples % numChannels == 0); // must be integral multiply of channels
|
||||
|
||||
const FLAC__uint64 firstSampleNumber = (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) ?
|
||||
frame->header.number.sample_number : (static_cast<FLAC__uint64>(frame->header.number.frame_number)) * _streaminfo.max_blocksize;
|
||||
|
||||
// Check whether we are about to reach beyond the last sample we are supposed to play.
|
||||
if (_lastSample != 0 && firstSampleNumber + numSamples >= _lastSample) {
|
||||
numSamples = (uint)(firstSampleNumber >= _lastSample ? 0 : _lastSample - firstSampleNumber);
|
||||
_lastSampleWritten = true;
|
||||
}
|
||||
|
||||
// The value in _requestedSamples counts raw samples, so if there are more than one
|
||||
// channel, we have to multiply the number of available sample "pairs" by numChannels
|
||||
numSamples *= numChannels;
|
||||
|
||||
const FLAC__int32 *inChannels[MAX_OUTPUT_CHANNELS];
|
||||
for (uint i = 0; i < numChannels; ++i)
|
||||
inChannels[i] = buffer[i];
|
||||
|
||||
// write the incoming samples directly into the buffer provided to us by the mixer
|
||||
if (_requestedSamples > 0) {
|
||||
assert(_requestedSamples % numChannels == 0);
|
||||
assert(_outBuffer != NULL);
|
||||
|
||||
// Copy & convert the available samples (limited both by how many we have available, and
|
||||
// by how many are actually needed).
|
||||
const uint copySamples = MIN(_requestedSamples, numSamples);
|
||||
(*_methodConvertBuffers)(_outBuffer, inChannels, copySamples, numChannels, numBits);
|
||||
|
||||
_requestedSamples -= copySamples;
|
||||
numSamples -= copySamples;
|
||||
_outBuffer += copySamples;
|
||||
}
|
||||
|
||||
// Write all remaining samples (i.e. those which didn't fit into the mixer buffer)
|
||||
// into the sample cache.
|
||||
if (_sampleCache.bufFill == 0)
|
||||
_sampleCache.bufReadPos = _sampleCache.bufData;
|
||||
const uint cacheSpace = (_sampleCache.bufData + BUFFER_SIZE) - (_sampleCache.bufReadPos + _sampleCache.bufFill);
|
||||
assert(numSamples <= cacheSpace);
|
||||
(*_methodConvertBuffers)(_sampleCache.bufReadPos + _sampleCache.bufFill, inChannels, numSamples, numChannels, numBits);
|
||||
|
||||
_sampleCache.bufFill += numSamples;
|
||||
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
inline ::FLAC__SeekableStreamDecoderSeekStatus FlacInputStream::callbackSeek(FLAC__uint64 absoluteByteOffset) {
|
||||
_inStream->seek(absoluteByteOffset, SEEK_SET);
|
||||
const bool result = (absoluteByteOffset == (FLAC__uint64)_inStream->pos());
|
||||
|
||||
#ifdef LEGACY_FLAC
|
||||
return result ? FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK : FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
|
||||
#else
|
||||
return result ? FLAC__STREAM_DECODER_SEEK_STATUS_OK : FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline ::FLAC__SeekableStreamDecoderTellStatus FlacInputStream::callbackTell(FLAC__uint64 *absoluteByteOffset) {
|
||||
*absoluteByteOffset = static_cast<FLAC__uint64>(_inStream->pos());
|
||||
#ifdef LEGACY_FLAC
|
||||
return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
|
||||
#else
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline ::FLAC__SeekableStreamDecoderLengthStatus FlacInputStream::callbackLength(FLAC__uint64 *streamLength) {
|
||||
*streamLength = static_cast<FLAC__uint64>(_inStream->size());
|
||||
#ifdef LEGACY_FLAC
|
||||
return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
|
||||
#else
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool FlacInputStream::callbackEOF() {
|
||||
return _inStream->eos();
|
||||
}
|
||||
|
||||
|
||||
inline void FlacInputStream::callbackMetadata(const ::FLAC__StreamMetadata *metadata) {
|
||||
assert(_decoder != NULL);
|
||||
assert(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); // others arent really interesting
|
||||
|
||||
_streaminfo = metadata->data.stream_info;
|
||||
setBestConvertBufferMethod(); // should be set after getting stream-information. FLAC always parses the info first
|
||||
}
|
||||
inline void FlacInputStream::callbackError(::FLAC__StreamDecoderErrorStatus status) {
|
||||
// some of these are non-critical-Errors
|
||||
debug(1, "FlacInputStream: An error occured while decoding. DecoderState is: %s",
|
||||
FLAC__StreamDecoderErrorStatusString[status]);
|
||||
}
|
||||
|
||||
/* Static Callback Wrappers */
|
||||
::FLAC__SeekableStreamDecoderReadStatus FlacInputStream::callWrapRead(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], FLAC_size_t *bytes, void *clientData) {
|
||||
FlacInputStream *instance = (FlacInputStream *)clientData;
|
||||
assert(0 != instance);
|
||||
return instance->callbackRead(buffer, bytes);
|
||||
}
|
||||
|
||||
::FLAC__SeekableStreamDecoderSeekStatus FlacInputStream::callWrapSeek(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData) {
|
||||
FlacInputStream *instance = (FlacInputStream *)clientData;
|
||||
assert(0 != instance);
|
||||
return instance->callbackSeek(absoluteByteOffset);
|
||||
}
|
||||
|
||||
::FLAC__SeekableStreamDecoderTellStatus FlacInputStream::callWrapTell(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData) {
|
||||
FlacInputStream *instance = (FlacInputStream *)clientData;
|
||||
assert(0 != instance);
|
||||
return instance->callbackTell(absoluteByteOffset);
|
||||
}
|
||||
|
||||
::FLAC__SeekableStreamDecoderLengthStatus FlacInputStream::callWrapLength(const ::FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData) {
|
||||
FlacInputStream *instance = (FlacInputStream *)clientData;
|
||||
assert(0 != instance);
|
||||
return instance->callbackLength(streamLength);
|
||||
}
|
||||
|
||||
FLAC__bool FlacInputStream::callWrapEOF(const ::FLAC__SeekableStreamDecoder *decoder, void *clientData) {
|
||||
FlacInputStream *instance = (FlacInputStream *)clientData;
|
||||
assert(0 != instance);
|
||||
return instance->callbackEOF();
|
||||
}
|
||||
|
||||
::FLAC__StreamDecoderWriteStatus FlacInputStream::callWrapWrite(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData) {
|
||||
FlacInputStream *instance = (FlacInputStream *)clientData;
|
||||
assert(0 != instance);
|
||||
return instance->callbackWrite(frame, buffer);
|
||||
}
|
||||
|
||||
void FlacInputStream::callWrapMetadata(const ::FLAC__SeekableStreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData) {
|
||||
FlacInputStream *instance = (FlacInputStream *)clientData;
|
||||
assert(0 != instance);
|
||||
instance->callbackMetadata(metadata);
|
||||
}
|
||||
|
||||
void FlacInputStream::callWrapError(const ::FLAC__SeekableStreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData) {
|
||||
FlacInputStream *instance = (FlacInputStream *)clientData;
|
||||
assert(0 != instance);
|
||||
instance->callbackError(status);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- Flac factory functions ---
|
||||
#pragma mark -
|
||||
|
||||
SeekableAudioStream *makeFlacStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
return new FlacInputStream(stream, disposeAfterUse);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_FLAC
|
73
sound/flac.h
73
sound/flac.h
@ -1,73 +0,0 @@
|
||||
/* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - agos
|
||||
* - kyra
|
||||
* - m4
|
||||
* - queen
|
||||
* - saga
|
||||
* - scumm
|
||||
* - sword1
|
||||
* - sword2
|
||||
* - touche
|
||||
* - tucker
|
||||
*/
|
||||
|
||||
#ifndef SOUND_FLAC_H
|
||||
#define SOUND_FLAC_H
|
||||
|
||||
#include "common/types.h"
|
||||
#include "common/sys.h"
|
||||
|
||||
#ifdef USE_FLAC
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class AudioStream;
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occured
|
||||
*/
|
||||
SeekableAudioStream *makeFlacStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_FLAC
|
||||
#endif // #ifndef SOUND_FLAC_H
|
@ -43,7 +43,7 @@ const Config::EmulatorDescription Config::_drivers[] = {
|
||||
{ "auto", "<default>", kAuto, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
|
||||
{ "mame", "MAME OPL emulator", kMame, kFlagOpl2 },
|
||||
#ifndef DISABLE_DOSBOX_OPL
|
||||
{ "db", "DOSBox OPL emulator (experimental)", kDOSBox, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
|
||||
{ "db", "DOSBox OPL emulator", kDOSBox, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 },
|
||||
#endif
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
@ -90,6 +90,8 @@ Config::DriverId Config::detect(OplType type) {
|
||||
}
|
||||
|
||||
// Detect the first matching emulator
|
||||
drv = -1;
|
||||
|
||||
for (int i = 1; _drivers[i].name; ++i) {
|
||||
if (_drivers[i].flags & flags) {
|
||||
drv = _drivers[i].id;
|
||||
@ -100,6 +102,10 @@ Config::DriverId Config::detect(OplType type) {
|
||||
return drv;
|
||||
}
|
||||
|
||||
OPL *Config::create(OplType type) {
|
||||
return create(kAuto, type);
|
||||
}
|
||||
|
||||
OPL *Config::create(DriverId driver, OplType type) {
|
||||
// On invalid driver selection, we try to do some fallback detection
|
||||
if (driver == -1) {
|
||||
|
@ -85,7 +85,7 @@ public:
|
||||
* Wrapper to easily init an OPL chip, without specifing an emulator.
|
||||
* By default it will try to initialize an OPL2 emulator, thus an AdLib card.
|
||||
*/
|
||||
static OPL *create(OplType type = kOpl2) { return create(detect(type), type); }
|
||||
static OPL *create(OplType type = kOpl2);
|
||||
|
||||
private:
|
||||
static const EmulatorDescription _drivers[];
|
||||
|
@ -83,7 +83,7 @@ static const MidiDriverDescription s_musicDrivers[] = {
|
||||
{"mt32", "MT-32 Emulation", MD_MT32, MDT_MIDI},
|
||||
#endif
|
||||
|
||||
// The flags for the "adlib" driver indicates that it can do adlib and MIDI.
|
||||
// The flags for the "adlib" driver indicates that it can do AdLib and MIDI.
|
||||
{"adlib", "AdLib", MD_ADLIB, MDT_ADLIB},
|
||||
{"pcspk", "PC Speaker", MD_PCSPK, MDT_PCSPK},
|
||||
{"pcjr", "IBM PCjr", MD_PCJR, MDT_PCSPK},
|
||||
|
@ -100,7 +100,7 @@ enum MidiDriverFlags {
|
||||
MDT_NONE = 0,
|
||||
MDT_PCSPK = 1 << 0, // PC Speaker: Maps to MD_PCSPK and MD_PCJR
|
||||
MDT_CMS = 1 << 1, // Creative Music System / Gameblaster: Maps to MD_CMS
|
||||
MDT_ADLIB = 1 << 2, // Adlib: Maps to MD_ADLIB
|
||||
MDT_ADLIB = 1 << 2, // AdLib: Maps to MD_ADLIB
|
||||
MDT_TOWNS = 1 << 3, // FM-TOWNS: Maps to MD_TOWNS
|
||||
MDT_MIDI = 1 << 4, // Real MIDI
|
||||
MDT_PREFER_MIDI = 1 << 5 // Real MIDI output is preferred
|
||||
|
@ -86,7 +86,7 @@ public:
|
||||
* Is the mixer ready and setup? This may not be the case on systems which
|
||||
* don't support digital sound output. In that case, the mixer proc may
|
||||
* never be called. That in turn can cause breakage in games which try to
|
||||
* sync with an audio stream. In particular, the Adlib MIDI emulation...
|
||||
* sync with an audio stream. In particular, the AdLib MIDI emulation...
|
||||
*
|
||||
* @return whether the mixer is ready and setup
|
||||
*
|
||||
|
@ -3,18 +3,18 @@ MODULE := sound
|
||||
MODULE_OBJS := \
|
||||
audiocd.o \
|
||||
audiostream.o \
|
||||
flac.o \
|
||||
fmopl.o \
|
||||
mididrv.o \
|
||||
midiparser.o \
|
||||
mixer.o \
|
||||
mp3.o \
|
||||
mpu401.o \
|
||||
musicplugin.o \
|
||||
null.o \
|
||||
raw.o \
|
||||
timestamp.o \
|
||||
vorbis.o \
|
||||
decoders/flac.o \
|
||||
decoders/mp3.o \
|
||||
decoders/raw.o \
|
||||
decoders/vorbis.o \
|
||||
softsynth/adlib.o \
|
||||
softsynth/opl/dosbox.o \
|
||||
softsynth/opl/mame.o \
|
||||
|
349
sound/mp3.cpp
349
sound/mp3.cpp
@ -1,349 +0,0 @@
|
||||
/* 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 "sound/mp3.h"
|
||||
|
||||
#ifdef USE_MAD
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "sound/audiocd.h"
|
||||
#include "sound/audiostream.h"
|
||||
|
||||
#include <mad.h>
|
||||
|
||||
|
||||
namespace Audio {
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- MP3 (MAD) stream ---
|
||||
#pragma mark -
|
||||
|
||||
|
||||
class MP3InputStream : public SeekableAudioStream {
|
||||
protected:
|
||||
enum State {
|
||||
MP3_STATE_INIT, // Need to init the decoder
|
||||
MP3_STATE_READY, // ready for processing data
|
||||
MP3_STATE_EOS // end of data reached (may need to loop)
|
||||
};
|
||||
|
||||
Common::SeekableReadStream *_inStream;
|
||||
DisposeAfterUse::Flag _disposeAfterUse;
|
||||
|
||||
uint _posInFrame;
|
||||
State _state;
|
||||
|
||||
Timestamp _length;
|
||||
mad_timer_t _totalTime;
|
||||
|
||||
mad_stream _stream;
|
||||
mad_frame _frame;
|
||||
mad_synth _synth;
|
||||
|
||||
enum {
|
||||
BUFFER_SIZE = 5 * 8192
|
||||
};
|
||||
|
||||
// This buffer contains a slab of input data
|
||||
byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD];
|
||||
|
||||
public:
|
||||
MP3InputStream(Common::SeekableReadStream *inStream,
|
||||
DisposeAfterUse::Flag dispose);
|
||||
~MP3InputStream();
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
bool endOfData() const { return _state == MP3_STATE_EOS; }
|
||||
bool isStereo() const { return MAD_NCHANNELS(&_frame.header) == 2; }
|
||||
int getRate() const { return _frame.header.samplerate; }
|
||||
|
||||
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, DisposeAfterUse::Flag dispose) :
|
||||
_inStream(inStream),
|
||||
_disposeAfterUse(dispose),
|
||||
_posInFrame(0),
|
||||
_state(MP3_STATE_INIT),
|
||||
_length(0, 1000),
|
||||
_totalTime(mad_timer_zero) {
|
||||
|
||||
// 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 the length of the stream
|
||||
initStream();
|
||||
|
||||
while (_state != MP3_STATE_EOS)
|
||||
readHeader();
|
||||
|
||||
_length = Timestamp(mad_timer_count(_totalTime, MAD_UNITS_MILLISECONDS), getRate());
|
||||
|
||||
deinitStream();
|
||||
|
||||
// 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.
|
||||
decodeMP3Data();
|
||||
}
|
||||
|
||||
MP3InputStream::~MP3InputStream() {
|
||||
deinitStream();
|
||||
|
||||
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete _inStream;
|
||||
}
|
||||
|
||||
void MP3InputStream::decodeMP3Data() {
|
||||
do {
|
||||
if (_state == MP3_STATE_INIT)
|
||||
initStream();
|
||||
|
||||
if (_state == MP3_STATE_EOS)
|
||||
return;
|
||||
|
||||
// If necessary, load more data into the stream decoder
|
||||
if (_stream.error == MAD_ERROR_BUFLEN)
|
||||
readMP3Data();
|
||||
|
||||
while (_state == MP3_STATE_READY) {
|
||||
// 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) {
|
||||
if (_stream.error == MAD_ERROR_BUFLEN) {
|
||||
break; // Read more data
|
||||
} else if (MAD_RECOVERABLE(_stream.error)) {
|
||||
// Note: we will occasionally see MAD_ERROR_BADDATAPTR errors here.
|
||||
// These are normal and expected (caused by our frame skipping (i.e. "seeking")
|
||||
// code above).
|
||||
debug(6, "MP3InputStream: Recoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
|
||||
continue;
|
||||
} else {
|
||||
warning("MP3InputStream: Unrecoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Synthesize PCM data
|
||||
mad_synth_frame(&_synth, &_frame);
|
||||
_posInFrame = 0;
|
||||
break;
|
||||
}
|
||||
} while (_state != MP3_STATE_EOS && _stream.error == MAD_ERROR_BUFLEN);
|
||||
|
||||
if (_stream.error != MAD_ERROR_NONE)
|
||||
_state = MP3_STATE_EOS;
|
||||
}
|
||||
|
||||
void MP3InputStream::readMP3Data() {
|
||||
uint32 remaining = 0;
|
||||
|
||||
// Give up immediately if we already used up all data in the stream
|
||||
if (_inStream->eos()) {
|
||||
_state = MP3_STATE_EOS;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_stream.next_frame) {
|
||||
// If there is still data in the MAD stream, we need to preserve it.
|
||||
// Note that we use memmove, as we are reusing the same buffer,
|
||||
// and hence the data regions we copy from and to may overlap.
|
||||
remaining = _stream.bufend - _stream.next_frame;
|
||||
assert(remaining < BUFFER_SIZE); // Paranoia check
|
||||
memmove(_buf, _stream.next_frame, remaining);
|
||||
}
|
||||
|
||||
// Try to read the next block
|
||||
uint32 size = _inStream->read(_buf + remaining, BUFFER_SIZE - remaining);
|
||||
if (size <= 0) {
|
||||
_state = MP3_STATE_EOS;
|
||||
return;
|
||||
}
|
||||
|
||||
// Feed the data we just read into the stream decoder
|
||||
_stream.error = MAD_ERROR_NONE;
|
||||
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
|
||||
sample += (1L << (MAD_F_FRACBITS - 16));
|
||||
|
||||
// clip
|
||||
if (sample > MAD_F_ONE - 1)
|
||||
sample = MAD_F_ONE - 1;
|
||||
else if (sample < -MAD_F_ONE)
|
||||
sample = -MAD_F_ONE;
|
||||
|
||||
// quantize and scale to not saturate when mixing a lot of channels
|
||||
return sample >> (MAD_F_FRACBITS + 1 - 16);
|
||||
}
|
||||
|
||||
int MP3InputStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples = 0;
|
||||
// Keep going as long as we have input available
|
||||
while (samples < numSamples && _state != MP3_STATE_EOS) {
|
||||
const int len = MIN(numSamples, samples + (int)(_synth.pcm.length - _posInFrame) * MAD_NCHANNELS(&_frame.header));
|
||||
while (samples < len) {
|
||||
*buffer++ = (int16)scale_sample(_synth.pcm.samples[0][_posInFrame]);
|
||||
samples++;
|
||||
if (MAD_NCHANNELS(&_frame.header) == 2) {
|
||||
*buffer++ = (int16)scale_sample(_synth.pcm.samples[1][_posInFrame]);
|
||||
samples++;
|
||||
}
|
||||
_posInFrame++;
|
||||
}
|
||||
if (_posInFrame >= _synth.pcm.length) {
|
||||
// We used up all PCM data in the current frame -- read & decode more
|
||||
decodeMP3Data();
|
||||
}
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- MP3 factory functions ---
|
||||
#pragma mark -
|
||||
|
||||
SeekableAudioStream *makeMP3Stream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
return new MP3InputStream(stream, disposeAfterUse);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_MAD
|
73
sound/mp3.h
73
sound/mp3.h
@ -1,73 +0,0 @@
|
||||
/* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - agos
|
||||
* - kyra
|
||||
* - m4
|
||||
* - queen
|
||||
* - saga
|
||||
* - scumm
|
||||
* - sword1
|
||||
* - sword2
|
||||
* - touche
|
||||
* - tucker
|
||||
*/
|
||||
|
||||
#ifndef SOUND_MP3_H
|
||||
#define SOUND_MP3_H
|
||||
|
||||
#include "common/types.h"
|
||||
#include "common/sys.h"
|
||||
|
||||
#ifdef USE_MAD
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class AudioStream;
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occured
|
||||
*/
|
||||
SeekableAudioStream *makeMP3Stream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_MAD
|
||||
#endif // #ifndef SOUND_MP3_H
|
@ -331,7 +331,7 @@ public:
|
||||
|
||||
template<bool stereo, bool reverseStereo>
|
||||
RateConverter *makeRateConverter(st_rate_t inrate, st_rate_t outrate) {
|
||||
if (outrate && inrate != outrate) {
|
||||
if (inrate != outrate) {
|
||||
if ((inrate % outrate) == 0) {
|
||||
return new SimpleRateConverter<stereo, reverseStereo>(inrate, outrate);
|
||||
} else {
|
||||
|
466
sound/raw.cpp
466
sound/raw.cpp
@ -1,466 +0,0 @@
|
||||
/* 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<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
|
||||
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)
|
||||
delete[] const_cast<byte *>(_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<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
|
||||
int RawMemoryStream<stereo, is16Bit, isUnsigned, isLE>::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 stereo, bool is16Bit, bool isUnsigned, bool isLE>
|
||||
bool RawMemoryStream<stereo, is16Bit, isUnsigned, isLE>::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<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
|
||||
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<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
|
||||
int RawDiskStream<stereo, is16Bit, isUnsigned, isLE>::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<int>(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 stereo, bool is16Bit, bool isUnsigned, bool isLE>
|
||||
bool RawDiskStream<stereo, is16Bit, isUnsigned, isLE>::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<STEREO, true, UNSIGNED, true>(rate, ptr, len, autoFree); \
|
||||
else \
|
||||
return new RawMemoryStream<STEREO, true, UNSIGNED, false>(rate, ptr, len, autoFree); \
|
||||
} else \
|
||||
return new RawMemoryStream<STEREO, false, UNSIGNED, false>(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<STEREO, true, UNSIGNED, true>(rate, disposeStream, stream, block, numBlocks); \
|
||||
else \
|
||||
return new RawDiskStream<STEREO, true, UNSIGNED, false>(rate, disposeStream, stream, block, numBlocks); \
|
||||
} else \
|
||||
return new RawDiskStream<STEREO, false, UNSIGNED, false>(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
|
144
sound/raw.h
144
sound/raw.h
@ -1,144 +0,0 @@
|
||||
/* 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
|
@ -165,6 +165,7 @@ static void drawMessage(int offset, const Common::String &text) {
|
||||
surf.pixels = calloc(surf.w, surf.h);
|
||||
font.drawString(&surf, text, 0, 0, surf.w, color, Graphics::kTextAlignCenter);
|
||||
int y = g_system->getHeight() / 2 - font.getFontHeight() / 2 + offset * (font.getFontHeight() + 1);
|
||||
// TODO implement in Residual
|
||||
//g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, 0, y, surf.w, surf.h);
|
||||
//g_system->updateScreen();
|
||||
free(surf.pixels);
|
||||
@ -282,12 +283,13 @@ int MidiDriver_MT32::open() {
|
||||
171, 0, 0, 0
|
||||
};
|
||||
|
||||
// FIXME
|
||||
// TODO implement in Residual
|
||||
//g_system->setPalette(dummy_palette, 0, 5);
|
||||
drawMessage(-1, "Initialising MT-32 Emulator");
|
||||
if (!_synth->open(prop))
|
||||
return MERR_DEVICE_NOT_AVAILABLE;
|
||||
_initialising = false;
|
||||
// TODO implement in Residual
|
||||
//g_system->fillScreen(0);
|
||||
//g_system->updateScreen();
|
||||
_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
|
||||
|
@ -177,7 +177,7 @@ struct Handler : public DOSBox::Handler {
|
||||
|
||||
uint32 writeAddr(uint32 port, uint8 val) {
|
||||
adlib_write_index(port, val);
|
||||
return index;
|
||||
return opl_index;
|
||||
}
|
||||
|
||||
void generate(int16 *chan, uint samples) {
|
||||
|
@ -53,7 +53,7 @@ bool OPL::init(int rate) {
|
||||
if (_opl)
|
||||
MAME::OPLDestroy(_opl);
|
||||
|
||||
_opl = MAME::makeAdlibOPL(rate);
|
||||
_opl = MAME::makeAdLibOPL(rate);
|
||||
return (_opl != 0);
|
||||
}
|
||||
|
||||
@ -1208,7 +1208,7 @@ int OPLTimerOver(FM_OPL *OPL, int c) {
|
||||
return OPL->status >> 7;
|
||||
}
|
||||
|
||||
FM_OPL *makeAdlibOPL(int rate) {
|
||||
FM_OPL *makeAdLibOPL(int rate) {
|
||||
// We need to emulate one YM3812 chip
|
||||
int env_bits = FMOPL_ENV_BITS_HQ;
|
||||
int eg_ent = FMOPL_EG_ENT_HQ;
|
||||
|
@ -173,7 +173,7 @@ void OPLWriteReg(FM_OPL *OPL, int r, int v);
|
||||
void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length);
|
||||
|
||||
// Factory method
|
||||
FM_OPL *makeAdlibOPL(int rate);
|
||||
FM_OPL *makeAdLibOPL(int rate);
|
||||
|
||||
// OPL API implementation
|
||||
class OPL : public ::OPL::OPL {
|
||||
|
@ -56,7 +56,7 @@ static const fltype frqmul_tab[16] = {
|
||||
0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15
|
||||
};
|
||||
// calculated frequency multiplication values (depend on sampling rate)
|
||||
static float frqmul[16];
|
||||
static fltype frqmul[16];
|
||||
|
||||
// key scale levels
|
||||
static Bit8u kslev[8][16];
|
||||
@ -125,8 +125,18 @@ static Bit32u wavestart[8] = {
|
||||
};
|
||||
|
||||
// envelope generator function constants
|
||||
static fltype attackconst[4] = {1/2.82624,1/2.25280,1/1.88416,1/1.59744};
|
||||
static fltype decrelconst[4] = {1/39.28064,1/31.41608,1/26.17344,1/22.44608};
|
||||
static fltype attackconst[4] = {
|
||||
(fltype)(1/2.82624),
|
||||
(fltype)(1/2.25280),
|
||||
(fltype)(1/1.88416),
|
||||
(fltype)(1/1.59744)
|
||||
};
|
||||
static fltype decrelconst[4] = {
|
||||
(fltype)(1/39.28064),
|
||||
(fltype)(1/31.41608),
|
||||
(fltype)(1/26.17344),
|
||||
(fltype)(1/22.44608)
|
||||
};
|
||||
|
||||
|
||||
void operator_advance(op_type* op_pt, Bit32s vib) {
|
||||
@ -274,9 +284,9 @@ void operator_attack(op_type* op_pt) {
|
||||
op_pt->amp = 1.0;
|
||||
op_pt->step_amp = 1.0;
|
||||
}
|
||||
op_pt->step_skip_pos <<= 1;
|
||||
if (op_pt->step_skip_pos==0) op_pt->step_skip_pos = 1;
|
||||
if (op_pt->step_skip_pos & op_pt->env_step_skip_a) { // check if required to skip next step
|
||||
op_pt->step_skip_pos_a <<= 1;
|
||||
if (op_pt->step_skip_pos_a==0) op_pt->step_skip_pos_a = 1;
|
||||
if (op_pt->step_skip_pos_a & op_pt->env_step_skip_a) { // check if required to skip next step
|
||||
op_pt->step_amp = op_pt->amp;
|
||||
}
|
||||
}
|
||||
@ -487,7 +497,7 @@ void adlib_init(Bit32u samplerate) {
|
||||
op[i].env_step_a = 0;
|
||||
op[i].env_step_d = 0;
|
||||
op[i].env_step_r = 0;
|
||||
op[i].step_skip_pos = 0;
|
||||
op[i].step_skip_pos_a = 0;
|
||||
op[i].env_step_skip_a = 0;
|
||||
|
||||
#if defined(OPLTYPE_IS_OPL3)
|
||||
@ -504,7 +514,7 @@ void adlib_init(Bit32u samplerate) {
|
||||
}
|
||||
|
||||
status = 0;
|
||||
index = 0;
|
||||
opl_index = 0;
|
||||
|
||||
|
||||
// create vibrato table
|
||||
@ -552,9 +562,9 @@ void adlib_init(Bit32u samplerate) {
|
||||
wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1) )*PI*2/WAVEPREC));
|
||||
wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1)+1)*PI*2/WAVEPREC));
|
||||
wavtable[i] = wavtable[(i<<1) +WAVEPREC];
|
||||
// table to be verified, alternative: (zero-less)
|
||||
/* wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)(((i*2+1)<<1)-1)*PI/WAVEPREC));
|
||||
wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)(((i*2+1)<<1) )*PI/WAVEPREC));
|
||||
// alternative: (zero-less)
|
||||
/* wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+1)*PI/WAVEPREC));
|
||||
wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+3)*PI/WAVEPREC));
|
||||
wavtable[i] = wavtable[(i<<1)-1+WAVEPREC]; */
|
||||
}
|
||||
for (i=0;i<(WAVEPREC>>3);i++) {
|
||||
@ -901,11 +911,11 @@ Bitu adlib_reg_read(Bitu port) {
|
||||
}
|
||||
|
||||
void adlib_write_index(Bitu port, Bit8u val) {
|
||||
index = val;
|
||||
opl_index = val;
|
||||
#if defined(OPLTYPE_IS_OPL3)
|
||||
if ((port&3)!=0) {
|
||||
// possibly second set
|
||||
if (((adlibreg[0x105]&1)!=0) || (index==5)) index |= ARC_SECONDSET;
|
||||
if (((adlibreg[0x105]&1)!=0) || (opl_index==5)) opl_index |= ARC_SECONDSET;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -39,6 +39,13 @@
|
||||
#define Bit8u uint8
|
||||
#define Bit8s int8
|
||||
|
||||
|
||||
/*
|
||||
define attribution that inlines/forces inlining of a function (optional)
|
||||
*/
|
||||
#define OPL_INLINE INLINE
|
||||
|
||||
|
||||
#undef NUM_CHANNELS
|
||||
#if defined(OPLTYPE_IS_OPL3)
|
||||
#define NUM_CHANNELS 18
|
||||
@ -137,7 +144,7 @@ typedef struct operator_struct {
|
||||
Bit32u generator_pos; // for non-standard sample rates we need to determine how many samples have passed
|
||||
Bits cur_env_step; // current (standardized) sample position
|
||||
Bits env_step_a,env_step_d,env_step_r; // number of std samples of one step (for attack/decay/release mode)
|
||||
Bit8u step_skip_pos; // position of 8-cyclic step skipping (always 2^x to check against mask)
|
||||
Bit8u step_skip_pos_a; // position of 8-cyclic step skipping (always 2^x to check against mask)
|
||||
Bits env_step_skip_a; // bitmask that determines if a step is skipped (respective bit is zero then)
|
||||
|
||||
#if defined(OPLTYPE_IS_OPL3)
|
||||
@ -153,7 +160,7 @@ op_type op[MAXOPERATORS];
|
||||
Bits int_samplerate;
|
||||
|
||||
Bit8u status;
|
||||
Bit32u index;
|
||||
Bit32u opl_index;
|
||||
#if defined(OPLTYPE_IS_OPL3)
|
||||
Bit8u adlibreg[512]; // adlib register set (including second set)
|
||||
Bit8u wave_sel[44]; // waveform selection
|
||||
|
@ -1275,7 +1275,7 @@ void SID::enable_external_filter(bool enable) {
|
||||
* E.g. provided a clock frequency of ~ 1MHz, the sample frequency can not
|
||||
* be set lower than ~ 8kHz. A lower sample frequency would make the
|
||||
* resampling code overfill its 16k sample ring buffer.
|
||||
*
|
||||
*
|
||||
* The end of passband frequency is also limited:
|
||||
* pass_freq <= 0.9*sample_freq/2
|
||||
*
|
||||
|
250
sound/vorbis.cpp
250
sound/vorbis.cpp
@ -1,250 +0,0 @@
|
||||
/* 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 "sound/vorbis.h"
|
||||
|
||||
#ifdef USE_VORBIS
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "sound/audiostream.h"
|
||||
#include "sound/audiocd.h"
|
||||
|
||||
#ifdef USE_TREMOR
|
||||
#ifdef __GP32__ // GP32 uses custom libtremor
|
||||
#include <ivorbisfile.h>
|
||||
#else
|
||||
#include <tremor/ivorbisfile.h>
|
||||
#endif
|
||||
#else
|
||||
#include <vorbis/vorbisfile.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace Audio {
|
||||
|
||||
// These are wrapper functions to allow using a SeekableReadStream object to
|
||||
// provide data to the OggVorbis_File object.
|
||||
|
||||
static size_t read_stream_wrap(void *ptr, size_t size, size_t nmemb, void *datasource) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
|
||||
|
||||
uint32 result = stream->read(ptr, size * nmemb);
|
||||
|
||||
return result / size;
|
||||
}
|
||||
|
||||
static int seek_stream_wrap(void *datasource, ogg_int64_t offset, int whence) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
|
||||
stream->seek((int32)offset, whence);
|
||||
return stream->pos();
|
||||
}
|
||||
|
||||
static int close_stream_wrap(void *datasource) {
|
||||
// Do nothing -- we leave it up to the VorbisInputStream to free memory as appropriate.
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long tell_stream_wrap(void *datasource) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
|
||||
return stream->pos();
|
||||
}
|
||||
|
||||
static ov_callbacks g_stream_wrap = {
|
||||
read_stream_wrap, seek_stream_wrap, close_stream_wrap, tell_stream_wrap
|
||||
};
|
||||
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- Ogg Vorbis stream ---
|
||||
#pragma mark -
|
||||
|
||||
|
||||
class VorbisInputStream : public SeekableAudioStream {
|
||||
protected:
|
||||
Common::SeekableReadStream *_inStream;
|
||||
DisposeAfterUse::Flag _disposeAfterUse;
|
||||
|
||||
bool _isStereo;
|
||||
int _rate;
|
||||
|
||||
Timestamp _length;
|
||||
|
||||
OggVorbis_File _ovFile;
|
||||
|
||||
int16 _buffer[4096];
|
||||
const int16 *_bufferEnd;
|
||||
const int16 *_pos;
|
||||
|
||||
public:
|
||||
// startTime / duration are in milliseconds
|
||||
VorbisInputStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose);
|
||||
~VorbisInputStream();
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
bool endOfData() const { return _pos >= _bufferEnd; }
|
||||
bool isStereo() const { return _isStereo; }
|
||||
int getRate() const { return _rate; }
|
||||
|
||||
bool seek(const Timestamp &where);
|
||||
Timestamp getLength() const { return _length; }
|
||||
protected:
|
||||
bool refill();
|
||||
};
|
||||
|
||||
VorbisInputStream::VorbisInputStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
|
||||
_inStream(inStream),
|
||||
_disposeAfterUse(dispose),
|
||||
_length(0, 1000),
|
||||
_bufferEnd(_buffer + ARRAYSIZE(_buffer)) {
|
||||
|
||||
int res = ov_open_callbacks(inStream, &_ovFile, NULL, 0, g_stream_wrap);
|
||||
if (res < 0) {
|
||||
warning("Could not create Vorbis stream (%d)", res);
|
||||
_pos = _bufferEnd;
|
||||
return;
|
||||
}
|
||||
|
||||
// Read in initial data
|
||||
if (!refill())
|
||||
return;
|
||||
|
||||
// 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 == DisposeAfterUse::YES)
|
||||
delete _inStream;
|
||||
}
|
||||
|
||||
int VorbisInputStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples = 0;
|
||||
while (samples < numSamples && _pos < _bufferEnd) {
|
||||
const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
|
||||
memcpy(buffer, _pos, len * 2);
|
||||
buffer += len;
|
||||
_pos += len;
|
||||
samples += len;
|
||||
if (_pos >= _bufferEnd) {
|
||||
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
|
||||
uint len_left = sizeof(_buffer);
|
||||
char *read_pos = (char *)_buffer;
|
||||
|
||||
while (len_left > 0) {
|
||||
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
|
||||
// specific signedness, byte order or bit depth as in Vorbisfile.
|
||||
result = ov_read(&_ovFile, read_pos, len_left,
|
||||
NULL);
|
||||
#else
|
||||
#ifdef SCUMM_BIG_ENDIAN
|
||||
result = ov_read(&_ovFile, read_pos, len_left,
|
||||
1,
|
||||
2, // 16 bit
|
||||
1, // signed
|
||||
NULL);
|
||||
#else
|
||||
result = ov_read(&_ovFile, read_pos, len_left,
|
||||
0,
|
||||
2, // 16 bit
|
||||
1, // signed
|
||||
NULL);
|
||||
#endif
|
||||
#endif
|
||||
if (result == OV_HOLE) {
|
||||
// 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;
|
||||
break;
|
||||
} else if (result < 0) {
|
||||
warning("Error reading from Vorbis stream (%d)", int(result));
|
||||
_pos = _bufferEnd;
|
||||
// Don't delete it yet, that causes problems in
|
||||
// the CD player emulation code.
|
||||
return false;
|
||||
} else {
|
||||
len_left -= result;
|
||||
read_pos += result;
|
||||
}
|
||||
}
|
||||
|
||||
_pos = _buffer;
|
||||
_bufferEnd = (int16 *)read_pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- Ogg Vorbis factory functions ---
|
||||
#pragma mark -
|
||||
|
||||
SeekableAudioStream *makeVorbisStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
return new VorbisInputStream(stream, disposeAfterUse);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_VORBIS
|
@ -1,73 +0,0 @@
|
||||
/* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - agos
|
||||
* - kyra
|
||||
* - m4
|
||||
* - queen
|
||||
* - saga
|
||||
* - scumm
|
||||
* - sword1
|
||||
* - sword2
|
||||
* - touche
|
||||
* - tucker
|
||||
*/
|
||||
|
||||
#ifndef SOUND_VORBIS_H
|
||||
#define SOUND_VORBIS_H
|
||||
|
||||
#include "common/types.h"
|
||||
#include "common/sys.h"
|
||||
|
||||
#ifdef USE_VORBIS
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class AudioStream;
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occured
|
||||
*/
|
||||
SeekableAudioStream *makeVorbisStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_VORBIS
|
||||
#endif // #ifndef SOUND_VORBIS_H
|
@ -69,11 +69,13 @@ sub html_entities_to_ascii {
|
||||
# ü -> ue
|
||||
# & -> &
|
||||
# ł -> l
|
||||
# Š -> S
|
||||
$text =~ s/á/a/g;
|
||||
$text =~ s/é/e/g;
|
||||
$text =~ s/ó/o/g;
|
||||
$text =~ s/ø/o/g;
|
||||
$text =~ s/ł/l/g;
|
||||
$text =~ s/Š/S/g;
|
||||
|
||||
$text =~ s/ä/a/g;
|
||||
$text =~ s/ü/ue/g;
|
||||
@ -95,6 +97,7 @@ sub html_entities_to_cpp {
|
||||
$text =~ s/ó/\\363/g;
|
||||
$text =~ s/ø/\\370/g;
|
||||
$text =~ s/ł/l/g;
|
||||
$text =~ s/Š/\\352/g;
|
||||
|
||||
$text =~ s/ä/\\344/g;
|
||||
$text =~ s/ö/\\366/g;
|
||||
@ -114,6 +117,7 @@ sub html_entities_to_rtf {
|
||||
$text =~ s/ó/\\'97/g;
|
||||
$text =~ s/ø/\\'bf/g;
|
||||
$text =~ s/ł/\\uc0\\u322 /g;
|
||||
$text =~ s/Š/\\uc0\\u352 /g;
|
||||
|
||||
$text =~ s/ä/\\'8a/g;
|
||||
$text =~ s/ö/\\'9a/g;
|
||||
@ -133,6 +137,7 @@ sub html_entities_to_tex {
|
||||
$text =~ s/ó/\\'o/g;
|
||||
$text =~ s/ø/{\\o}/g;
|
||||
$text =~ s/ł/{\\l}/g;
|
||||
$text =~ s/Š/{\\v S}/g;
|
||||
|
||||
$text =~ s/ä/\\"a/g;
|
||||
$text =~ s/ö/\\"o/g;
|
||||
@ -428,15 +433,18 @@ sub add_paragraph {
|
||||
print wrap($tab, $tab, html_entities_to_ascii($text))."\n";
|
||||
print "\n";
|
||||
} elsif ($mode eq "TEX") {
|
||||
$text = html_entities_to_tex($text);
|
||||
print '\item' . "\n";
|
||||
print $text;
|
||||
print "\n";
|
||||
} elsif ($mode eq "RTF") {
|
||||
$text = html_entities_to_rtf($text);
|
||||
# Center text
|
||||
print '\pard\qc' . "\n";
|
||||
print "\\\n";
|
||||
print $text . "\\\n";
|
||||
} elsif ($mode eq "CPP") {
|
||||
$text = html_entities_to_ascii($text);
|
||||
my $line_start = '"C0""';
|
||||
my $line_end = '",';
|
||||
print $line_start . $text . $line_end . "\n";
|
||||
|
Loading…
x
Reference in New Issue
Block a user