From 068ff94d20a79bfa2f1c47ce811b8b3a80438e4b Mon Sep 17 00:00:00 2001 From: Pawel Kolodziejski Date: Sun, 7 Jul 2013 15:29:33 +0200 Subject: [PATCH] ALL: synced with ScummVM --- Makefile | 9 +- Makefile.common | 4 +- audio/audiostream.cpp | 10 +- audio/decoders/adpcm.cpp | 63 +- audio/decoders/adpcm.h | 8 +- audio/decoders/adpcm_intern.h | 7 + audio/decoders/aiff.h | 2 +- audio/fmopl.cpp | 2 +- audio/fmopl.h | 4 +- audio/mididrv.h | 4 +- audio/midiparser.cpp | 1 + audio/midiparser.h | 1 + audio/mixer.cpp | 19 +- audio/mixer_intern.h | 3 +- audio/softsynth/adlib.cpp | 1891 +++++++++---- audio/softsynth/fluidsynth.cpp | 52 +- audio/softsynth/mt32.cpp | 256 +- audio/softsynth/mt32/AReverbModel.cpp | 244 +- audio/softsynth/mt32/AReverbModel.h | 59 +- audio/softsynth/mt32/BReverbModel.cpp | 393 +++ audio/softsynth/mt32/BReverbModel.h | 112 + audio/softsynth/mt32/DelayReverb.cpp | 81 +- audio/softsynth/mt32/DelayReverb.h | 9 +- audio/softsynth/mt32/FreeverbModel.cpp | 6 +- audio/softsynth/mt32/FreeverbModel.h | 4 +- audio/softsynth/mt32/LA32Ramp.cpp | 10 +- audio/softsynth/mt32/LA32Ramp.h | 2 +- audio/softsynth/mt32/LA32WaveGenerator.cpp | 418 +++ audio/softsynth/mt32/LA32WaveGenerator.h | 246 ++ audio/softsynth/mt32/LegacyWaveGenerator.cpp | 347 +++ audio/softsynth/mt32/LegacyWaveGenerator.h | 146 + audio/softsynth/mt32/Part.cpp | 149 +- audio/softsynth/mt32/Part.h | 24 +- audio/softsynth/mt32/Partial.cpp | 435 +-- audio/softsynth/mt32/Partial.h | 21 +- audio/softsynth/mt32/PartialManager.cpp | 2 +- audio/softsynth/mt32/PartialManager.h | 2 +- audio/softsynth/mt32/Poly.cpp | 11 +- audio/softsynth/mt32/Poly.h | 7 +- audio/softsynth/mt32/ROMInfo.cpp | 111 + audio/softsynth/mt32/ROMInfo.h | 77 + audio/softsynth/mt32/Structures.h | 2 +- audio/softsynth/mt32/Synth.cpp | 242 +- audio/softsynth/mt32/Synth.h | 131 +- audio/softsynth/mt32/TVA.cpp | 6 +- audio/softsynth/mt32/TVA.h | 2 +- audio/softsynth/mt32/TVF.cpp | 2 +- audio/softsynth/mt32/TVF.h | 2 +- audio/softsynth/mt32/TVP.cpp | 7 +- audio/softsynth/mt32/TVP.h | 2 +- audio/softsynth/mt32/Tables.cpp | 41 +- audio/softsynth/mt32/Tables.h | 15 +- audio/softsynth/mt32/mmath.h | 6 +- audio/softsynth/mt32/module.mk | 4 + audio/softsynth/mt32/mt32emu.h | 30 +- audio/softsynth/opl/dosbox.cpp | 27 +- audio/softsynth/opl/mame.cpp | 2 +- backends/events/default/default-events.cpp | 5 +- backends/events/sdl/sdl-events.cpp | 7 +- backends/fs/abstract-fs.h | 2 +- .../surfacesdl/surfacesdl-graphics.cpp | 2 + .../graphics/surfacesdl/surfacesdl-graphics.h | 12 +- backends/keymapper/keymapper.h | 2 +- backends/midi/coreaudio.cpp | 117 +- backends/midi/seq.cpp | 11 +- backends/mixer/sdl/sdl-mixer.cpp | 2 +- backends/modular-backend.cpp | 8 +- backends/modular-backend.h | 9 +- backends/module.mk | 12 +- backends/mutex/sdl/sdl-mutex.cpp | 6 +- backends/platform/android/android.cpp | 11 +- backends/platform/android/android.h | 2 +- backends/platform/android/android.mk | 4 +- backends/platform/sdl/ps3/ps3.cpp | 3 +- backends/platform/sdl/sdl-sys.h | 3 + backends/platform/sdl/sdl.cpp | 28 +- backends/platform/sdl/sdl.h | 4 +- backends/saves/recorder/recorder-saves.cpp | 35 + backends/saves/recorder/recorder-saves.h | 36 + backends/timer/default/default-timer.cpp | 2 +- base/commandLine.cpp | 89 +- base/main.cpp | 35 +- base/plugins.cpp | 4 +- base/plugins.h | 2 +- common/EventDispatcher.cpp | 14 +- common/EventRecorder.cpp | 428 --- common/EventRecorder.h | 110 - common/archive.cpp | 6 +- common/archive.h | 6 +- common/bufferedstream.h | 2 +- common/c++11-compat.h | 42 + common/config-file.cpp | 11 +- common/config-file.h | 3 +- common/config-manager.h | 2 +- common/cosinetables.cpp | 6 +- common/cosinetables.h | 9 +- common/debug-channels.h | 2 +- common/debug.cpp | 2 +- common/debug.h | 4 + common/events.h | 12 +- common/fft.h | 1 - common/file.cpp | 2 +- common/forbidden.h | 25 + common/fs.cpp | 2 +- common/func.h | 2 +- common/hash-str.h | 2 +- common/hashmap.cpp | 2 +- common/hashmap.h | 2 +- common/language.h | 2 +- common/macresman.cpp | 4 +- common/memorypool.h | 2 +- common/memstream.h | 39 +- common/module.mk | 6 +- common/mutex.cpp | 2 +- common/platform.cpp | 4 +- common/platform.h | 6 +- common/random.cpp | 11 +- common/random.h | 2 +- common/rdft.h | 36 +- common/recorderfile.cpp | 708 +++++ common/recorderfile.h | 180 ++ common/rect.h | 2 +- common/scummsys.h | 8 +- common/sinetables.cpp | 12 +- common/sinetables.h | 7 + common/singleton.h | 2 +- common/str.cpp | 25 +- common/str.h | 5 + common/stream.cpp | 8 +- common/stream.h | 2 +- common/system.cpp | 17 +- common/system.h | 18 +- common/taskbar.h | 2 +- common/textconsole.h | 2 +- common/unzip.cpp | 2 +- common/unzip.h | 2 +- common/updates.h | 2 +- common/util.cpp | 5 + common/util.h | 13 +- common/winexe.cpp | 2 +- common/xmlparser.cpp | 2 +- common/zlib.cpp | 12 +- common/zlib.h | 6 +- configure | 373 +-- devtools/create_project/config.h | 6 +- devtools/create_project/create_project.cpp | 47 +- devtools/create_project/msbuild.cpp | 19 +- devtools/create_project/msvc.cpp | 2 +- devtools/create_project/scripts/postbuild.cmd | 3 + devtools/credits.pl | 40 +- devtools/module.mk | 20 +- dists/msvc12/readme.txt | 6 + engines/advancedDetector.cpp | 11 +- engines/advancedDetector.h | 3 + engines/engine.cpp | 7 +- engines/grim/lua/lstrlib.cpp | 2 + graphics/cursorman.cpp | 7 + graphics/cursorman.h | 6 +- graphics/decoders/jpeg.cpp | 16 +- graphics/primitives.cpp | 2 +- graphics/primitives.h | 2 +- graphics/scaler.h | 6 + graphics/thumbnail.cpp | 147 +- graphics/thumbnail.h | 20 +- graphics/yuv_to_rgb.h | 15 +- gui/EventRecorder.cpp | 665 +++++ gui/EventRecorder.h | 293 ++ gui/ThemeEngine.cpp | 35 +- gui/ThemeEngine.h | 15 +- gui/ThemeParser.cpp | 10 +- gui/about.cpp | 57 +- gui/browser.cpp | 18 +- gui/browser.h | 5 + gui/browser_osx.mm | 73 + gui/debugger.cpp | 2 +- gui/debugger.h | 2 +- gui/dialog.h | 3 + gui/editrecorddialog.cpp | 87 + gui/editrecorddialog.h | 56 + gui/fluidsynth-dialog.cpp | 366 +++ gui/fluidsynth-dialog.h | 104 + gui/gui-manager.cpp | 138 +- gui/gui-manager.h | 6 + gui/launcher.cpp | 89 +- gui/launcher.h | 15 +- gui/module.mk | 13 + gui/onscreendialog.cpp | 231 ++ gui/onscreendialog.h | 64 + gui/options.cpp | 166 +- gui/options.h | 9 +- gui/predictivedialog.cpp | 5 +- gui/recorderdialog.cpp | 291 ++ gui/recorderdialog.h | 81 + gui/themes/default.inc | 2512 ++++++++++------- gui/themes/modern.zip | Bin 1454701 -> 1483951 bytes gui/themes/modern/THEMERC | 2 +- gui/themes/modern/editbtn.bmp | Bin 0 -> 3126 bytes gui/themes/modern/editbtn_small.bmp | Bin 0 -> 822 bytes gui/themes/modern/fastreplay.bmp | Bin 0 -> 3126 bytes gui/themes/modern/fastreplay_small.bmp | Bin 0 -> 822 bytes gui/themes/modern/modern_gfx.stx | 8 + gui/themes/modern/modern_layout.stx | 310 +- gui/themes/modern/modern_layout_lowres.stx | 309 +- gui/themes/modern/stopbtn.bmp | Bin 0 -> 3126 bytes gui/themes/modern/stopbtn_small.bmp | Bin 0 -> 822 bytes gui/themes/modern/switchbtn.bmp | Bin 0 -> 3126 bytes gui/themes/modern/switchbtn_small.bmp | Bin 0 -> 822 bytes gui/widget.cpp | 4 +- gui/widgets/edittext.cpp | 8 +- gui/widgets/list.cpp | 17 +- gui/widgets/list.h | 1 + gui/widgets/popup.cpp | 26 +- po/POTFILES | 1 + ports.mk | 20 +- test/common/bitstream.h | 152 + test/common/bufferedseekablereadstream.h | 7 + test/common/memorywritestream.h | 31 + test/common/str.h | 8 + test/cxxtest/cxxtestgen.pl | 0 test/cxxtest/sample/Makefile.PL | 0 test/cxxtest/sample/msvc/CxxTest_1_Run.dsp | 186 +- test/cxxtest/sample/msvc/CxxTest_2_Build.dsp | 188 +- .../sample/msvc/CxxTest_3_Generate.dsp | 186 +- .../cxxtest/sample/msvc/CxxTest_Workspace.dsw | 116 +- video/bink_decoder.cpp | 9 +- video/bink_decoder.h | 4 +- video/video_decoder.cpp | 50 +- video/video_decoder.h | 26 + 228 files changed, 11689 insertions(+), 4232 deletions(-) create mode 100644 audio/softsynth/mt32/BReverbModel.cpp create mode 100644 audio/softsynth/mt32/BReverbModel.h create mode 100644 audio/softsynth/mt32/LA32WaveGenerator.cpp create mode 100644 audio/softsynth/mt32/LA32WaveGenerator.h create mode 100644 audio/softsynth/mt32/LegacyWaveGenerator.cpp create mode 100644 audio/softsynth/mt32/LegacyWaveGenerator.h create mode 100644 audio/softsynth/mt32/ROMInfo.cpp create mode 100644 audio/softsynth/mt32/ROMInfo.h create mode 100644 backends/saves/recorder/recorder-saves.cpp create mode 100644 backends/saves/recorder/recorder-saves.h delete mode 100644 common/EventRecorder.cpp delete mode 100644 common/EventRecorder.h create mode 100644 common/c++11-compat.h create mode 100644 common/recorderfile.cpp create mode 100644 common/recorderfile.h create mode 100644 dists/msvc12/readme.txt create mode 100644 gui/EventRecorder.cpp create mode 100644 gui/EventRecorder.h create mode 100644 gui/editrecorddialog.cpp create mode 100644 gui/editrecorddialog.h create mode 100644 gui/fluidsynth-dialog.cpp create mode 100644 gui/fluidsynth-dialog.h create mode 100644 gui/onscreendialog.cpp create mode 100644 gui/onscreendialog.h create mode 100644 gui/recorderdialog.cpp create mode 100644 gui/recorderdialog.h create mode 100644 gui/themes/modern/editbtn.bmp create mode 100644 gui/themes/modern/editbtn_small.bmp create mode 100644 gui/themes/modern/fastreplay.bmp create mode 100644 gui/themes/modern/fastreplay_small.bmp create mode 100644 gui/themes/modern/stopbtn.bmp create mode 100644 gui/themes/modern/stopbtn_small.bmp create mode 100644 gui/themes/modern/switchbtn.bmp create mode 100644 gui/themes/modern/switchbtn_small.bmp create mode 100644 test/common/bitstream.h create mode 100644 test/common/memorywritestream.h mode change 100644 => 100755 test/cxxtest/cxxtestgen.pl mode change 100644 => 100755 test/cxxtest/sample/Makefile.PL diff --git a/Makefile b/Makefile index 1c5b2c57039..e6a791cf90a 100644 --- a/Makefile +++ b/Makefile @@ -32,8 +32,10 @@ ifeq "$(HAVE_GCC)" "1" # being helpful. #CXXFLAGS+= -Wmissing-format-attribute - # Disable RTTI and exceptions +ifneq "$(BACKEND)" "tizen" + # Disable RTTI and exceptions. These settings cause tizen apps to crash CXXFLAGS+= -fno-rtti -fno-exceptions +endif ifneq "$(HAVE_CLANG)" "1" # enable checking of pointers returned by "new", but only when we do not @@ -44,6 +46,11 @@ endif ifeq "$(HAVE_CLANG)" "1" CXXFLAGS+= -Wno-conversion -Wno-shorten-64-to-32 -Wno-sign-compare -Wno-four-char-constants + # We use a anonymous nested type declaration in an anonymous union in + # common/str.h. This is no standard construct and clang warns about it. + # It works for all our target systems though, thus we simply disable that + # warning. + CXXFLAGS+= -Wno-nested-anon-types endif ifeq "$(HAVE_ICC)" "1" diff --git a/Makefile.common b/Makefile.common index bd3f037f088..bbacac6f9ba 100644 --- a/Makefile.common +++ b/Makefile.common @@ -80,7 +80,7 @@ endif $(EXECUTABLE): $(OBJS) $(QUIET_LINK)$(LD) $(LDFLAGS) $(PRE_OBJS_FLAGS) $+ $(POST_OBJS_FLAGS) $(LIBS) -o $@ -distclean: clean +distclean: clean clean-devtools $(RM) config.h config.mk config.log clean: @@ -174,7 +174,7 @@ ifeq ($(origin VER_REV), undefined) VER_DIRTY := $(shell cd $(srcdir); git update-index --refresh --unmerged 1>/dev/null 2>&1; git diff-index --quiet HEAD || echo "-dirty") # Get the working copy base revision #ResidualVM: --always -VER_REV := $(shell cd $(srcdir); git describe --always --match desc/\* | cut -d '-' -f 2-)$(VER_DIRTY) +VER_REV := $(shell cd $(srcdir); git describe --always --long --match desc/\* | cut -d '-' -f 2-)$(VER_DIRTY) endif else GITROOT := git://github.com/residualvm/residualvm.git diff --git a/audio/audiostream.cpp b/audio/audiostream.cpp index b570a8f610f..804fbec79b2 100644 --- a/audio/audiostream.cpp +++ b/audio/audiostream.cpp @@ -30,7 +30,7 @@ #include "audio/audiostream.h" #include "audio/decoders/flac.h" #include "audio/decoders/mp3.h" -//#include "audio/decoders/quicktime.h" +//#include "audio/decoders/quicktime.h" do not include in REsidualVM #include "audio/decoders/raw.h" #include "audio/decoders/vorbis.h" @@ -99,6 +99,10 @@ LoopingAudioStream::LoopingAudioStream(RewindableAudioStream *stream, uint loops // TODO: Properly indicate error _loops = _completeIterations = 1; } + if (stream->endOfData()) { + // Apparently this is an empty stream + _loops = _completeIterations = 1; + } } int LoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) { @@ -119,6 +123,10 @@ int LoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) { _loops = _completeIterations = 1; return samplesRead; } + if (_parent->endOfData()) { + // Apparently this is an empty stream + _loops = _completeIterations = 1; + } return samplesRead + readBuffer(buffer + samplesRead, remainingSamples); } diff --git a/audio/decoders/adpcm.cpp b/audio/decoders/adpcm.cpp index 2fe509e1f30..61b0abaaca0 100644 --- a/audio/decoders/adpcm.cpp +++ b/audio/decoders/adpcm.cpp @@ -268,7 +268,6 @@ static const int MSADPCMAdaptationTable[] = { 768, 614, 512, 409, 307, 230, 230, 230 }; - int16 MS_ADPCMStream::decodeMS(ADPCMChannelStatus *c, byte code) { int32 predictor; @@ -290,40 +289,42 @@ int16 MS_ADPCMStream::decodeMS(ADPCMChannelStatus *c, byte code) { int MS_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { int samples; byte data; - int i = 0; + int i; - samples = 0; + for (samples = 0; samples < numSamples && !endOfData(); samples++) { + if (_decodedSampleCount == 0) { + if (_blockPos[0] == _blockAlign) { + // read block header + for (i = 0; i < _channels; i++) { + _status.ch[i].predictor = CLIP(_stream->readByte(), (byte)0, (byte)6); + _status.ch[i].coeff1 = MSADPCMAdaptCoeff1[_status.ch[i].predictor]; + _status.ch[i].coeff2 = MSADPCMAdaptCoeff2[_status.ch[i].predictor]; + } - while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { - if (_blockPos[0] == _blockAlign) { - // read block header - for (i = 0; i < _channels; i++) { - _status.ch[i].predictor = CLIP(_stream->readByte(), (byte)0, (byte)6); - _status.ch[i].coeff1 = MSADPCMAdaptCoeff1[_status.ch[i].predictor]; - _status.ch[i].coeff2 = MSADPCMAdaptCoeff2[_status.ch[i].predictor]; + for (i = 0; i < _channels; i++) + _status.ch[i].delta = _stream->readSint16LE(); + + for (i = 0; i < _channels; i++) + _status.ch[i].sample1 = _stream->readSint16LE(); + + for (i = 0; i < _channels; i++) + _decodedSamples[_decodedSampleCount++] = _status.ch[i].sample2 = _stream->readSint16LE(); + + for (i = 0; i < _channels; i++) + _decodedSamples[_decodedSampleCount++] = _status.ch[i].sample1; + + _blockPos[0] = _channels * 7; + } else { + data = _stream->readByte(); + _blockPos[0]++; + _decodedSamples[_decodedSampleCount++] = decodeMS(&_status.ch[0], (data >> 4) & 0x0f); + _decodedSamples[_decodedSampleCount++] = decodeMS(&_status.ch[_channels - 1], data & 0x0f); } - - for (i = 0; i < _channels; i++) - _status.ch[i].delta = _stream->readSint16LE(); - - for (i = 0; i < _channels; i++) - _status.ch[i].sample1 = _stream->readSint16LE(); - - for (i = 0; i < _channels; i++) - buffer[samples++] = _status.ch[i].sample2 = _stream->readSint16LE(); - - for (i = 0; i < _channels; i++) - buffer[samples++] = _status.ch[i].sample1; - - _blockPos[0] = _channels * 7; } - for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) { - data = _stream->readByte(); - _blockPos[0]++; - buffer[samples] = decodeMS(&_status.ch[0], (data >> 4) & 0x0f); - buffer[samples + 1] = decodeMS(&_status.ch[_channels - 1], data & 0x0f); - } + // (1 - (count - 1)) ensures that _decodedSamples acts as a FIFO of depth 2 + buffer[samples] = _decodedSamples[1 - (_decodedSampleCount - 1)]; + _decodedSampleCount--; } return samples; @@ -432,7 +433,7 @@ int16 Ima_ADPCMStream::decodeIMA(byte code, int channel) { return samp; } -RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign) { +RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, ADPCMType type, int rate, int channels, uint32 blockAlign) { // If size is 0, report the entire size of the stream if (!size) size = stream->size(); diff --git a/audio/decoders/adpcm.h b/audio/decoders/adpcm.h index ac8d529917f..d3c46574bff 100644 --- a/audio/decoders/adpcm.h +++ b/audio/decoders/adpcm.h @@ -51,7 +51,7 @@ class RewindableAudioStream; // http://wiki.multimedia.cx/index.php?title=Category:ADPCM_Audio_Codecs // Usually, if the audio stream we're trying to play has the FourCC header // string intact, it's easy to discern which encoding is used -enum typesADPCM { +enum ADPCMType { kADPCMOki, // Dialogic/Oki ADPCM (aka VOX) kADPCMMSIma, // Microsoft IMA ADPCM kADPCMMS, // Microsoft ADPCM @@ -76,9 +76,9 @@ enum typesADPCM { RewindableAudioStream *makeADPCMStream( Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, - uint32 size, typesADPCM type, - int rate = 22050, - int channels = 2, + uint32 size, ADPCMType type, + int rate, + int channels, uint32 blockAlign = 0); } // End of namespace Audio diff --git a/audio/decoders/adpcm_intern.h b/audio/decoders/adpcm_intern.h index 3b8d8c74d03..66a1aa605f6 100644 --- a/audio/decoders/adpcm_intern.h +++ b/audio/decoders/adpcm_intern.h @@ -206,12 +206,19 @@ public: if (blockAlign == 0) error("MS_ADPCMStream(): blockAlign isn't specified for MS ADPCM"); memset(&_status, 0, sizeof(_status)); + _decodedSampleCount = 0; } + virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); } + virtual int readBuffer(int16 *buffer, const int numSamples); protected: int16 decodeMS(ADPCMChannelStatus *c, byte); + +private: + uint8 _decodedSampleCount; + int16 _decodedSamples[4]; }; // Duck DK3 IMA ADPCM Decoder diff --git a/audio/decoders/aiff.h b/audio/decoders/aiff.h index afcdb6ae6c0..0d96e73c26d 100644 --- a/audio/decoders/aiff.h +++ b/audio/decoders/aiff.h @@ -48,7 +48,7 @@ class SeekableAudioStream; * successful. In that case, the stream's seek position will be set to the * start of the audio data, and size, rate and flags contain information * necessary for playback. Currently this function only supports uncompressed - * raw PCM data as well as IMA ADPCM. + * raw PCM. */ extern bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags); diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp index da655643a7c..d2fe7dc0e88 100644 --- a/audio/fmopl.cpp +++ b/audio/fmopl.cpp @@ -137,7 +137,7 @@ OPL *Config::create(DriverId driver, OplType type) { return new MAME::OPL(); else warning("MAME OPL emulator only supports OPL2 emulation"); - return 0; + return 0; #ifndef DISABLE_DOSBOX_OPL case kDOSBox: diff --git a/audio/fmopl.h b/audio/fmopl.h index 323cc3d028d..ad1794d8734 100644 --- a/audio/fmopl.h +++ b/audio/fmopl.h @@ -129,7 +129,9 @@ public: /** * Function to directly write to a specific OPL register. - * This writes to *both* chips for a Dual OPL2. + * This writes to *both* chips for a Dual OPL2. We allow + * writing to secondary OPL registers by using register + * values >= 0x100. * * @param r hardware register number to write to * @param v value, which will be written diff --git a/audio/mididrv.h b/audio/mididrv.h index fb3e29bd60a..56b4a265cbd 100644 --- a/audio/mididrv.h +++ b/audio/mididrv.h @@ -194,7 +194,9 @@ public: enum { // PROP_TIMEDIV = 1, PROP_OLD_ADLIB = 2, - PROP_CHANNEL_MASK = 3 + PROP_CHANNEL_MASK = 3, + // HACK: Not so nice, but our SCUMM AdLib code is in audio/ + PROP_SCUMM_OPL3 = 4 }; /** diff --git a/audio/midiparser.cpp b/audio/midiparser.cpp index eec32c05d18..9c144c24794 100644 --- a/audio/midiparser.cpp +++ b/audio/midiparser.cpp @@ -46,6 +46,7 @@ _numTracks(0), _activeTrack(255), _abortParse(0) { memset(_activeNotes, 0, sizeof(_activeNotes)); + memset(_tracks, 0, sizeof(_tracks)); _nextEvent.start = NULL; _nextEvent.delta = 0; _nextEvent.event = 0; diff --git a/audio/midiparser.h b/audio/midiparser.h index a4dbf174e12..bb9749b97fc 100644 --- a/audio/midiparser.h +++ b/audio/midiparser.h @@ -394,6 +394,7 @@ public: static MidiParser *createParser_SMF(); static MidiParser *createParser_XMIDI(XMidiCallbackProc proc = defaultXMidiCallback, void *refCon = 0); + static MidiParser *createParser_QT(); static void timerCallback(void *data) { ((MidiParser *) data)->onTimer(); } }; diff --git a/audio/mixer.cpp b/audio/mixer.cpp index 965766170da..ab3ed9eb2de 100644 --- a/audio/mixer.cpp +++ b/audio/mixer.cpp @@ -20,6 +20,8 @@ * */ +#include "gui/EventRecorder.h" + #include "common/util.h" #include "common/system.h" #include "common/textconsole.h" @@ -171,9 +173,9 @@ private: #pragma mark --- Mixer --- #pragma mark - - +// TODO: parameter "system" is unused MixerImpl::MixerImpl(OSystem *system, uint sampleRate) - : _syst(system), _mutex(), _sampleRate(sampleRate), _mixerReady(false), _handleSeed(0), _soundTypeSettings() { + : _mutex(), _sampleRate(sampleRate), _mixerReady(false), _handleSeed(0), _soundTypeSettings() { assert(sampleRate > 0); @@ -427,6 +429,7 @@ void MixerImpl::pauseHandle(SoundHandle handle, bool paused) { bool MixerImpl::isSoundIDActive(int id) { Common::StackLock lock(_mutex); + g_eventRec.updateSubsystems(); for (int i = 0; i != NUM_CHANNELS; i++) if (_channels[i] && _channels[i]->getId() == id) return true; @@ -443,6 +446,7 @@ int MixerImpl::getSoundID(SoundHandle handle) { bool MixerImpl::isSoundHandleActive(SoundHandle handle) { Common::StackLock lock(_mutex); + g_eventRec.updateSubsystems(); const int index = handle._val % NUM_CHANNELS; return _channels[index] && _channels[index]->getHandle()._val == handle._val; } @@ -491,7 +495,7 @@ Channel::Channel(Mixer *mixer, Mixer::SoundType type, AudioStream *stream, DisposeAfterUse::Flag autofreeStream, bool reverseStereo, int id, bool permanent) : _type(type), _mixer(mixer), _id(id), _permanent(permanent), _volume(Mixer::kMaxChannelVolume), _balance(0), _pauseLevel(0), _samplesConsumed(0), _samplesDecoded(0), _mixerTimeStamp(0), - _pauseStartTime(0), _pauseTime(0), _converter(0), + _pauseStartTime(0), _pauseTime(0), _converter(0), _volL(0), _volR(0), _stream(stream, autofreeStream) { assert(mixer); assert(stream); @@ -556,12 +560,12 @@ void Channel::pause(bool paused) { _pauseLevel++; if (_pauseLevel == 1) - _pauseStartTime = g_system->getMillis(); + _pauseStartTime = g_system->getMillis(true); } else if (_pauseLevel > 0) { _pauseLevel--; if (!_pauseLevel) { - _pauseTime = (g_system->getMillis() - _pauseStartTime); + _pauseTime = (g_system->getMillis(true) - _pauseStartTime); _pauseStartTime = 0; } } @@ -579,7 +583,7 @@ Timestamp Channel::getElapsedTime() { if (isPaused()) delta = _pauseStartTime - _mixerTimeStamp; else - delta = g_system->getMillis() - _mixerTimeStamp - _pauseTime; + delta = g_system->getMillis(true) - _mixerTimeStamp - _pauseTime; // Convert the number of samples into a time duration. @@ -599,13 +603,12 @@ int Channel::mix(int16 *data, uint len) { assert(_stream); int res = 0; - if (_stream->endOfData()) { // TODO: call drain method } else { assert(_converter); _samplesConsumed = _samplesDecoded; - _mixerTimeStamp = g_system->getMillis(); + _mixerTimeStamp = g_system->getMillis(true); _pauseTime = 0; res = _converter->flow(*_stream, data, len, _volL, _volR); _samplesDecoded += res; diff --git a/audio/mixer_intern.h b/audio/mixer_intern.h index 77aaf416b51..ba36b0697c1 100644 --- a/audio/mixer_intern.h +++ b/audio/mixer_intern.h @@ -51,10 +51,9 @@ namespace Audio { class MixerImpl : public Mixer { private: enum { - NUM_CHANNELS = 32 + NUM_CHANNELS = 32 // ResidualVM specific }; - OSystem *_syst; Common::Mutex _mutex; const uint _sampleRate; diff --git a/audio/softsynth/adlib.cpp b/audio/softsynth/adlib.cpp index 32a5f4a9108..0cadea7f220 100644 --- a/audio/softsynth/adlib.cpp +++ b/audio/softsynth/adlib.cpp @@ -32,7 +32,13 @@ #include "common/translation.h" #ifdef DEBUG_ADLIB -static int tick; +static int g_tick; +#endif + +// Only include OPL3 when we actually have an AdLib emulator builtin, which +// supports OPL3. +#ifndef DISABLE_DOSBOX_OPL +#define ENABLE_OPL3 #endif class MidiDriver_ADLIB; @@ -52,21 +58,21 @@ struct InstrumentExtra { } PACKED_STRUCT; struct AdLibInstrument { - byte mod_characteristic; - byte mod_scalingOutputLevel; - byte mod_attackDecay; - byte mod_sustainRelease; - byte mod_waveformSelect; - byte car_characteristic; - byte car_scalingOutputLevel; - byte car_attackDecay; - byte car_sustainRelease; - byte car_waveformSelect; + byte modCharacteristic; + byte modScalingOutputLevel; + byte modAttackDecay; + byte modSustainRelease; + byte modWaveformSelect; + byte carCharacteristic; + byte carScalingOutputLevel; + byte carAttackDecay; + byte carSustainRelease; + byte carWaveformSelect; byte feedback; - byte flags_a; - InstrumentExtra extra_a; - byte flags_b; - InstrumentExtra extra_b; + byte flagsA; + InstrumentExtra extraA; + byte flagsB; + InstrumentExtra extraB; byte duration; } PACKED_STRUCT; #include "common/pack-end.h" @@ -77,16 +83,20 @@ class AdLibPart : public MidiChannel { protected: // AdLibPart *_prev, *_next; AdLibVoice *_voice; - int16 _pitchbend; - byte _pitchbend_factor; - int8 _transpose_eff; - byte _vol_eff; - int8 _detune_eff; - byte _modwheel; + int16 _pitchBend; + byte _pitchBendFactor; + //int8 _transposeEff; + byte _volEff; + int8 _detuneEff; + byte _modWheel; bool _pedal; byte _program; - byte _pri_eff; - AdLibInstrument _part_instr; + byte _priEff; + byte _pan; + AdLibInstrument _partInstr; +#ifdef ENABLE_OPL3 + AdLibInstrument _partInstrSecondary; +#endif protected: MidiDriver_ADLIB *_owner; @@ -99,21 +109,25 @@ protected: public: AdLibPart() { _voice = 0; - _pitchbend = 0; - _pitchbend_factor = 2; - _transpose_eff = 0; - _vol_eff = 0; - _detune_eff = 0; - _modwheel = 0; + _pitchBend = 0; + _pitchBendFactor = 2; + //_transposeEff = 0; + _volEff = 0; + _detuneEff = 0; + _modWheel = 0; _pedal = 0; _program = 0; - _pri_eff = 0; + _priEff = 0; + _pan = 64; _owner = 0; _allocated = false; _channel = 0; - memset(&_part_instr, 0, sizeof(_part_instr)); + memset(&_partInstr, 0, sizeof(_partInstr)); +#ifdef ENABLE_OPL3 + memset(&_partInstrSecondary, 0, sizeof(_partInstrSecondary)); +#endif } MidiDriver *device(); @@ -132,7 +146,7 @@ public: void controlChange(byte control, byte value); void modulationWheel(byte value); void volume(byte value); - void panPosition(byte value) { return; } // Not supported + void panPosition(byte value); void pitchBendFactor(byte value); void detune(byte value); void priority(byte value); @@ -162,7 +176,6 @@ public: void noteOff(byte note); void noteOn(byte note, byte velocity); void programChange(byte program) { } - void pitchBend(int16 bend) { } // Control Change messages void modulationWheel(byte value) { } @@ -181,26 +194,26 @@ private: struct Struct10 { byte active; - int16 cur_val; + int16 curVal; int16 count; - uint16 max_value; - int16 start_value; + uint16 maxValue; + int16 startValue; byte loop; - byte table_a[4]; - byte table_b[4]; + byte tableA[4]; + byte tableB[4]; int8 unk3; - int8 modwheel; - int8 modwheel_last; - uint16 speed_lo_max; - uint16 num_steps; - int16 speed_hi; + int8 modWheel; + int8 modWheelLast; + uint16 speedLoMax; + uint16 numSteps; + int16 speedHi; int8 direction; - uint16 speed_lo; - uint16 speed_lo_counter; + uint16 speedLo; + uint16 speedLoCounter; }; struct Struct11 { - int16 modify_val; + int16 modifyVal; byte param, flag0x40, flag0x10; Struct10 *s10; }; @@ -208,11 +221,11 @@ struct Struct11 { struct AdLibVoice { AdLibPart *_part; AdLibVoice *_next, *_prev; - byte _waitforpedal; + byte _waitForPedal; byte _note; byte _channel; - byte _twochan; - byte _vol_1, _vol_2; + byte _twoChan; + byte _vol1, _vol2; int16 _duration; Struct10 _s10a; @@ -220,26 +233,34 @@ struct AdLibVoice { Struct10 _s10b; Struct11 _s11b; +#ifdef ENABLE_OPL3 + byte _secTwoChan; + byte _secVol1, _secVol2; +#endif + AdLibVoice() { memset(this, 0, sizeof(AdLibVoice)); } }; struct AdLibSetParams { - byte a, b, c, d; + byte registerBase; + byte shift; + byte mask; + byte inversion; }; -static const byte channel_mappings[9] = { +static const byte g_operator1Offsets[9] = { 0, 1, 2, 8, 9, 10, 16, 17, 18 }; -static const byte channel_mappings_2[9] = { +static const byte g_operator2Offsets[9] = { 3, 4, 5, 11, 12, 13, 19, 20, 21 }; -static const AdLibSetParams adlib_setparam_table[] = { +static const AdLibSetParams g_setParamTable[] = { {0x40, 0, 63, 63}, // level {0xE0, 2, 0, 0}, // unused {0x40, 6, 192, 0}, // level key scaling @@ -257,21 +278,21 @@ static const AdLibSetParams adlib_setparam_table[] = { {0xC0, 1, 14, 0} // feedback }; -static const byte param_table_1[16] = { +static const byte g_paramTable1[16] = { 29, 28, 27, 0, 3, 4, 7, 8, 13, 16, 17, 20, 21, 30, 31, 0 }; -static const uint16 maxval_table[16] = { +static const uint16 g_maxValTable[16] = { 0x2FF, 0x1F, 0x7, 0x3F, 0x0F, 0x0F, 0x0F, 0x3, 0x3F, 0x0F, 0x0F, 0x0F, 0x3, 0x3E, 0x1F, 0 }; -static const uint16 num_steps_table[] = { +static const uint16 g_numStepsTable[] = { 1, 2, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, @@ -282,7 +303,7 @@ static const uint16 num_steps_table[] = { 600, 860, 1200, 1600 }; -static const byte note_to_f_num[] = { +static const byte g_noteFrequencies[] = { 90, 91, 92, 92, 93, 94, 94, 95, 96, 96, 97, 98, 98, 99, 100, 101, 101, 102, 103, 104, 104, 105, 106, 107, @@ -303,188 +324,530 @@ static const byte note_to_f_num[] = { 242, 243, 245, 247, 249, 251, 252, 254 }; -static const AdLibInstrument map_gm_to_fm[128] = { +static const AdLibInstrument g_gmInstruments[128] = { // 0x00 -{ 0xC2, 0xC5, 0x2B, 0x99, 0x58, 0xC2, 0x1F, 0x1E, 0xC8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x23 }, -{ 0x22, 0x53, 0x0E, 0x8A, 0x30, 0x14, 0x06, 0x1D, 0x7A, 0x5C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x06, 0x00, 0x1C, 0x79, 0x40, 0x02, 0x00, 0x4B, 0x79, 0x58, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC2, 0x89, 0x2A, 0x89, 0x49, 0xC2, 0x16, 0x1C, 0xB8, 0x7C, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x23 }, -{ 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x20, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x84, 0x40, 0x3B, 0x5A, 0x6F, 0x81, 0x0E, 0x3B, 0x5A, 0x7F, 0x0B, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x8C, 0x80, 0x05, 0xEA, 0x59, 0x82, 0x0A, 0x3C, 0xAA, 0x64, 0x07, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x85, 0x40, 0x0D, 0xEC, 0x71, 0x84, 0x58, 0x3E, 0xCB, 0x7C, 0x01, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x8A, 0xC0, 0x0C, 0xDC, 0x50, 0x88, 0x58, 0x3D, 0xDA, 0x7C, 0x01, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC9, 0x40, 0x2B, 0x78, 0x42, 0xC2, 0x04, 0x4C, 0x8A, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x1A }, -{ 0x2A, 0x0E, 0x17, 0x89, 0x28, 0x22, 0x0C, 0x1B, 0x09, 0x70, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE7, 0x9B, 0x08, 0x08, 0x26, 0xE2, 0x06, 0x0A, 0x08, 0x70, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC5, 0x05, 0x00, 0xFC, 0x40, 0x84, 0x00, 0x00, 0xDC, 0x50, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x86, 0x40, 0x5D, 0x5A, 0x41, 0x81, 0x00, 0x0B, 0x5A, 0x7F, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, + { 0xC2, 0xC5, 0x2B, 0x99, 0x58, 0xC2, 0x1F, 0x1E, 0xC8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0x22, 0x53, 0x0E, 0x8A, 0x30, 0x14, 0x06, 0x1D, 0x7A, 0x5C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x00, 0x1C, 0x79, 0x40, 0x02, 0x00, 0x4B, 0x79, 0x58, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x89, 0x2A, 0x89, 0x49, 0xC2, 0x16, 0x1C, 0xB8, 0x7C, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x20, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x3B, 0x5A, 0x6F, 0x81, 0x0E, 0x3B, 0x5A, 0x7F, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8C, 0x80, 0x05, 0xEA, 0x59, 0x82, 0x0A, 0x3C, 0xAA, 0x64, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x85, 0x40, 0x0D, 0xEC, 0x71, 0x84, 0x58, 0x3E, 0xCB, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8A, 0xC0, 0x0C, 0xDC, 0x50, 0x88, 0x58, 0x3D, 0xDA, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC9, 0x40, 0x2B, 0x78, 0x42, 0xC2, 0x04, 0x4C, 0x8A, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A }, + { 0x2A, 0x0E, 0x17, 0x89, 0x28, 0x22, 0x0C, 0x1B, 0x09, 0x70, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x9B, 0x08, 0x08, 0x26, 0xE2, 0x06, 0x0A, 0x08, 0x70, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC5, 0x05, 0x00, 0xFC, 0x40, 0x84, 0x00, 0x00, 0xDC, 0x50, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x86, 0x40, 0x5D, 0x5A, 0x41, 0x81, 0x00, 0x0B, 0x5A, 0x7F, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, // 0x10 -{ 0xED, 0x00, 0x7B, 0xC8, 0x40, 0xE1, 0x99, 0x4A, 0xE9, 0x7E, 0x07, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE8, 0x4F, 0x3A, 0xD7, 0x7C, 0xE2, 0x97, 0x49, 0xF9, 0x7D, 0x05, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x10, 0x2F, 0xF7, 0x7D, 0xF3, 0x45, 0x8F, 0xC7, 0x62, 0x07, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x01, 0x8C, 0x9F, 0xDA, 0x70, 0xE4, 0x50, 0x9F, 0xDA, 0x6A, 0x09, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x08, 0xD5, 0x9D, 0xA5, 0x45, 0xE2, 0x3F, 0x9F, 0xD6, 0x49, 0x07, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE5, 0x0F, 0x7D, 0xB8, 0x2E, 0xA2, 0x0F, 0x7C, 0xC7, 0x61, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0x62, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x88, 0x9C, 0x50, 0x64, 0xE2, 0x18, 0x70, 0xC4, 0x7C, 0x0B, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x18 }, -{ 0x42, 0x55, 0x3E, 0xEB, 0x24, 0xD4, 0x08, 0x0D, 0xA9, 0x71, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x18 }, -{ 0xC2, 0x00, 0x2B, 0x17, 0x51, 0xC2, 0x1E, 0x4D, 0x97, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x19 }, -{ 0xC6, 0x01, 0x2D, 0xA7, 0x44, 0xC2, 0x06, 0x0E, 0xA7, 0x79, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC2, 0x0C, 0x06, 0x06, 0x55, 0xC2, 0x3F, 0x09, 0x86, 0x7D, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0A }, -{ 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0x59, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0x7F, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0x7D, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0B }, + { 0xED, 0x00, 0x7B, 0xC8, 0x40, 0xE1, 0x99, 0x4A, 0xE9, 0x7E, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x4F, 0x3A, 0xD7, 0x7C, 0xE2, 0x97, 0x49, 0xF9, 0x7D, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x10, 0x2F, 0xF7, 0x7D, 0xF3, 0x45, 0x8F, 0xC7, 0x62, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x01, 0x8C, 0x9F, 0xDA, 0x70, 0xE4, 0x50, 0x9F, 0xDA, 0x6A, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x08, 0xD5, 0x9D, 0xA5, 0x45, 0xE2, 0x3F, 0x9F, 0xD6, 0x49, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE5, 0x0F, 0x7D, 0xB8, 0x2E, 0xA2, 0x0F, 0x7C, 0xC7, 0x61, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0x62, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x88, 0x9C, 0x50, 0x64, 0xE2, 0x18, 0x70, 0xC4, 0x7C, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0x42, 0x55, 0x3E, 0xEB, 0x24, 0xD4, 0x08, 0x0D, 0xA9, 0x71, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0xC2, 0x00, 0x2B, 0x17, 0x51, 0xC2, 0x1E, 0x4D, 0x97, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 }, + { 0xC6, 0x01, 0x2D, 0xA7, 0x44, 0xC2, 0x06, 0x0E, 0xA7, 0x79, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x0C, 0x06, 0x06, 0x55, 0xC2, 0x3F, 0x09, 0x86, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A }, + { 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0x59, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0x7F, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0x7D, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B }, // 0x20 -{ 0xC2, 0x40, 0x3C, 0x96, 0x58, 0xC4, 0xDE, 0x0E, 0xC7, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x20 }, -{ 0x31, 0x13, 0x2D, 0xD7, 0x3C, 0xE2, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x22, 0x86, 0x0D, 0xD7, 0x50, 0xE4, 0x18, 0x5E, 0xB8, 0x7C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x28 }, -{ 0xF2, 0x0A, 0x0D, 0xD7, 0x40, 0xE4, 0x1F, 0x5E, 0xB8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x09, 0x4B, 0xD6, 0x48, 0xE4, 0x1F, 0x1C, 0xB8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x28 }, -{ 0x62, 0x11, 0x0C, 0xE6, 0x3C, 0xE4, 0x1F, 0x0C, 0xC8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x12, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x7D, 0xB8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x13, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x5D, 0xB8, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xA2, 0x40, 0x5D, 0xBA, 0x3F, 0xE2, 0x00, 0x8F, 0xD8, 0x79, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x40, 0x3D, 0xDA, 0x3B, 0xE1, 0x00, 0x7E, 0xD8, 0x7A, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x62, 0x00, 0x6D, 0xFA, 0x5D, 0xE2, 0x00, 0x8F, 0xC8, 0x79, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x00, 0x4E, 0xDB, 0x4A, 0xE3, 0x18, 0x6F, 0xE9, 0x7E, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x00, 0x4E, 0xDB, 0x66, 0xE2, 0x00, 0x7F, 0xE9, 0x7E, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x02, 0x0F, 0x66, 0xAA, 0x51, 0x02, 0x64, 0x29, 0xF9, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0x16, 0x4A, 0x04, 0xBA, 0x39, 0xC2, 0x58, 0x2D, 0xCA, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0x02, 0x00, 0x01, 0x7A, 0x79, 0x02, 0x3F, 0x28, 0xEA, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, + { 0xC2, 0x40, 0x3C, 0x96, 0x58, 0xC4, 0xDE, 0x0E, 0xC7, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 }, + { 0x31, 0x13, 0x2D, 0xD7, 0x3C, 0xE2, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x22, 0x86, 0x0D, 0xD7, 0x50, 0xE4, 0x18, 0x5E, 0xB8, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0xF2, 0x0A, 0x0D, 0xD7, 0x40, 0xE4, 0x1F, 0x5E, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x09, 0x4B, 0xD6, 0x48, 0xE4, 0x1F, 0x1C, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0x62, 0x11, 0x0C, 0xE6, 0x3C, 0xE4, 0x1F, 0x0C, 0xC8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x12, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x7D, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x13, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x5D, 0xB8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xA2, 0x40, 0x5D, 0xBA, 0x3F, 0xE2, 0x00, 0x8F, 0xD8, 0x79, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x3D, 0xDA, 0x3B, 0xE1, 0x00, 0x7E, 0xD8, 0x7A, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x6D, 0xFA, 0x5D, 0xE2, 0x00, 0x8F, 0xC8, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0x4A, 0xE3, 0x18, 0x6F, 0xE9, 0x7E, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0x66, 0xE2, 0x00, 0x7F, 0xE9, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x0F, 0x66, 0xAA, 0x51, 0x02, 0x64, 0x29, 0xF9, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x16, 0x4A, 0x04, 0xBA, 0x39, 0xC2, 0x58, 0x2D, 0xCA, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x02, 0x00, 0x01, 0x7A, 0x79, 0x02, 0x3F, 0x28, 0xEA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, // 0x30 -{ 0x62, 0x53, 0x9C, 0xBA, 0x31, 0x62, 0x5B, 0xAD, 0xC9, 0x55, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x40, 0x6E, 0xDA, 0x49, 0xE2, 0x13, 0x8F, 0xF9, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x40, 0x8F, 0xFA, 0x50, 0xF2, 0x04, 0x7F, 0xFA, 0x7D, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x3D, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE6, 0x80, 0x9C, 0x99, 0x42, 0xE2, 0x04, 0x7D, 0x78, 0x60, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0x7C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC3, 0x3F, 0x4B, 0xE9, 0x7E, 0xC1, 0x3F, 0x9B, 0xF9, 0x7F, 0x0B, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x06 }, -{ 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0x68, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x00, 0x8F, 0xFB, 0x50, 0xF6, 0x47, 0x8F, 0xE9, 0x68, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x00, 0xAF, 0x88, 0x58, 0xF2, 0x54, 0x6E, 0xC9, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x84, 0x4E, 0x78, 0x6C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0x7C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0x7D, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0x7C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, + { 0x62, 0x53, 0x9C, 0xBA, 0x31, 0x62, 0x5B, 0xAD, 0xC9, 0x55, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0x6E, 0xDA, 0x49, 0xE2, 0x13, 0x8F, 0xF9, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x8F, 0xFA, 0x50, 0xF2, 0x04, 0x7F, 0xFA, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x3D, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x80, 0x9C, 0x99, 0x42, 0xE2, 0x04, 0x7D, 0x78, 0x60, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC3, 0x3F, 0x4B, 0xE9, 0x7E, 0xC1, 0x3F, 0x9B, 0xF9, 0x7F, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0x68, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x00, 0x8F, 0xFB, 0x50, 0xF6, 0x47, 0x8F, 0xE9, 0x68, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x00, 0xAF, 0x88, 0x58, 0xF2, 0x54, 0x6E, 0xC9, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x84, 0x4E, 0x78, 0x6C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0x7D, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, // 0x40 -{ 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0x7D, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x0F, 0x90, 0xF8, 0x78, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x62, 0x00, 0x8E, 0xC9, 0x3D, 0xE6, 0x00, 0x7E, 0xD8, 0x68, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x00, 0x5F, 0xF9, 0x48, 0xE6, 0x98, 0x8F, 0xF8, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x62, 0x0C, 0x6E, 0xD8, 0x3D, 0x2A, 0x06, 0x7D, 0xD8, 0x58, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x00, 0x7E, 0x89, 0x38, 0xE6, 0x84, 0x80, 0xF8, 0x68, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x80, 0x6C, 0xD9, 0x30, 0xE2, 0x00, 0x8D, 0xC8, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x80, 0x88, 0x48, 0x40, 0xE2, 0x0A, 0x7D, 0xA8, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x00, 0x77, 0xC5, 0x54, 0xE2, 0x00, 0x9E, 0xD7, 0x70, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x80, 0x86, 0xB9, 0x64, 0xE2, 0x05, 0x9F, 0xD7, 0x78, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x00, 0x68, 0x68, 0x56, 0xE2, 0x08, 0x9B, 0xB3, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x00, 0xA6, 0x87, 0x41, 0xE2, 0x0A, 0x7E, 0xC9, 0x7C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x80, 0x9A, 0xB8, 0x48, 0xE2, 0x00, 0x9E, 0xF9, 0x60, 0x09, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x80, 0x8E, 0x64, 0x68, 0xE2, 0x28, 0x6F, 0x73, 0x7C, 0x01, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, + { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0x7D, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x0F, 0x90, 0xF8, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x8E, 0xC9, 0x3D, 0xE6, 0x00, 0x7E, 0xD8, 0x68, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x5F, 0xF9, 0x48, 0xE6, 0x98, 0x8F, 0xF8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x0C, 0x6E, 0xD8, 0x3D, 0x2A, 0x06, 0x7D, 0xD8, 0x58, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x7E, 0x89, 0x38, 0xE6, 0x84, 0x80, 0xF8, 0x68, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x6C, 0xD9, 0x30, 0xE2, 0x00, 0x8D, 0xC8, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x88, 0x48, 0x40, 0xE2, 0x0A, 0x7D, 0xA8, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x77, 0xC5, 0x54, 0xE2, 0x00, 0x9E, 0xD7, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x86, 0xB9, 0x64, 0xE2, 0x05, 0x9F, 0xD7, 0x78, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x68, 0x68, 0x56, 0xE2, 0x08, 0x9B, 0xB3, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0xA6, 0x87, 0x41, 0xE2, 0x0A, 0x7E, 0xC9, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x9A, 0xB8, 0x48, 0xE2, 0x00, 0x9E, 0xF9, 0x60, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x8E, 0x64, 0x68, 0xE2, 0x28, 0x6F, 0x73, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, // 0x50 -{ 0xE8, 0x00, 0x7D, 0x99, 0x54, 0xE6, 0x80, 0x80, 0xF8, 0x7C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE6, 0x00, 0x9F, 0xB9, 0x6D, 0xE1, 0x00, 0x8F, 0xC8, 0x7D, 0x02, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x00, 0x09, 0x68, 0x4A, 0xE2, 0x2B, 0x9E, 0xF3, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC4, 0x00, 0x99, 0xE8, 0x3B, 0xE2, 0x25, 0x6F, 0x93, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE6, 0x00, 0x6F, 0xDA, 0x69, 0xE2, 0x05, 0x2F, 0xD8, 0x6A, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0x7C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE3, 0x00, 0x0F, 0xF7, 0x7D, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x3C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE8, 0x40, 0x0D, 0x89, 0x7D, 0xE2, 0x17, 0x7E, 0xD9, 0x7C, 0x07, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x00, 0xDF, 0x8A, 0x56, 0xE2, 0x5E, 0xCF, 0xBA, 0x7E, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE2, 0x00, 0x0B, 0x68, 0x60, 0xE2, 0x01, 0x9E, 0xB8, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xEA, 0x00, 0xAE, 0xAB, 0x49, 0xE2, 0x00, 0xAE, 0xBA, 0x6C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xEB, 0x80, 0x8C, 0xCB, 0x3A, 0xE2, 0x86, 0xAF, 0xCA, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE5, 0x40, 0xDB, 0x3B, 0x3C, 0xE2, 0x80, 0xBE, 0xCA, 0x71, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE4, 0x00, 0x9E, 0xAA, 0x3D, 0xE1, 0x43, 0x0F, 0xBA, 0x7E, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE7, 0x40, 0xEC, 0xCA, 0x44, 0xE2, 0x03, 0xBF, 0xBA, 0x66, 0x02, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, + { 0xE8, 0x00, 0x7D, 0x99, 0x54, 0xE6, 0x80, 0x80, 0xF8, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x9F, 0xB9, 0x6D, 0xE1, 0x00, 0x8F, 0xC8, 0x7D, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x09, 0x68, 0x4A, 0xE2, 0x2B, 0x9E, 0xF3, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x99, 0xE8, 0x3B, 0xE2, 0x25, 0x6F, 0x93, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x6F, 0xDA, 0x69, 0xE2, 0x05, 0x2F, 0xD8, 0x6A, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x00, 0x0F, 0xF7, 0x7D, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x3C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x40, 0x0D, 0x89, 0x7D, 0xE2, 0x17, 0x7E, 0xD9, 0x7C, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0xDF, 0x8A, 0x56, 0xE2, 0x5E, 0xCF, 0xBA, 0x7E, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x0B, 0x68, 0x60, 0xE2, 0x01, 0x9E, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0x00, 0xAE, 0xAB, 0x49, 0xE2, 0x00, 0xAE, 0xBA, 0x6C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEB, 0x80, 0x8C, 0xCB, 0x3A, 0xE2, 0x86, 0xAF, 0xCA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE5, 0x40, 0xDB, 0x3B, 0x3C, 0xE2, 0x80, 0xBE, 0xCA, 0x71, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x9E, 0xAA, 0x3D, 0xE1, 0x43, 0x0F, 0xBA, 0x7E, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x40, 0xEC, 0xCA, 0x44, 0xE2, 0x03, 0xBF, 0xBA, 0x66, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, // 0x60 -{ 0xEA, 0x00, 0x68, 0xB8, 0x48, 0xE2, 0x0A, 0x8E, 0xB8, 0x7C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x61, 0x00, 0xBE, 0x99, 0x7E, 0xE3, 0x40, 0xCF, 0xCA, 0x7D, 0x09, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xCD, 0x00, 0x0B, 0x00, 0x48, 0xC2, 0x58, 0x0C, 0x00, 0x7C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x1C }, -{ 0xE2, 0x00, 0x0E, 0x00, 0x52, 0xE2, 0x58, 0x5F, 0xD0, 0x7D, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xCC, 0x00, 0x7D, 0xDA, 0x40, 0xC2, 0x00, 0x5E, 0x9B, 0x58, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE9, 0xC0, 0xEE, 0xD8, 0x43, 0xE2, 0x05, 0xDD, 0xAA, 0x70, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xDA, 0x00, 0x8F, 0xAC, 0x4A, 0x22, 0x05, 0x8D, 0x8A, 0x75, 0x02, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x62, 0x8A, 0xCB, 0x7A, 0x74, 0xE6, 0x56, 0xAF, 0xDB, 0x70, 0x02, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xC2, 0x41, 0xAC, 0x5B, 0x5B, 0xC2, 0x80, 0x0D, 0xCB, 0x7D, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x12 }, -{ 0x75, 0x00, 0x0E, 0xCB, 0x5A, 0xE2, 0x1E, 0x0A, 0xC9, 0x7D, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x10 }, -{ 0x41, 0x00, 0x0E, 0xEA, 0x53, 0xC2, 0x00, 0x08, 0xCA, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x07 }, -{ 0xC1, 0x40, 0x0C, 0x59, 0x6A, 0xC2, 0x80, 0x3C, 0xAB, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0D }, -{ 0x4B, 0x00, 0x0A, 0xF5, 0x61, 0xC2, 0x19, 0x0C, 0xE9, 0x7C, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x07 }, -{ 0x62, 0x00, 0x7F, 0xD8, 0x54, 0xEA, 0x00, 0x8F, 0xD8, 0x7D, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, + { 0xEA, 0x00, 0x68, 0xB8, 0x48, 0xE2, 0x0A, 0x8E, 0xB8, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x61, 0x00, 0xBE, 0x99, 0x7E, 0xE3, 0x40, 0xCF, 0xCA, 0x7D, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xCD, 0x00, 0x0B, 0x00, 0x48, 0xC2, 0x58, 0x0C, 0x00, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C }, + { 0xE2, 0x00, 0x0E, 0x00, 0x52, 0xE2, 0x58, 0x5F, 0xD0, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xCC, 0x00, 0x7D, 0xDA, 0x40, 0xC2, 0x00, 0x5E, 0x9B, 0x58, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE9, 0xC0, 0xEE, 0xD8, 0x43, 0xE2, 0x05, 0xDD, 0xAA, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xDA, 0x00, 0x8F, 0xAC, 0x4A, 0x22, 0x05, 0x8D, 0x8A, 0x75, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x8A, 0xCB, 0x7A, 0x74, 0xE6, 0x56, 0xAF, 0xDB, 0x70, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x41, 0xAC, 0x5B, 0x5B, 0xC2, 0x80, 0x0D, 0xCB, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 }, + { 0x75, 0x00, 0x0E, 0xCB, 0x5A, 0xE2, 0x1E, 0x0A, 0xC9, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x41, 0x00, 0x0E, 0xEA, 0x53, 0xC2, 0x00, 0x08, 0xCA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0xC1, 0x40, 0x0C, 0x59, 0x6A, 0xC2, 0x80, 0x3C, 0xAB, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0x4B, 0x00, 0x0A, 0xF5, 0x61, 0xC2, 0x19, 0x0C, 0xE9, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x62, 0x00, 0x7F, 0xD8, 0x54, 0xEA, 0x00, 0x8F, 0xD8, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, // 0x70 -{ 0xCF, 0x40, 0x09, 0xEA, 0x54, 0xC4, 0x00, 0x0C, 0xDB, 0x64, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xCF, 0x40, 0x0C, 0xAA, 0x54, 0xC4, 0x00, 0x18, 0xF9, 0x64, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xC9, 0x0E, 0x88, 0xD9, 0x3E, 0xC2, 0x08, 0x1A, 0xEA, 0x6C, 0x0C, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x03, 0x00, 0x15, 0x00, 0x64, 0x02, 0x00, 0x08, 0x00, 0x7C, 0x09, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x01, 0x00, 0x47, 0xD7, 0x6C, 0x01, 0x3F, 0x0C, 0xFB, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0x00, 0x00, 0x36, 0x67, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x02, 0x00, 0x36, 0x68, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0xCB, 0x00, 0xAF, 0x00, 0x7E, 0xC0, 0x00, 0xC0, 0x06, 0x7F, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0F }, -{ 0x05, 0x0D, 0x80, 0xA6, 0x7F, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0x0F, 0x00, 0x90, 0xFA, 0x68, 0x06, 0x00, 0xA7, 0x39, 0x54, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x06 }, -{ 0xC9, 0x15, 0xDD, 0xFF, 0x7C, 0x00, 0x00, 0xE7, 0xFC, 0x6C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x38 }, -{ 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0x07, 0x80, 0x0B, 0xC8, 0x65, 0x02, 0x3F, 0x0C, 0xEA, 0x7C, 0x0F, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x08, 0x00, 0x0B, 0x3C, 0x7C, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 } + { 0xCF, 0x40, 0x09, 0xEA, 0x54, 0xC4, 0x00, 0x0C, 0xDB, 0x64, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x40, 0x0C, 0xAA, 0x54, 0xC4, 0x00, 0x18, 0xF9, 0x64, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xC9, 0x0E, 0x88, 0xD9, 0x3E, 0xC2, 0x08, 0x1A, 0xEA, 0x6C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x03, 0x00, 0x15, 0x00, 0x64, 0x02, 0x00, 0x08, 0x00, 0x7C, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x01, 0x00, 0x47, 0xD7, 0x6C, 0x01, 0x3F, 0x0C, 0xFB, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x00, 0x00, 0x36, 0x67, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x02, 0x00, 0x36, 0x68, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0xCB, 0x00, 0xAF, 0x00, 0x7E, 0xC0, 0x00, 0xC0, 0x06, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F }, + { 0x05, 0x0D, 0x80, 0xA6, 0x7F, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x0F, 0x00, 0x90, 0xFA, 0x68, 0x06, 0x00, 0xA7, 0x39, 0x54, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xC9, 0x15, 0xDD, 0xFF, 0x7C, 0x00, 0x00, 0xE7, 0xFC, 0x6C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 }, + { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x07, 0x80, 0x0B, 0xC8, 0x65, 0x02, 0x3F, 0x0C, 0xEA, 0x7C, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x08, 0x00, 0x0B, 0x3C, 0x7C, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }; -static AdLibInstrument gm_percussion_to_fm[39] = { -{ 0x1A, 0x3F, 0x15, 0x05, 0x7C, 0x02, 0x21, 0x2B, 0xE4, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x06 }, -{ 0x11, 0x12, 0x04, 0x07, 0x7C, 0x02, 0x23, 0x0B, 0xE5, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x0A, 0x3F, 0x0B, 0x01, 0x7C, 0x1F, 0x1C, 0x46, 0xD0, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x01 }, -{ 0x00, 0x3F, 0x0F, 0x00, 0x7C, 0x10, 0x12, 0x07, 0x00, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x0F, 0x3F, 0x0B, 0x00, 0x7C, 0x1F, 0x0F, 0x19, 0xD0, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x00, 0x3F, 0x1F, 0x00, 0x7E, 0x1F, 0x16, 0x07, 0x00, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x1F, 0x4A, 0xD9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x7F, 0x08, 0xFF, 0x7E, 0x00, 0xC7, 0x2D, 0xF7, 0x73, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x43, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x7F, 0x08, 0xCF, 0x7E, 0x00, 0x45, 0x2A, 0xF8, 0x4B, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0C }, -{ 0x12, 0x3F, 0x06, 0x17, 0x7C, 0x03, 0x27, 0x0B, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x7F, 0x08, 0xCD, 0x7E, 0x00, 0x40, 0x1A, 0x69, 0x63, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0C }, -{ 0x13, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x17, 0x0A, 0xD9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0x15, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x3F, 0x2B, 0xFB, 0x7E, 0xC0, 0x1E, 0x1A, 0xCA, 0x7F, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x10 }, -{ 0x17, 0x3F, 0x04, 0x09, 0x7C, 0x03, 0x22, 0x0D, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x3F, 0x0F, 0x5E, 0x7C, 0xC6, 0x13, 0x00, 0xCA, 0x7F, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0xCF, 0x3F, 0x7E, 0x9D, 0x7C, 0xC8, 0xC0, 0x0A, 0xBA, 0x74, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x06 }, -{ 0xCF, 0x3F, 0x4D, 0x9F, 0x7C, 0xC6, 0x00, 0x08, 0xDA, 0x5B, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0xCF, 0x3F, 0x5D, 0xAA, 0x7A, 0xC0, 0xA4, 0x67, 0x99, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xCF, 0x3F, 0x4A, 0xFD, 0x7C, 0xCF, 0x00, 0x59, 0xEA, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x0F, 0x18, 0x0A, 0xFA, 0x57, 0x06, 0x07, 0x06, 0x39, 0x7C, 0x0A, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xCF, 0x3F, 0x2B, 0xFC, 0x7C, 0xCC, 0xC6, 0x0B, 0xEA, 0x7F, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x10 }, -{ 0x05, 0x1A, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x0C, 0xEA, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x07 }, -{ 0x04, 0x19, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x2C, 0xEA, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x04 }, -{ 0x04, 0x0A, 0x04, 0x00, 0x6C, 0x01, 0x07, 0x0D, 0xFA, 0x74, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x07 }, -{ 0x15, 0x14, 0x05, 0x00, 0x7D, 0x01, 0x07, 0x5C, 0xE9, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x10, 0x10, 0x05, 0x08, 0x7C, 0x01, 0x08, 0x0D, 0xEA, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x05 }, -{ 0x11, 0x00, 0x06, 0x87, 0x7F, 0x02, 0x40, 0x09, 0x59, 0x68, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x08 }, -{ 0x13, 0x26, 0x04, 0x6A, 0x7F, 0x01, 0x00, 0x08, 0x5A, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x08 }, -{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC4, 0x00, 0x18, 0xF9, 0x54, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC3, 0x00, 0x18, 0xF8, 0x54, 0x04, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xCB, 0x3F, 0x8F, 0x00, 0x7E, 0xC5, 0x00, 0x98, 0xD6, 0x5F, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x0D }, -{ 0x0C, 0x18, 0x87, 0xB3, 0x7F, 0x19, 0x10, 0x55, 0x75, 0x7C, 0x0E, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x05, 0x11, 0x15, 0x00, 0x64, 0x02, 0x08, 0x08, 0x00, 0x5C, 0x09, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0x04, 0x08, 0x15, 0x00, 0x48, 0x01, 0x08, 0x08, 0x00, 0x60, 0x08, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x02 }, -{ 0xDA, 0x00, 0x53, 0x30, 0x68, 0x07, 0x1E, 0x49, 0xC4, 0x7E, 0x03, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 }, -{ 0x1C, 0x00, 0x07, 0xBC, 0x6C, 0x0C, 0x14, 0x0B, 0x6A, 0x7E, 0x0B, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x03 }, -{ 0x0A, 0x0E, 0x7F, 0x00, 0x7D, 0x13, 0x20, 0x28, 0x03, 0x7C, 0x06, 0, { 0,0,0,0,0,0,0,0 }, 0, { 0,0,0,0,0,0,0,0 }, 0x00 } +static AdLibInstrument g_gmPercussionInstruments[39] = { + { 0x1A, 0x3F, 0x15, 0x05, 0x7C, 0x02, 0x21, 0x2B, 0xE4, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0x11, 0x12, 0x04, 0x07, 0x7C, 0x02, 0x23, 0x0B, 0xE5, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x0A, 0x3F, 0x0B, 0x01, 0x7C, 0x1F, 0x1C, 0x46, 0xD0, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 }, + { 0x00, 0x3F, 0x0F, 0x00, 0x7C, 0x10, 0x12, 0x07, 0x00, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0F, 0x3F, 0x0B, 0x00, 0x7C, 0x1F, 0x0F, 0x19, 0xD0, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x00, 0x3F, 0x1F, 0x00, 0x7E, 0x1F, 0x16, 0x07, 0x00, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x1F, 0x4A, 0xD9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x7F, 0x08, 0xFF, 0x7E, 0x00, 0xC7, 0x2D, 0xF7, 0x73, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x43, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x7F, 0x08, 0xCF, 0x7E, 0x00, 0x45, 0x2A, 0xF8, 0x4B, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0x12, 0x3F, 0x06, 0x17, 0x7C, 0x03, 0x27, 0x0B, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x7F, 0x08, 0xCD, 0x7E, 0x00, 0x40, 0x1A, 0x69, 0x63, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0x13, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x17, 0x0A, 0xD9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x15, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x2B, 0xFB, 0x7E, 0xC0, 0x1E, 0x1A, 0xCA, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x17, 0x3F, 0x04, 0x09, 0x7C, 0x03, 0x22, 0x0D, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x0F, 0x5E, 0x7C, 0xC6, 0x13, 0x00, 0xCA, 0x7F, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x7E, 0x9D, 0x7C, 0xC8, 0xC0, 0x0A, 0xBA, 0x74, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xCF, 0x3F, 0x4D, 0x9F, 0x7C, 0xC6, 0x00, 0x08, 0xDA, 0x5B, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0xCF, 0x3F, 0x5D, 0xAA, 0x7A, 0xC0, 0xA4, 0x67, 0x99, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x4A, 0xFD, 0x7C, 0xCF, 0x00, 0x59, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0F, 0x18, 0x0A, 0xFA, 0x57, 0x06, 0x07, 0x06, 0x39, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x2B, 0xFC, 0x7C, 0xCC, 0xC6, 0x0B, 0xEA, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x05, 0x1A, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x0C, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x04, 0x19, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x2C, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x04, 0x0A, 0x04, 0x00, 0x6C, 0x01, 0x07, 0x0D, 0xFA, 0x74, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x15, 0x14, 0x05, 0x00, 0x7D, 0x01, 0x07, 0x5C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x10, 0x10, 0x05, 0x08, 0x7C, 0x01, 0x08, 0x0D, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x11, 0x00, 0x06, 0x87, 0x7F, 0x02, 0x40, 0x09, 0x59, 0x68, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0x13, 0x26, 0x04, 0x6A, 0x7F, 0x01, 0x00, 0x08, 0x5A, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC4, 0x00, 0x18, 0xF9, 0x54, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC3, 0x00, 0x18, 0xF8, 0x54, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCB, 0x3F, 0x8F, 0x00, 0x7E, 0xC5, 0x00, 0x98, 0xD6, 0x5F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0x0C, 0x18, 0x87, 0xB3, 0x7F, 0x19, 0x10, 0x55, 0x75, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x05, 0x11, 0x15, 0x00, 0x64, 0x02, 0x08, 0x08, 0x00, 0x5C, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x04, 0x08, 0x15, 0x00, 0x48, 0x01, 0x08, 0x08, 0x00, 0x60, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xDA, 0x00, 0x53, 0x30, 0x68, 0x07, 0x1E, 0x49, 0xC4, 0x7E, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x1C, 0x00, 0x07, 0xBC, 0x6C, 0x0C, 0x14, 0x0B, 0x6A, 0x7E, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x0A, 0x0E, 0x7F, 0x00, 0x7D, 0x13, 0x20, 0x28, 0x03, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }; -static const byte gm_percussion_lookup[128] = { +#ifdef ENABLE_OPL3 +static const AdLibInstrument g_gmInstrumentsOPL3[128][2] = { + { { 0xC2, 0xC2, 0x0A, 0x6B, 0xA0, 0xC2, 0x08, 0x0D, 0x88, 0xC8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0x02, 0x00, 0x0C, 0x78, 0x61, 0x04, 0x4C, 0x0B, 0x9A, 0xC8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 } }, + { { 0x22, 0x53, 0x0E, 0x8A, 0x60, 0x14, 0x06, 0x1D, 0x7A, 0xB8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x22, 0x5A, 0x0E, 0x8A, 0x40, 0x14, 0x2F, 0x0E, 0x7A, 0x88, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x06, 0x00, 0x1C, 0x79, 0x70, 0x02, 0x00, 0x4B, 0x79, 0xA8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x00, 0x1A, 0x79, 0x60, 0x02, 0x00, 0x4C, 0xA9, 0xC8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x80, 0x0B, 0x89, 0x90, 0xC2, 0x06, 0x1B, 0xA8, 0xB0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0x04, 0x28, 0x5D, 0xB8, 0x01, 0x02, 0x00, 0x3C, 0x70, 0x88, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x40, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x40, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0xD3, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x87, 0x40, 0x3A, 0x5A, 0x94, 0x82, 0x04, 0x3D, 0x59, 0xAC, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x84, 0x40, 0x3B, 0x5A, 0xC3, 0x81, 0x00, 0x3B, 0x5A, 0xFB, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x3B, 0x5A, 0xC3, 0x81, 0x00, 0x3B, 0x5A, 0xFB, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x8C, 0x80, 0x05, 0xEA, 0xA9, 0x82, 0x04, 0x3D, 0xAA, 0xB0, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8C, 0x80, 0x06, 0x98, 0xA9, 0x86, 0x10, 0x36, 0x7A, 0xFD, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x85, 0x40, 0x0D, 0xEC, 0xE1, 0x84, 0x58, 0x3E, 0xCB, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x0D, 0xEB, 0xE0, 0x84, 0x48, 0x3E, 0xCA, 0xC0, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x8A, 0xC0, 0x0C, 0xDC, 0xA0, 0x88, 0x58, 0x3D, 0xDA, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8A, 0xC0, 0x0C, 0xDC, 0xA0, 0x88, 0x58, 0x3D, 0xDA, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC9, 0x40, 0x2B, 0x78, 0x8A, 0xC2, 0x0A, 0x4C, 0x8A, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A }, + { 0xCA, 0x40, 0x47, 0xCA, 0xB4, 0xC2, 0x00, 0x57, 0x8A, 0xB8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A } }, + { { 0x2A, 0x0E, 0x17, 0x89, 0x50, 0x22, 0x0C, 0x1B, 0x09, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x2A, 0x1A, 0x19, 0x8A, 0x00, 0x22, 0x38, 0x0B, 0x0A, 0x00, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE7, 0x9B, 0x08, 0x08, 0x4A, 0xE2, 0x06, 0x0A, 0x08, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x9B, 0x08, 0x08, 0x4A, 0xE2, 0x2F, 0x0A, 0x08, 0x68, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC5, 0x0A, 0x05, 0xDC, 0xB8, 0x84, 0x06, 0x00, 0xEC, 0xC0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x09, 0x10, 0x04, 0x5B, 0xA5, 0x02, 0x08, 0x00, 0xEC, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x86, 0x40, 0x5D, 0x5A, 0x81, 0x81, 0x00, 0x0B, 0x5A, 0xFB, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x86, 0x40, 0x5D, 0x5A, 0x81, 0x81, 0x00, 0x0B, 0x5A, 0xFB, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xED, 0x0F, 0x5B, 0xC8, 0xC8, 0xE2, 0x9F, 0x4A, 0xE9, 0xF9, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x40, 0x0A, 0xA7, 0x64, 0xE2, 0x8B, 0x6A, 0x79, 0xB1, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0x4F, 0x3A, 0xD7, 0xF8, 0xE2, 0x97, 0x49, 0xF9, 0xF9, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC9, 0x02, 0x16, 0x9A, 0xAB, 0xC4, 0x15, 0x46, 0xBA, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x08, 0x2F, 0xF7, 0xE1, 0xF3, 0x42, 0x8F, 0xC7, 0xC2, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x00, 0x2D, 0xF7, 0xC1, 0xE4, 0x40, 0x7F, 0xC7, 0xD2, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x01, 0x8C, 0x9F, 0xDA, 0xE8, 0xE4, 0x50, 0x9F, 0xDA, 0xF2, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x80, 0x9F, 0xDA, 0x00, 0xE3, 0x50, 0x9F, 0xD9, 0xFA, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x08, 0xD5, 0x9D, 0xA5, 0x89, 0xE2, 0x3F, 0x9F, 0xD6, 0x91, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x08, 0xD5, 0x9D, 0xA5, 0x89, 0xE2, 0x3F, 0x9F, 0xD6, 0x91, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE5, 0x0F, 0x7D, 0xB8, 0x5A, 0xA2, 0x0C, 0x7C, 0xC7, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x4C, 0xAC, 0x56, 0x31, 0x02, 0x08, 0x8D, 0x46, 0xDC, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0xC2, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x00, 0x9F, 0xDB, 0xA9, 0xE1, 0x00, 0x8F, 0xD7, 0xBA, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x88, 0x9C, 0x50, 0xC8, 0xE2, 0x18, 0x70, 0xC4, 0xF8, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x9C, 0x50, 0xB0, 0xE4, 0x00, 0x70, 0xC4, 0xA0, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 } }, + { { 0x42, 0x53, 0x3E, 0xEB, 0x48, 0xD4, 0x05, 0x1D, 0xA9, 0xC9, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0x42, 0x54, 0x6F, 0xEB, 0x61, 0xD4, 0x02, 0x2E, 0xA9, 0xC8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 } }, + { { 0xC2, 0x00, 0x59, 0x17, 0xB1, 0xC2, 0x1E, 0x6D, 0x98, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 }, + { 0xC2, 0x00, 0x08, 0xB3, 0x99, 0xC2, 0x06, 0x2B, 0x58, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 } }, + { { 0xC6, 0x01, 0x2D, 0xA7, 0x88, 0xC2, 0x08, 0x0E, 0xA7, 0xC1, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x2D, 0xA7, 0x91, 0xC2, 0x02, 0x0E, 0xA7, 0xD1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x0C, 0x06, 0x06, 0xA9, 0xC2, 0x3F, 0x08, 0xB8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A }, + { 0xC1, 0x00, 0x68, 0x50, 0xB8, 0xC2, 0x00, 0x48, 0x84, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A } }, + { { 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0xB1, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x2F, 0x6F, 0x79, 0x00, 0xC8, 0x0F, 0x5E, 0x98, 0xB9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0xFB, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0xFB, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B }, + { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B } }, + { { 0xC2, 0x41, 0x3D, 0x96, 0x88, 0xC4, 0xCA, 0x0E, 0xC7, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 }, + { 0xC2, 0x04, 0x58, 0xC9, 0x90, 0xC2, 0x94, 0x2C, 0xB9, 0xF0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 } }, + { { 0x31, 0x13, 0x2D, 0xD7, 0x78, 0xE2, 0x18, 0x2E, 0xB8, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x31, 0x13, 0x2D, 0xD7, 0x78, 0xE2, 0x18, 0x2E, 0xB8, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x22, 0x86, 0x0D, 0xD7, 0xA0, 0xE4, 0x18, 0x5E, 0xB8, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0x22, 0x86, 0x0D, 0xD7, 0xA0, 0xE4, 0x18, 0x5E, 0xB8, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 } }, + { { 0xF2, 0x0A, 0x0D, 0xD7, 0x80, 0xE4, 0x1F, 0x5E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x06, 0x9A, 0xD7, 0xA0, 0xC2, 0x1F, 0x59, 0xB8, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x09, 0x4B, 0xD6, 0x90, 0xE4, 0x1F, 0x1C, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0xF2, 0x09, 0x4B, 0xD6, 0x90, 0xE4, 0x1F, 0x1C, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 } }, + { { 0x62, 0x11, 0x0C, 0xE6, 0x78, 0xE4, 0x1F, 0x0C, 0xC8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x11, 0x0C, 0xE6, 0x78, 0xE4, 0x1F, 0x0C, 0xC8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x12, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x7D, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x12, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x7D, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x13, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x5D, 0xB8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x13, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x5D, 0xB8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xA2, 0x40, 0x5D, 0xBA, 0x7B, 0xE2, 0x00, 0x8F, 0xD8, 0xF1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xA2, 0x40, 0x5D, 0xBA, 0x7B, 0xE2, 0x00, 0x8F, 0xD8, 0xF1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x40, 0x3D, 0xDA, 0x73, 0xE1, 0x00, 0x7E, 0xD8, 0xF2, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x3D, 0xDA, 0x73, 0xE1, 0x00, 0x7E, 0xD8, 0xF2, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x00, 0x6D, 0xFA, 0xB9, 0xE2, 0x00, 0x8F, 0xC8, 0xF1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x6D, 0xFA, 0xB9, 0xE2, 0x00, 0x8F, 0xC8, 0xF1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x4E, 0xDB, 0x92, 0xE3, 0x18, 0x6F, 0xE9, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x6F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x7F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x7F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x02, 0x0F, 0x66, 0xAA, 0xA1, 0x02, 0x64, 0x29, 0xF9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x02, 0x00, 0x65, 0xAA, 0xF1, 0x02, 0x4A, 0x28, 0xF9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x16, 0x4A, 0x04, 0xBA, 0x71, 0xC2, 0x48, 0x2E, 0xCA, 0xF0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x14, 0xC0, 0x66, 0x08, 0x90, 0xC2, 0x48, 0x2C, 0x0A, 0xA0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x02, 0x0A, 0x01, 0x7A, 0xB1, 0x02, 0x12, 0x2A, 0xEA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x06, 0x75, 0x05, 0xB1, 0x01, 0x3F, 0x28, 0xEA, 0xF9, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x16 } }, + { { 0x62, 0x53, 0x9C, 0xBA, 0x61, 0x62, 0x5A, 0xAD, 0xCA, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0x9F, 0x8A, 0x98, 0xE2, 0x11, 0x7F, 0xB8, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x40, 0x6E, 0xDA, 0x91, 0xE2, 0x13, 0x8F, 0xF9, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0x6E, 0xDA, 0x91, 0xE2, 0x13, 0x8F, 0xF9, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x40, 0x8F, 0xFA, 0xA0, 0xF2, 0x04, 0x7F, 0xFA, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x8F, 0xFA, 0xA0, 0xF2, 0x04, 0x7F, 0xFA, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE6, 0x80, 0x9C, 0x99, 0x82, 0xE2, 0x04, 0x8D, 0x78, 0xC0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE0, 0x44, 0x8A, 0xA9, 0x5B, 0xE1, 0x06, 0x8D, 0x79, 0xBA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x06, 0x7C, 0x7A, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC3, 0x3F, 0x4B, 0xE9, 0xFA, 0xC1, 0x3F, 0x9B, 0xF9, 0xFB, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xC3, 0x3F, 0x4B, 0xE9, 0xFA, 0xC1, 0x3F, 0x9B, 0xF9, 0xFB, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x02, 0xAF, 0xFB, 0x90, 0xF6, 0x54, 0x8F, 0xE9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x9F, 0xFA, 0xB0, 0xF2, 0x58, 0x7F, 0xEA, 0xF8, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x00, 0xAF, 0x88, 0xA8, 0xF2, 0x46, 0x6E, 0xC9, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x00, 0x7B, 0x88, 0xA8, 0xD2, 0x4C, 0x69, 0xE9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x8F, 0x4E, 0x78, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x02, 0x85, 0x89, 0xC8, 0xD2, 0x94, 0x77, 0x49, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x02, 0x9F, 0xB8, 0x90, 0x22, 0x8A, 0x9F, 0xE8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x00, 0x86, 0xB8, 0x98, 0x02, 0x8F, 0x89, 0xE8, 0xF9, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0xF9, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0xF9, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x1A, 0x90, 0xF8, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x10, 0x69, 0x18, 0xCF, 0xD4, 0x14, 0x5B, 0x04, 0xFD, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x00, 0x8E, 0xC9, 0x79, 0xE6, 0x00, 0x7E, 0xD8, 0xD0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x8E, 0xC9, 0x79, 0xE6, 0x00, 0x7E, 0xD8, 0xD0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x00, 0x5F, 0xF9, 0x88, 0xE4, 0x9E, 0x8F, 0xF8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x00, 0x97, 0xF9, 0x90, 0xC9, 0x80, 0x69, 0x98, 0xA0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x0C, 0x6E, 0xD8, 0x79, 0x2A, 0x09, 0x7D, 0xD8, 0xC0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x04, 0x8A, 0xD8, 0x80, 0x0C, 0x12, 0x85, 0xD8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x7E, 0x89, 0x70, 0xE6, 0x8F, 0x80, 0xF8, 0xF0, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x67, 0x59, 0x70, 0xC6, 0x8A, 0x77, 0xA8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x80, 0x6C, 0xD9, 0x60, 0xE2, 0x00, 0x8D, 0xC8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x6C, 0xD9, 0x60, 0xE2, 0x00, 0x8D, 0xC8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x80, 0x88, 0x48, 0x98, 0xE2, 0x1E, 0x8E, 0xC9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0xA8, 0xB9, 0x80, 0xE2, 0x0C, 0x89, 0x09, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x77, 0xC5, 0xA8, 0xE2, 0x00, 0x9E, 0xD7, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x77, 0xC5, 0xA8, 0xE2, 0x00, 0x9E, 0xD7, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x80, 0x86, 0xB9, 0xA8, 0xE2, 0x14, 0x9F, 0xD7, 0xB0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x80, 0x94, 0x09, 0x78, 0xC2, 0x00, 0x97, 0x97, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x00, 0x68, 0x68, 0xAA, 0xE2, 0x0A, 0x9B, 0xB3, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x00, 0x86, 0x68, 0xA0, 0xC2, 0x00, 0x77, 0x47, 0xE0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0xA6, 0x87, 0x81, 0xE2, 0x0A, 0x7E, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x89, 0x40, 0x79, 0xE2, 0x00, 0x7E, 0xC9, 0x90, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x80, 0xAA, 0xB8, 0x90, 0xE2, 0x00, 0x9E, 0xF9, 0xC0, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x80, 0x9D, 0xB8, 0x51, 0xE2, 0x00, 0x9E, 0xF9, 0xA0, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x80, 0x8E, 0x64, 0xD0, 0xE2, 0x28, 0x6F, 0x73, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x8E, 0x64, 0xD0, 0xE2, 0x28, 0x6F, 0x73, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0x00, 0x7D, 0x99, 0xA8, 0xE6, 0x80, 0x80, 0xF8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x00, 0x7D, 0x99, 0xA8, 0xE6, 0x80, 0x80, 0xF8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE6, 0x00, 0x9F, 0xB9, 0xD9, 0xE1, 0x00, 0x8F, 0xC8, 0xF9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x9F, 0xB9, 0xD9, 0xE1, 0x00, 0x8F, 0xC8, 0xF9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x09, 0x68, 0x92, 0xE2, 0x2B, 0x9E, 0xF3, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x09, 0x68, 0x92, 0xE2, 0x2B, 0x9E, 0xF3, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC4, 0x00, 0x99, 0xE8, 0x73, 0xE2, 0x25, 0x6F, 0x93, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x99, 0xE8, 0x73, 0xE2, 0x25, 0x6F, 0x93, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE6, 0x00, 0x6F, 0xDA, 0xC9, 0xE2, 0x05, 0x2F, 0xD8, 0xAA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x4F, 0xDA, 0xC8, 0xE2, 0x00, 0x0F, 0xD8, 0xD0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE3, 0x00, 0x0F, 0xF7, 0xF9, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x00, 0x0F, 0xF7, 0xF9, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0x40, 0x0D, 0x89, 0xF9, 0xE2, 0x17, 0x7E, 0xD9, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x40, 0x0D, 0x89, 0xF9, 0xE2, 0x17, 0x7E, 0xD9, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0xDF, 0x8A, 0xAA, 0xE2, 0x5E, 0xCF, 0xBA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0xDF, 0x8A, 0xAA, 0xE2, 0x5E, 0xCF, 0xBA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x00, 0x0B, 0x68, 0xC0, 0xE2, 0x01, 0x9E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x0B, 0x68, 0xC0, 0xE2, 0x01, 0x9E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEA, 0x00, 0xAE, 0xAB, 0x91, 0xE2, 0x00, 0xAE, 0xBA, 0xD8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0x00, 0xAE, 0xAB, 0x91, 0xE2, 0x00, 0xAE, 0xBA, 0xD8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEB, 0x80, 0x8C, 0xCB, 0x72, 0xE2, 0x86, 0xAF, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEB, 0xC3, 0x9C, 0xCB, 0xA2, 0xE2, 0x4C, 0xAE, 0xCA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE5, 0x40, 0xDB, 0x3B, 0x78, 0xE2, 0x80, 0xBE, 0xCA, 0xE1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x8E, 0xCB, 0xC0, 0xE2, 0x90, 0xAE, 0xCA, 0xFB, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x9E, 0xAA, 0x79, 0xE1, 0x43, 0x0F, 0xBA, 0xFA, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x9E, 0xAA, 0x79, 0xE1, 0x43, 0x0F, 0xBA, 0xFA, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE7, 0x40, 0xEB, 0xCA, 0x80, 0xE2, 0x03, 0xBF, 0xBA, 0xC2, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x80, 0xDB, 0xCA, 0x40, 0xE2, 0x08, 0xDF, 0xBA, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEA, 0x00, 0x68, 0xB8, 0x90, 0xE2, 0x0A, 0x8E, 0xB8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0x00, 0x68, 0xB8, 0x90, 0xE2, 0x0A, 0x8E, 0xB8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x61, 0x00, 0xBE, 0x99, 0xFA, 0xE3, 0x40, 0xCF, 0xCA, 0xF9, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0xCE, 0x9A, 0xA8, 0xE2, 0x45, 0xCF, 0xCA, 0xA0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xCD, 0x00, 0x0B, 0x00, 0x90, 0xC2, 0x58, 0x0C, 0x00, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C }, + { 0xCD, 0x00, 0x0B, 0x00, 0x90, 0xC2, 0x58, 0x0C, 0x00, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C } }, + { { 0xE2, 0x00, 0x0E, 0x00, 0xA2, 0xE2, 0x58, 0x5F, 0xD0, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x0E, 0x00, 0xA2, 0xE2, 0x58, 0x5F, 0xD0, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEC, 0x00, 0x7D, 0xDA, 0x80, 0xE2, 0x00, 0x5E, 0x9B, 0xA8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x0A, 0x4C, 0xC9, 0x60, 0xE2, 0x07, 0x0C, 0x7A, 0xB8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE9, 0xC0, 0xEE, 0xD8, 0x83, 0xE2, 0x05, 0xDD, 0xAA, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xED, 0x48, 0xDE, 0xD8, 0xB4, 0xE1, 0x00, 0xDD, 0xAA, 0xA9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xDA, 0x00, 0x8F, 0xAC, 0x92, 0x22, 0x05, 0x8D, 0x8A, 0xE9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEF, 0x00, 0x8C, 0xAA, 0x67, 0x25, 0x00, 0x9D, 0xAB, 0xC1, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x82, 0xCB, 0x7A, 0xD8, 0xE6, 0x56, 0xAF, 0xDB, 0xE0, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x84, 0xBB, 0xAA, 0xCA, 0xCF, 0x41, 0xAC, 0xDA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x41, 0xAC, 0xBB, 0xBB, 0xC2, 0x85, 0x0E, 0xCB, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 }, + { 0xC2, 0x03, 0x6A, 0x5B, 0xA4, 0xC2, 0x0D, 0x2A, 0xBB, 0xFC, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 } }, + { { 0x75, 0x00, 0x0E, 0xBB, 0xB2, 0xE2, 0x1E, 0x0A, 0xA9, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x62, 0x00, 0x04, 0x9A, 0xE8, 0xE2, 0x00, 0x0A, 0x48, 0xFD, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } }, + { { 0x41, 0x00, 0x0E, 0xEA, 0xA3, 0xC2, 0x00, 0x08, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x41, 0x00, 0x0E, 0xEA, 0xA3, 0xC2, 0x00, 0x08, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0xC1, 0x40, 0x0C, 0x59, 0xD2, 0xC2, 0x80, 0x3C, 0xAB, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0xC1, 0x40, 0x0C, 0x59, 0xD2, 0xC2, 0x80, 0x3C, 0xAB, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D } }, + { { 0x4B, 0x00, 0x0A, 0xF5, 0xC1, 0xC2, 0x19, 0x0C, 0xE9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x4B, 0x00, 0x0A, 0xF5, 0xC1, 0xC2, 0x19, 0x0C, 0xE9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0x62, 0x00, 0x7F, 0xD8, 0xA8, 0xEA, 0x00, 0x8F, 0xD8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x7F, 0xD8, 0xA8, 0xEA, 0x00, 0x8F, 0xD8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xCF, 0x40, 0x09, 0xEA, 0xA8, 0xC4, 0x00, 0x0C, 0xDB, 0xC8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x40, 0x09, 0xEA, 0xA8, 0xC4, 0x00, 0x0C, 0xDB, 0xC8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x40, 0x0C, 0xAA, 0xA8, 0xC4, 0x00, 0x18, 0xF9, 0xC8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x40, 0x0C, 0xAA, 0xA8, 0xC4, 0x00, 0x18, 0xF9, 0xC8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xC9, 0x0C, 0x88, 0xD9, 0x6A, 0xC2, 0x14, 0x3A, 0xEA, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0xC5, 0x00, 0x98, 0xD9, 0x92, 0xC1, 0x16, 0x6E, 0xF9, 0xE8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x03, 0x00, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xF8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x03, 0x00, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xF8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x01, 0x0C, 0x44, 0xE6, 0xE8, 0x01, 0x3F, 0x0C, 0xEA, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x02, 0x3F, 0x05, 0x08, 0xF8, 0x03, 0x3F, 0x3C, 0xF9, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0x00, 0x00, 0x36, 0x67, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x00, 0x00, 0x36, 0x67, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x02, 0x00, 0x36, 0x68, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x02, 0x00, 0x36, 0x68, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0xCB, 0x00, 0xAF, 0x00, 0xFA, 0xC0, 0x00, 0xC0, 0x06, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F }, + { 0xCB, 0x00, 0xAF, 0x00, 0xFA, 0xC0, 0x00, 0xC0, 0x06, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F } }, + { { 0x05, 0x0D, 0x80, 0xA6, 0xFB, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x05, 0x0D, 0x80, 0xA6, 0xFB, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x0F, 0x00, 0x90, 0xFA, 0xD0, 0x06, 0x00, 0xA7, 0x39, 0xA8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0x0F, 0x00, 0x90, 0xFA, 0xD0, 0x06, 0x00, 0xA7, 0x39, 0xA8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0xC9, 0x15, 0xDD, 0xFF, 0xF8, 0x00, 0x00, 0xE7, 0xFC, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 }, + { 0xC9, 0x15, 0xDD, 0xFF, 0xF8, 0x00, 0x00, 0xE7, 0xFC, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 } }, + { { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x07, 0x80, 0x0B, 0xC8, 0xC9, 0x02, 0x3F, 0x0C, 0xEA, 0xF8, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x07, 0x80, 0x0B, 0xC8, 0xC9, 0x02, 0x3F, 0x0C, 0xEA, 0xF8, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x08, 0x00, 0x0B, 0x3C, 0xF8, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x08, 0x00, 0x0B, 0x3C, 0xF8, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } } +}; + +static const AdLibInstrument g_gmPercussionInstrumentsOPL3[39][2] = { + { { 0x1A, 0x3F, 0x15, 0x05, 0xF8, 0x02, 0x21, 0x2B, 0xE4, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0x11, 0x18, 0x15, 0x00, 0xF8, 0x12, 0x00, 0x2B, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0x11, 0x12, 0x04, 0x07, 0xF8, 0x02, 0x18, 0x0B, 0xE5, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x11, 0x28, 0x06, 0x04, 0xF8, 0x02, 0x1E, 0x1B, 0x02, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x0A, 0x3F, 0x0B, 0x01, 0xF8, 0x1F, 0x13, 0x46, 0xD0, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 }, + { 0x04, 0x18, 0x06, 0x01, 0xB0, 0x10, 0x00, 0x07, 0x00, 0x90, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 } }, + { { 0x00, 0x3F, 0x0F, 0x00, 0xF8, 0x10, 0x0A, 0x07, 0x00, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x02, 0x14, 0x04, 0x00, 0xC0, 0x11, 0x08, 0x07, 0x00, 0xC6, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x0F, 0x3F, 0x0B, 0x00, 0xF8, 0x1F, 0x07, 0x19, 0xD0, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0E, 0x32, 0x76, 0x03, 0xF8, 0x1F, 0x0F, 0x77, 0xD4, 0xFC, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x00, 0x3F, 0x1F, 0x00, 0xFA, 0x1F, 0x0C, 0x07, 0x00, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x07, 0x11, 0x13, 0x00, 0xA0, 0x13, 0x00, 0x07, 0x00, 0xC8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x12, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x16, 0x4A, 0xD9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x02, 0x22, 0x05, 0xB6, 0xF8, 0x04, 0x0A, 0x59, 0x03, 0xF8, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x7F, 0x08, 0xFF, 0xFA, 0x00, 0xC0, 0x2D, 0xF7, 0xE3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xD2, 0x7F, 0x04, 0x0F, 0xFA, 0x10, 0xCD, 0x24, 0x07, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x12, 0x3F, 0x05, 0x06, 0xF8, 0x43, 0x17, 0x0C, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x13, 0x09, 0x96, 0xF8, 0x44, 0x0A, 0x07, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x7F, 0x08, 0xCF, 0xFA, 0x00, 0x40, 0x2A, 0xF8, 0x8B, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0xCF, 0x7F, 0x05, 0x07, 0xFA, 0x00, 0x40, 0x25, 0x08, 0xC3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C } }, + { { 0x12, 0x3F, 0x06, 0x17, 0xF8, 0x03, 0x1D, 0x0B, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x1A, 0x08, 0x96, 0xF8, 0x44, 0x00, 0x08, 0x03, 0xF8, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x7F, 0x08, 0xCD, 0xFA, 0x00, 0x40, 0x1A, 0x69, 0xB3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0xCD, 0x3F, 0x36, 0x05, 0xFC, 0x0F, 0x47, 0x46, 0x06, 0xDF, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C } }, + { { 0x13, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x0D, 0x0A, 0xD9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x14, 0x09, 0x96, 0xF8, 0x44, 0x02, 0x07, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x15, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x16, 0x0C, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x00, 0x07, 0x96, 0xE8, 0x44, 0x02, 0x08, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x3F, 0x2B, 0xFB, 0xFA, 0xC0, 0x16, 0x1A, 0xCA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0xCF, 0x3F, 0x2B, 0xFB, 0xFA, 0xC0, 0x1E, 0x1A, 0xCA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } }, + { { 0x17, 0x3F, 0x04, 0x09, 0xF8, 0x03, 0x18, 0x0D, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x00, 0x07, 0x96, 0xF8, 0x44, 0x02, 0x08, 0xF9, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x3F, 0x0F, 0x5E, 0xF8, 0xC6, 0x0C, 0x00, 0xCA, 0xFB, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x04, 0x57, 0xF8, 0xC5, 0x13, 0x06, 0x05, 0xFF, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x3F, 0x7E, 0x9D, 0xF8, 0xC8, 0xC0, 0x0A, 0xBA, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xCF, 0x3F, 0x77, 0x09, 0xF8, 0xC2, 0xC0, 0x08, 0xB5, 0xEA, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0xCF, 0x3F, 0x4D, 0x9F, 0xF8, 0xC6, 0x00, 0x08, 0xDA, 0xAB, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0xCF, 0x3F, 0x47, 0x06, 0xF8, 0xCD, 0x00, 0x07, 0x05, 0xB3, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0xCF, 0x3F, 0x5D, 0xAA, 0xF2, 0xC0, 0x8A, 0x67, 0x99, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x9A, 0x69, 0xF8, 0xCF, 0x88, 0x88, 0x48, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x3F, 0x4A, 0xFD, 0xF8, 0xCF, 0x00, 0x59, 0xEA, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x48, 0x06, 0xF8, 0xCF, 0x00, 0x54, 0x04, 0xF9, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x0F, 0x18, 0x0A, 0xFA, 0xAB, 0x06, 0x06, 0x06, 0x39, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x03, 0x18, 0x04, 0x09, 0xAC, 0x05, 0x07, 0x08, 0x07, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x3F, 0x2B, 0xFC, 0xF8, 0xCC, 0xC4, 0x0B, 0xEA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0xCF, 0x3F, 0x25, 0x06, 0xF8, 0xCC, 0xD7, 0x05, 0x02, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } }, + { { 0x05, 0x1A, 0x04, 0x00, 0xF8, 0x12, 0x08, 0x0C, 0xEA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x01, 0x00, 0x09, 0x08, 0x40, 0x13, 0x00, 0x2A, 0x0A, 0xD8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0x04, 0x19, 0x04, 0x00, 0xF8, 0x12, 0x08, 0x2C, 0xEA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x04, 0x00, 0x07, 0x08, 0x40, 0x12, 0x00, 0x29, 0x08, 0xE0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x04, 0x0A, 0x04, 0x00, 0xD8, 0x01, 0x02, 0x0D, 0xFA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x04, 0x00, 0x03, 0x09, 0x93, 0x02, 0x00, 0x28, 0x09, 0xE8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0x15, 0x14, 0x05, 0x00, 0xF9, 0x01, 0x03, 0x5C, 0xE9, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x05, 0x00, 0x03, 0x03, 0x49, 0x02, 0x00, 0x58, 0x08, 0xE0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x10, 0x10, 0x05, 0x08, 0xF8, 0x01, 0x03, 0x0D, 0xEA, 0xE8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x10, 0x00, 0x0C, 0x0C, 0x48, 0x02, 0x00, 0x08, 0xB9, 0xE0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x11, 0x00, 0x06, 0x87, 0xFB, 0x02, 0x40, 0x09, 0x59, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0x15, 0x00, 0x04, 0x87, 0xFB, 0x02, 0x40, 0x09, 0x59, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 } }, + { { 0x13, 0x26, 0x04, 0x6A, 0xFB, 0x01, 0x00, 0x08, 0x5A, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0x12, 0x26, 0x03, 0x6A, 0xFB, 0x02, 0x00, 0x06, 0x5A, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 } }, + { { 0xCF, 0x4D, 0x0C, 0xAA, 0xA0, 0xC4, 0x00, 0x18, 0xF9, 0x90, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x4E, 0x05, 0xA6, 0xA0, 0xC6, 0x00, 0x16, 0xF8, 0x60, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x4D, 0x0C, 0xAA, 0xA0, 0xC3, 0x00, 0x18, 0xF8, 0x98, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x4E, 0x06, 0xAA, 0xA0, 0xC5, 0x00, 0x19, 0xF9, 0x90, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCB, 0x3F, 0x8F, 0x00, 0xFA, 0xC5, 0x06, 0x98, 0xD6, 0xBB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0xC0, 0x00, 0xF0, 0x00, 0x00, 0xC0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D } }, + { { 0x0C, 0x18, 0x87, 0xB3, 0xFB, 0x19, 0x0B, 0x55, 0x75, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0C, 0x18, 0x87, 0xB3, 0xFB, 0x1B, 0x10, 0x57, 0x75, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x05, 0x11, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xA8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x02, 0x11, 0x13, 0x00, 0xC8, 0x02, 0x00, 0x05, 0x00, 0x80, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x04, 0x08, 0x15, 0x00, 0x90, 0x01, 0x00, 0x08, 0x00, 0xC0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x03, 0x08, 0x14, 0x00, 0x90, 0x02, 0x00, 0x07, 0x00, 0xA8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xDA, 0x00, 0x53, 0x30, 0xC0, 0x07, 0x10, 0x49, 0xC4, 0xDA, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x00, 0x56, 0x30, 0x90, 0x06, 0x00, 0x46, 0x56, 0x62, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x1C, 0x00, 0x07, 0xBC, 0xC8, 0x0C, 0x0A, 0x0B, 0x6A, 0xF2, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x18, 0x00, 0x07, 0xBC, 0x88, 0x09, 0x00, 0x0B, 0x6A, 0xBA, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x0A, 0x0E, 0x7F, 0x00, 0xF9, 0x13, 0x16, 0x28, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x01, 0x0E, 0x54, 0x00, 0xF9, 0x15, 0x03, 0x27, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } } +}; +#endif + +static const byte g_gmPercussionInstrumentMap[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, @@ -495,9 +858,9 @@ static const byte gm_percussion_lookup[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; -static byte lookup_table[64][32]; +static byte g_volumeLookupTable[64][32]; -static const byte volume_table[] = { +static const byte g_volumeTable[] = { 0, 4, 7, 11, 13, 16, 18, 20, 22, 24, 26, 27, @@ -516,7 +879,7 @@ static const byte volume_table[] = { 62, 63, 63, 63 }; -static int lookup_volume(int a, int b) { +static int lookupVolume(int a, int b) { if (b == 0) return 0; @@ -529,32 +892,32 @@ static int lookup_volume(int a, int b) { if (b < 0) { if (a < 0) { - return lookup_table[-a][-b]; + return g_volumeLookupTable[-a][-b]; } else { - return -lookup_table[a][-b]; + return -g_volumeLookupTable[a][-b]; } } else { if (a < 0) { - return -lookup_table[-a][b]; + return -g_volumeLookupTable[-a][b]; } else { - return lookup_table[a][b]; + return g_volumeLookupTable[a][b]; } } } -static void create_lookup_table() { +static void createLookupTable() { int i, j; int sum; for (i = 0; i < 64; i++) { sum = i; for (j = 0; j < 32; j++) { - lookup_table[i][j] = sum >> 5; + g_volumeLookupTable[i][j] = sum >> 5; sum += i; } } for (i = 0; i < 64; i++) - lookup_table[i][0] = 0; + g_volumeLookupTable[i][0] = 0; } //////////////////////////////////////// @@ -584,58 +947,75 @@ public: // AudioStream API - bool isStereo() const { return false; } + bool isStereo() const { return _opl->isStereo(); } int getRate() const { return _mixer->getOutputRate(); } private: - bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games + bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games +#ifdef ENABLE_OPL3 + bool _opl3Mode; +#endif - FM_OPL *_opl; - byte *_adlib_reg_cache; + OPL::OPL *_opl; + byte *_regCache; +#ifdef ENABLE_OPL3 + byte *_regCacheSecondary; +#endif - int _adlib_timer_counter; + int _timerCounter; - uint16 channel_table_2[9]; - int _voice_index; - int _timer_p; - int _timer_q; - uint16 curnote_table[9]; + uint16 _channelTable2[9]; + int _voiceIndex; + int _timerIncrease; + int _timerThreshold; + uint16 _curNotTable[9]; AdLibVoice _voices[9]; AdLibPart _parts[32]; AdLibPercussionChannel _percussion; void generateSamples(int16 *buf, int len); void onTimer(); - void part_key_on(AdLibPart *part, AdLibInstrument *instr, byte note, byte velocity); - void part_key_off(AdLibPart *part, byte note); + void partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan); + void partKeyOff(AdLibPart *part, byte note); - void adlib_key_off(int chan); - void adlib_note_on(int chan, byte note, int mod); - void adlib_note_on_ex(int chan, byte note, int mod); - int adlib_get_reg_value_param(int chan, byte data); - void adlib_setup_channel(int chan, AdLibInstrument *instr, byte vol_1, byte vol_2); - byte adlib_get_reg_value(byte reg) { - return _adlib_reg_cache[reg]; + void adlibKeyOff(int chan); + void adlibNoteOn(int chan, byte note, int mod); + void adlibNoteOnEx(int chan, byte note, int mod); + int adlibGetRegValueParam(int chan, byte data); + void adlibSetupChannel(int chan, const AdLibInstrument *instr, byte vol1, byte vol2); +#ifdef ENABLE_OPL3 + void adlibSetupChannelSecondary(int chan, const AdLibInstrument *instr, byte vol1, byte vol2, byte pan); +#endif + byte adlibGetRegValue(byte reg) { + return _regCache[reg]; } - void adlib_set_param(int channel, byte param, int value); - void adlib_key_onoff(int channel); - void adlib_write(byte reg, byte value); - void adlib_playnote(int channel, int note); +#ifdef ENABLE_OPL3 + byte adlibGetRegValueSecondary(byte reg) { + return _regCacheSecondary[reg]; + } +#endif + void adlibSetParam(int channel, byte param, int value, bool primary = true); + void adlibKeyOnOff(int channel); + void adlibWrite(byte reg, byte value); +#ifdef ENABLE_OPL3 + void adlibWriteSecondary(byte reg, byte value); +#endif + void adlibPlayNote(int channel, int note); - AdLibVoice *allocate_voice(byte pri); + AdLibVoice *allocateVoice(byte pri); - void mc_off(AdLibVoice *voice); + void mcOff(AdLibVoice *voice); - static void link_mc(AdLibPart *part, AdLibVoice *voice); - void mc_inc_stuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11); - void mc_init_stuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11, byte flags, - InstrumentExtra *ie); + static void linkMc(AdLibPart *part, AdLibVoice *voice); + void mcIncStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11); + void mcInitStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11, byte flags, + const InstrumentExtra *ie); - void struct10_init(Struct10 *s10, InstrumentExtra *ie); - static byte struct10_ontimer(Struct10 *s10, Struct11 *s11); - static void struct10_setup(Struct10 *s10); - static int random_nr(int a); - void mc_key_on(AdLibVoice *voice, AdLibInstrument *instr, byte note, byte velocity); + void struct10Init(Struct10 *s10, const InstrumentExtra *ie); + static byte struct10OnTimer(Struct10 *s10, Struct11 *s11); + static void struct10Setup(Struct10 *s10); + static int randomNr(int a); + void mcKeyOn(AdLibVoice *voice, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan); }; // MidiChannel method implementations @@ -643,7 +1023,7 @@ private: void AdLibPart::init(MidiDriver_ADLIB *owner, byte channel) { _owner = owner; _channel = channel; - _pri_eff = 127; + _priEff = 127; programChange(0); } @@ -657,41 +1037,64 @@ void AdLibPart::send(uint32 b) { void AdLibPart::noteOff(byte note) { #ifdef DEBUG_ADLIB - debug(6, "%10d: noteOff(%d)", tick, note); + debug(6, "%10d: noteOff(%d)", g_tick, note); #endif - _owner->part_key_off(this, note); + _owner->partKeyOff(this, note); } void AdLibPart::noteOn(byte note, byte velocity) { #ifdef DEBUG_ADLIB - debug(6, "%10d: noteOn(%d,%d)", tick, note, velocity); + debug(6, "%10d: noteOn(%d,%d)", g_tick, note, velocity); #endif - _owner->part_key_on(this, &_part_instr, note, velocity); + _owner->partKeyOn(this, &_partInstr, note, velocity, +#ifdef ENABLE_OPL3 + &_partInstrSecondary, +#else + NULL, +#endif + _pan); } void AdLibPart::programChange(byte program) { if (program > 127) return; -/* + /* uint i; uint count = 0; - for (i = 0; i < ARRAYSIZE(map_gm_to_fm[0]); ++i) - count += map_gm_to_fm[program][i]; + for (i = 0; i < ARRAYSIZE(g_gmInstruments[0]); ++i) + count += g_gmInstruments[program][i]; if (!count) warning("No AdLib instrument defined for GM program %d", (int)program); -*/ + */ _program = program; - memcpy(&_part_instr, &map_gm_to_fm[program], sizeof(AdLibInstrument)); +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + memcpy(&_partInstr, &g_gmInstruments[program], sizeof(AdLibInstrument)); +#ifdef ENABLE_OPL3 + } else { + memcpy(&_partInstr, &g_gmInstrumentsOPL3[program][0], sizeof(AdLibInstrument)); + memcpy(&_partInstrSecondary, &g_gmInstrumentsOPL3[program][1], sizeof(AdLibInstrument)); + } +#endif } void AdLibPart::pitchBend(int16 bend) { AdLibVoice *voice; - _pitchbend = bend; + _pitchBend = bend; for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, - (_pitchbend * _pitchbend_factor >> 6) + _detune_eff); +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + _owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/, + (_pitchBend * _pitchBendFactor >> 6) + _detuneEff); +#ifdef ENABLE_OPL3 + } else { + _owner->adlibNoteOn(voice->_channel, voice->_note, _pitchBend >> 1); + } +#endif } } @@ -699,75 +1102,137 @@ void AdLibPart::controlChange(byte control, byte value) { switch (control) { case 0: case 32: - break; // Bank select. Not supported - case 1: modulationWheel(value); break; - case 7: volume(value); break; - case 10: break; // Pan position. Not supported. - case 16: pitchBendFactor(value); break; - case 17: detune(value); break; - case 18: priority(value); break; - case 64: sustain(value > 0); break; - case 91: break; // Effects level. Not supported. - case 93: break; // Chorus level. Not supported. - case 119: break; // Unknown, used in Simon the Sorcerer 2 - case 121: // reset all controllers + // Bank select. Not supported + break; + case 1: + modulationWheel(value); + break; + case 7: + volume(value); + break; + case 10: + panPosition(value); + break; + case 16: + pitchBendFactor(value); + break; + case 17: + detune(value); + break; + case 18: + priority(value); + break; + case 64: + sustain(value > 0); + break; + case 91: + // Effects level. Not supported. + break; + case 93: + // Chorus level. Not supported. + break; + case 119: + // Unknown, used in Simon the Sorcerer 2 + break; + case 121: + // reset all controllers modulationWheel(0); pitchBendFactor(0); detune(0); sustain(0); break; - case 123: allNotesOff(); break; + case 123: + allNotesOff(); + break; default: - warning("AdLib: Unknown control change message %d (%d)", (int) control, (int)value); + warning("AdLib: Unknown control change message %d (%d)", (int)control, (int)value); } } void AdLibPart::modulationWheel(byte value) { AdLibVoice *voice; - _modwheel = value; + _modWheel = value; for (voice = _voice; voice; voice = voice->_next) { if (voice->_s10a.active && voice->_s11a.flag0x40) - voice->_s10a.modwheel = _modwheel >> 2; + voice->_s10a.modWheel = _modWheel >> 2; if (voice->_s10b.active && voice->_s11b.flag0x40) - voice->_s10b.modwheel = _modwheel >> 2; + voice->_s10b.modWheel = _modWheel >> 2; } } void AdLibPart::volume(byte value) { AdLibVoice *voice; - _vol_eff = value; + _volEff = value; for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_set_param(voice->_channel, 0, volume_table[lookup_table[voice->_vol_2][_vol_eff >> 2]]); - if (voice->_twochan) { - _owner->adlib_set_param(voice->_channel, 13, volume_table[lookup_table[voice->_vol_1][_vol_eff >> 2]]); +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + _owner->adlibSetParam(voice->_channel, 0, g_volumeTable[g_volumeLookupTable[voice->_vol2][_volEff >> 2]]); + if (voice->_twoChan) { + _owner->adlibSetParam(voice->_channel, 13, g_volumeTable[g_volumeLookupTable[voice->_vol1][_volEff >> 2]]); + } +#ifdef ENABLE_OPL3 + } else { + _owner->adlibSetParam(voice->_channel, 0, g_volumeTable[((voice->_vol2 + 1) * _volEff) >> 7], true); + _owner->adlibSetParam(voice->_channel, 0, g_volumeTable[((voice->_secVol2 + 1) * _volEff) >> 7], false); + if (voice->_twoChan) { + _owner->adlibSetParam(voice->_channel, 13, g_volumeTable[((voice->_vol1 + 1) * _volEff) >> 7], true); + } + if (voice->_secTwoChan) { + _owner->adlibSetParam(voice->_channel, 13, g_volumeTable[((voice->_secVol1 + 1) * _volEff) >> 7], false); + } } +#endif } } +void AdLibPart::panPosition(byte value) { + _pan = value; +} + void AdLibPart::pitchBendFactor(byte value) { +#ifdef ENABLE_OPL3 + // Not supported in OPL3 mode. + if (_owner->_opl3Mode) { + return; + } +#endif + AdLibVoice *voice; - _pitchbend_factor = value; + _pitchBendFactor = value; for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, - (_pitchbend * _pitchbend_factor >> 6) + _detune_eff); + _owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/, + (_pitchBend * _pitchBendFactor >> 6) + _detuneEff); } } void AdLibPart::detune(byte value) { + // Sam&Max's OPL3 driver uses this for a completly different purpose. It + // is related to voice allocation. We ignore this for now. + // TODO: We probably need to look how the interpreter side of Sam&Max's + // iMuse version handles all this too. Implementing the driver side here + // would be not that hard. +#ifdef ENABLE_OPL3 + if (_owner->_opl3Mode) { + //_maxNotes = value; + return; + } +#endif + AdLibVoice *voice; - _detune_eff = value; + _detuneEff = value; for (voice = _voice; voice; voice = voice->_next) { - _owner->adlib_note_on(voice->_channel, voice->_note + _transpose_eff, - (_pitchbend * _pitchbend_factor >> 6) + _detune_eff); + _owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/, + (_pitchBend * _pitchBendFactor >> 6) + _detuneEff); } } void AdLibPart::priority(byte value) { - _pri_eff = value; + _priEff = value; } void AdLibPart::sustain(bool value) { @@ -776,20 +1241,29 @@ void AdLibPart::sustain(bool value) { _pedal = value; if (!value) { for (voice = _voice; voice; voice = voice->_next) { - if (voice->_waitforpedal) - _owner->mc_off(voice); + if (voice->_waitForPedal) + _owner->mcOff(voice); } } } void AdLibPart::allNotesOff() { while (_voice) - _owner->mc_off(_voice); + _owner->mcOff(_voice); } void AdLibPart::sysEx_customInstrument(uint32 type, const byte *instr) { + // Sam&Max allows for instrument overwrites, but we will not support it + // until we can find any track actually using it. +#ifdef ENABLE_OPL3 + if (_owner->_opl3Mode) { + warning("AdLibPart::sysEx_customInstrument: Used in OPL3 mode"); + return; + } +#endif + if (type == 'ADL ') { - memcpy(&_part_instr, instr, sizeof(AdLibInstrument)); + memcpy(&_partInstr, instr, sizeof(AdLibInstrument)); } } @@ -803,8 +1277,8 @@ AdLibPercussionChannel::~AdLibPercussionChannel() { void AdLibPercussionChannel::init(MidiDriver_ADLIB *owner, byte channel) { AdLibPart::init(owner, channel); - _pri_eff = 0; - _vol_eff = 127; + _priEff = 0; + _volEff = 127; // Initialize the custom instruments data memset(_notes, 0, sizeof(_notes)); @@ -812,33 +1286,49 @@ void AdLibPercussionChannel::init(MidiDriver_ADLIB *owner, byte channel) { } void AdLibPercussionChannel::noteOff(byte note) { - // Jamieson630: Unless I run into a specific instrument that - // may require a key off, I'm going to ignore this message. - // The rationale is that a percussion instrument should - // fade out of its own accord, and the AdLib instrument - // definitions used should follow this rule. Since - // percussion voices are allocated at the lowest priority - // anyway, we know that "hanging" percussion sounds will - // not prevent later musical instruments (or even other - // percussion sounds) from playing. -/* - _owner->part_key_off(this, note); -*/ + if (_customInstruments[note]) { + note = _notes[note]; + } + + // This used to ignore note off events, since the builtin percussion + // instrument data has a duration value, which causes the percussion notes + // to stop automatically. This is not the case for (Groovie's) custom + // percussion instruments though. Also the OPL3 driver of Sam&Max actually + // does not handle the duration value, so we need it there too. + _owner->partKeyOff(this, note); } void AdLibPercussionChannel::noteOn(byte note, byte velocity) { - AdLibInstrument *inst = NULL; + const AdLibInstrument *inst = NULL; + const AdLibInstrument *sec = NULL; // The custom instruments have priority over the default mapping - inst = _customInstruments[note]; - if (inst) - note = _notes[note]; + // We do not support custom instruments in OPL3 mode though. +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + inst = _customInstruments[note]; + if (inst) + note = _notes[note]; +#ifdef ENABLE_OPL3 + } +#endif if (!inst) { - // Use the default GM to FM mapping as a fallback as a fallback - byte key = gm_percussion_lookup[note]; - if (key != 0xFF) - inst = &gm_percussion_to_fm[key]; + // Use the default GM to FM mapping as a fallback + byte key = g_gmPercussionInstrumentMap[note]; + if (key != 0xFF) { +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + inst = &g_gmPercussionInstruments[key]; +#ifdef ENABLE_OPL3 + } else { + inst = &g_gmPercussionInstrumentsOPL3[key][0]; + sec = &g_gmPercussionInstrumentsOPL3[key][1]; + } +#endif + } } if (!inst) { @@ -846,10 +1336,18 @@ void AdLibPercussionChannel::noteOn(byte note, byte velocity) { return; } - _owner->part_key_on(this, inst, note, velocity); + _owner->partKeyOn(this, inst, note, velocity, sec, _pan); } void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *instr) { + // We do not allow custom instruments in OPL3 mode right now. +#ifdef ENABLE_OPL3 + if (_owner->_opl3Mode) { + warning("AdLibPercussionChannel::sysEx_customInstrument: Used in OPL3 mode"); + return; + } +#endif + if (type == 'ADLP') { byte note = instr[0]; _notes[note] = instr[1]; @@ -861,16 +1359,16 @@ void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *ins } // Save the new instrument data - _customInstruments[note]->mod_characteristic = instr[2]; - _customInstruments[note]->mod_scalingOutputLevel = instr[3]; - _customInstruments[note]->mod_attackDecay = instr[4]; - _customInstruments[note]->mod_sustainRelease = instr[5]; - _customInstruments[note]->mod_waveformSelect = instr[6]; - _customInstruments[note]->car_characteristic = instr[7]; - _customInstruments[note]->car_scalingOutputLevel = instr[8]; - _customInstruments[note]->car_attackDecay = instr[9]; - _customInstruments[note]->car_sustainRelease = instr[10]; - _customInstruments[note]->car_waveformSelect = instr[11]; + _customInstruments[note]->modCharacteristic = instr[2]; + _customInstruments[note]->modScalingOutputLevel = instr[3]; + _customInstruments[note]->modAttackDecay = instr[4]; + _customInstruments[note]->modSustainRelease = instr[5]; + _customInstruments[note]->modWaveformSelect = instr[6]; + _customInstruments[note]->carCharacteristic = instr[7]; + _customInstruments[note]->carScalingOutputLevel = instr[8]; + _customInstruments[note]->carAttackDecay = instr[9]; + _customInstruments[note]->carSustainRelease = instr[10]; + _customInstruments[note]->carWaveformSelect = instr[11]; _customInstruments[note]->feedback = instr[12]; } } @@ -882,21 +1380,28 @@ MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer) uint i; _scummSmallHeader = false; +#ifdef ENABLE_OPL3 + _opl3Mode = false; +#endif - _adlib_reg_cache = 0; + _regCache = 0; +#ifdef ENABLE_OPL3 + _regCacheSecondary = 0; +#endif - _adlib_timer_counter = 0; - _voice_index = 0; - for (i = 0; i < ARRAYSIZE(curnote_table); ++i) { - curnote_table[i] = 0; + _timerCounter = 0; + _voiceIndex = -1; + for (i = 0; i < ARRAYSIZE(_curNotTable); ++i) { + _curNotTable[i] = 0; } for (i = 0; i < ARRAYSIZE(_parts); ++i) { _parts[i].init(this, i + ((i >= 9) ? 1 : 0)); } _percussion.init(this, 9); - _timer_p = 0xD69; - _timer_q = 0x411B; + _timerIncrease = 0xD69; + _timerThreshold = 0x411B; + _opl = 0; } int MidiDriver_ADLIB::open() { @@ -914,14 +1419,37 @@ int MidiDriver_ADLIB::open() { voice->_s11b.s10 = &voice->_s10a; } - _adlib_reg_cache = (byte *)calloc(256, 1); + // Try to use OPL3 when requested. +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + _opl = OPL::Config::create(OPL::Config::kOpl3); + } - _opl = makeAdLibOPL(getRate()); + // Initialize plain OPL2 when no OPL3 is intiailized already. + if (!_opl) { +#endif + _opl = OPL::Config::create(); +#ifdef ENABLE_OPL3 + _opl3Mode = false; + } +#endif + _opl->init(getRate()); - adlib_write(1, 0x20); - adlib_write(8, 0x40); - adlib_write(0xBD, 0x00); - create_lookup_table(); + _regCache = (byte *)calloc(256, 1); + + adlibWrite(8, 0x40); + adlibWrite(0xBD, 0x00); +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + adlibWrite(1, 0x20); + createLookupTable(); +#ifdef ENABLE_OPL3 + } else { + _regCacheSecondary = (byte *)calloc(256, 1); + adlibWriteSecondary(5, 1); + } +#endif _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); @@ -938,14 +1466,17 @@ void MidiDriver_ADLIB::close() { uint i; for (i = 0; i < ARRAYSIZE(_voices); ++i) { if (_voices[i]._part) - mc_off(&_voices[i]); + mcOff(&_voices[i]); } // Turn off the OPL emulation - OPLDestroy(_opl); -// YM3812Shutdown(); + delete _opl; + _opl = 0; - free(_adlib_reg_cache); + free(_regCache); +#ifdef ENABLE_OPL3 + free(_regCacheSecondary); +#endif } void MidiDriver_ADLIB::send(uint32 b) { @@ -954,9 +1485,9 @@ void MidiDriver_ADLIB::send(uint32 b) { void MidiDriver_ADLIB::send(byte chan, uint32 b) { //byte param3 = (byte) ((b >> 24) & 0xFF); - byte param2 = (byte) ((b >> 16) & 0xFF); - byte param1 = (byte) ((b >> 8) & 0xFF); - byte cmd = (byte) (b & 0xF0); + byte param2 = (byte)((b >> 16) & 0xFF); + byte param1 = (byte)((b >> 8) & 0xFF); + byte cmd = (byte)(b & 0xF0); AdLibPart *part; if (chan == 9) @@ -997,29 +1528,42 @@ void MidiDriver_ADLIB::send(byte chan, uint32 b) { uint32 MidiDriver_ADLIB::property(int prop, uint32 param) { switch (prop) { - case PROP_OLD_ADLIB: // Older games used a different operator volume algorithm - _scummSmallHeader = (param > 0); - if (_scummSmallHeader) { - _timer_p = 473; - _timer_q = 1000; - } else { - _timer_p = 0xD69; - _timer_q = 0x411B; - } - return 1; + case PROP_OLD_ADLIB: // Older games used a different operator volume algorithm + _scummSmallHeader = (param > 0); + if (_scummSmallHeader) { + _timerIncrease = 473; + _timerThreshold = 1000; + } else { + _timerIncrease = 0xD69; + _timerThreshold = 0x411B; + } + return 1; + + case PROP_SCUMM_OPL3: // Sam&Max OPL3 support. +#ifdef ENABLE_OPL3 + _opl3Mode = (param > 0); +#endif + return 1; } return 0; } void MidiDriver_ADLIB::setPitchBendRange(byte channel, uint range) { +#ifdef ENABLE_OPL3 + // Not supported in OPL3 mode. + if (_opl3Mode) { + return; + } +#endif + AdLibVoice *voice; AdLibPart *part = &_parts[channel]; - part->_pitchbend_factor = range; + part->_pitchBendFactor = range; for (voice = part->_voice; voice; voice = voice->_next) { - adlib_note_on(voice->_channel, voice->_note + part->_transpose_eff, - (part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff); + adlibNoteOn(voice->_channel, voice->_note/* + part->_transposeEff*/, + (part->_pitchBend * part->_pitchBendFactor >> 6) + part->_detuneEff); } } @@ -1043,54 +1587,77 @@ MidiChannel *MidiDriver_ADLIB::allocateChannel() { // All the code brought over from IMuseAdLib -void MidiDriver_ADLIB::adlib_write(byte reg, byte value) { - if (_adlib_reg_cache[reg] == value) +void MidiDriver_ADLIB::adlibWrite(byte reg, byte value) { + if (_regCache[reg] == value) { return; + } #ifdef DEBUG_ADLIB - debug(6, "%10d: adlib_write[%x] = %x", tick, reg, value); + debug(6, "%10d: adlibWrite[%x] = %x", g_tick, reg, value); #endif - _adlib_reg_cache[reg] = value; + _regCache[reg] = value; - OPLWriteReg(_opl, reg, value); + _opl->writeReg(reg, value); } +#ifdef ENABLE_OPL3 +void MidiDriver_ADLIB::adlibWriteSecondary(byte reg, byte value) { + assert(_opl3Mode); + + if (_regCacheSecondary[reg] == value) { + return; + } +#ifdef DEBUG_ADLIB + debug(6, "%10d: adlibWriteSecondary[%x] = %x", g_tick, reg, value); +#endif + _regCacheSecondary[reg] = value; + + _opl->writeReg(reg | 0x100, value); +} +#endif + void MidiDriver_ADLIB::generateSamples(int16 *data, int len) { - memset(data, 0, sizeof(int16) * len); - YM3812UpdateOne(_opl, data, len); + if (_opl->isStereo()) { + len *= 2; + } + _opl->readBuffer(data, len); } void MidiDriver_ADLIB::onTimer() { - AdLibVoice *voice; - int i; - - _adlib_timer_counter += _timer_p; - while (_adlib_timer_counter >= _timer_q) { - _adlib_timer_counter -= _timer_q; + _timerCounter += _timerIncrease; + while (_timerCounter >= _timerThreshold) { + _timerCounter -= _timerThreshold; #ifdef DEBUG_ADLIB - tick++; + g_tick++; #endif - voice = _voices; - for (i = 0; i != ARRAYSIZE(_voices); i++, voice++) { - if (!voice->_part) - continue; - if (voice->_duration && (voice->_duration -= 0x11) <= 0) { - mc_off(voice); - return; - } - if (voice->_s10a.active) { - mc_inc_stuff(voice, &voice->_s10a, &voice->_s11a); - } - if (voice->_s10b.active) { - mc_inc_stuff(voice, &voice->_s10b, &voice->_s11b); + // Sam&Max's OPL3 driver does not have any timer handling like this. +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + AdLibVoice *voice = _voices; + for (int i = 0; i != ARRAYSIZE(_voices); i++, voice++) { + if (!voice->_part) + continue; + if (voice->_duration && (voice->_duration -= 0x11) <= 0) { + mcOff(voice); + return; + } + if (voice->_s10a.active) { + mcIncStuff(voice, &voice->_s10a, &voice->_s11a); + } + if (voice->_s10b.active) { + mcIncStuff(voice, &voice->_s10b, &voice->_s11b); + } } +#ifdef ENABLE_OPL3 } +#endif } } -void MidiDriver_ADLIB::mc_off(AdLibVoice *voice) { +void MidiDriver_ADLIB::mcOff(AdLibVoice *voice) { AdLibVoice *tmp; - adlib_key_off(voice->_channel); + adlibKeyOff(voice->_channel); tmp = voice->_prev; @@ -1103,57 +1670,62 @@ void MidiDriver_ADLIB::mc_off(AdLibVoice *voice) { voice->_part = NULL; } -void MidiDriver_ADLIB::mc_inc_stuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11) { +void MidiDriver_ADLIB::mcIncStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11) { byte code; AdLibPart *part = voice->_part; - code = struct10_ontimer(s10, s11); + code = struct10OnTimer(s10, s11); if (code & 1) { switch (s11->param) { case 0: - voice->_vol_2 = s10->start_value + s11->modify_val; + voice->_vol2 = s10->startValue + s11->modifyVal; if (!_scummSmallHeader) { - adlib_set_param(voice->_channel, 0, - volume_table[lookup_table[voice->_vol_2] - [part->_vol_eff >> 2]]); + adlibSetParam(voice->_channel, 0, + g_volumeTable[g_volumeLookupTable[voice->_vol2] + [part->_volEff >> 2]]); } else { - adlib_set_param(voice->_channel, 0, voice->_vol_2); + adlibSetParam(voice->_channel, 0, voice->_vol2); } break; case 13: - voice->_vol_1 = s10->start_value + s11->modify_val; - if (voice->_twochan && !_scummSmallHeader) { - adlib_set_param(voice->_channel, 13, - volume_table[lookup_table[voice->_vol_1] - [part->_vol_eff >> 2]]); + voice->_vol1 = s10->startValue + s11->modifyVal; + if (voice->_twoChan && !_scummSmallHeader) { + adlibSetParam(voice->_channel, 13, + g_volumeTable[g_volumeLookupTable[voice->_vol1] + [part->_volEff >> 2]]); } else { - adlib_set_param(voice->_channel, 13, voice->_vol_1); + adlibSetParam(voice->_channel, 13, voice->_vol1); } break; case 30: - s11->s10->modwheel = (char)s11->modify_val; + s11->s10->modWheel = (char)s11->modifyVal; break; case 31: - s11->s10->unk3 = (char)s11->modify_val; + s11->s10->unk3 = (char)s11->modifyVal; break; default: - adlib_set_param(voice->_channel, s11->param, - s10->start_value + s11->modify_val); + adlibSetParam(voice->_channel, s11->param, + s10->startValue + s11->modifyVal); break; } } if (code & 2 && s11->flag0x10) - adlib_key_onoff(voice->_channel); + adlibKeyOnOff(voice->_channel); } -void MidiDriver_ADLIB::adlib_key_off(int chan){ +void MidiDriver_ADLIB::adlibKeyOff(int chan) { byte reg = chan + 0xB0; - adlib_write(reg, adlib_get_reg_value(reg) & ~0x20); + adlibWrite(reg, adlibGetRegValue(reg) & ~0x20); +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + adlibWriteSecondary(reg, adlibGetRegValueSecondary(reg) & ~0x20); + } +#endif } -byte MidiDriver_ADLIB::struct10_ontimer(Struct10 *s10, Struct11 *s11) { +byte MidiDriver_ADLIB::struct10OnTimer(Struct10 *s10, Struct11 *s11) { byte result = 0; int i; @@ -1162,51 +1734,54 @@ byte MidiDriver_ADLIB::struct10_ontimer(Struct10 *s10, Struct11 *s11) { return 0; } - i = s10->cur_val + s10->speed_hi; - s10->speed_lo_counter += s10->speed_lo; - if (s10->speed_lo_counter >= s10->speed_lo_max) { - s10->speed_lo_counter -= s10->speed_lo_max; + i = s10->curVal + s10->speedHi; + s10->speedLoCounter += s10->speedLo; + if (s10->speedLoCounter >= s10->speedLoMax) { + s10->speedLoCounter -= s10->speedLoMax; i += s10->direction; } - if (s10->cur_val != i || s10->modwheel != s10->modwheel_last) { - s10->cur_val = i; - s10->modwheel_last = s10->modwheel; - i = lookup_volume(i, s10->modwheel_last); - if (i != s11->modify_val) { - s11->modify_val = i; + if (s10->curVal != i || s10->modWheel != s10->modWheelLast) { + s10->curVal = i; + s10->modWheelLast = s10->modWheel; + i = lookupVolume(i, s10->modWheelLast); + if (i != s11->modifyVal) { + s11->modifyVal = i; result = 1; } } - if (!--s10->num_steps) { + if (!--s10->numSteps) { s10->active++; if (s10->active > 4) { if (s10->loop) { s10->active = 1; result |= 2; - struct10_setup(s10); + struct10Setup(s10); } else { s10->active = 0; } } else { - struct10_setup(s10); + struct10Setup(s10); } } return result; } -void MidiDriver_ADLIB::adlib_set_param(int channel, byte param, int value) { +void MidiDriver_ADLIB::adlibSetParam(int channel, byte param, int value, bool primary) { const AdLibSetParams *as; byte reg; assert(channel >= 0 && channel < 9); +#ifdef ENABLE_OPL3 + assert(!_opl3Mode || (param == 0 || param == 13)); +#endif if (param <= 12) { - reg = channel_mappings_2[channel]; + reg = g_operator2Offsets[channel]; } else if (param <= 25) { param -= 13; - reg = channel_mappings[channel]; + reg = g_operator1Offsets[channel]; } else if (param <= 27) { param -= 13; reg = channel; @@ -1216,54 +1791,66 @@ void MidiDriver_ADLIB::adlib_set_param(int channel, byte param, int value) { else value -= 383; value <<= 4; - channel_table_2[channel] = value; - adlib_playnote(channel, curnote_table[channel] + value); + _channelTable2[channel] = value; + adlibPlayNote(channel, _curNotTable[channel] + value); return; } else { return; } - as = &adlib_setparam_table[param]; - if (as->d) - value = as->d - value; - reg += as->a; - adlib_write(reg, (adlib_get_reg_value(reg) & ~as->c) | (((byte)value) << as->b)); + as = &g_setParamTable[param]; + if (as->inversion) + value = as->inversion - value; + reg += as->registerBase; +#ifdef ENABLE_OPL3 + if (primary) { +#endif + adlibWrite(reg, (adlibGetRegValue(reg) & ~as->mask) | (((byte)value) << as->shift)); +#ifdef ENABLE_OPL3 + } else { + adlibWriteSecondary(reg, (adlibGetRegValueSecondary(reg) & ~as->mask) | (((byte)value) << as->shift)); + } +#endif } -void MidiDriver_ADLIB::adlib_key_onoff(int channel) { +void MidiDriver_ADLIB::adlibKeyOnOff(int channel) { +#ifdef ENABLE_OPL3 + assert(!_opl3Mode); +#endif + byte val; byte reg = channel + 0xB0; assert(channel >= 0 && channel < 9); - val = adlib_get_reg_value(reg); - adlib_write(reg, val & ~0x20); - adlib_write(reg, val | 0x20); + val = adlibGetRegValue(reg); + adlibWrite(reg, val & ~0x20); + adlibWrite(reg, val | 0x20); } -void MidiDriver_ADLIB::struct10_setup(Struct10 *s10) { +void MidiDriver_ADLIB::struct10Setup(Struct10 *s10) { int b, c, d, e, f, g, h; byte t; b = s10->unk3; f = s10->active - 1; - t = s10->table_a[f]; - e = num_steps_table[lookup_table[t & 0x7F][b]]; + t = s10->tableA[f]; + e = g_numStepsTable[g_volumeLookupTable[t & 0x7F][b]]; if (t & 0x80) { - e = random_nr(e); + e = randomNr(e); } if (e == 0) e++; - s10->num_steps = s10->speed_lo_max = e; + s10->numSteps = s10->speedLoMax = e; if (f != 2) { - c = s10->max_value; - g = s10->start_value; - t = s10->table_b[f]; - d = lookup_volume(c, (t & 0x7F) - 31); + c = s10->maxValue; + g = s10->startValue; + t = s10->tableB[f]; + d = lookupVolume(c, (t & 0x7F) - 31); if (t & 0x80) { - d = random_nr(d); + d = randomNr(d); } if (d + g > c) { h = c - g; @@ -1272,12 +1859,12 @@ void MidiDriver_ADLIB::struct10_setup(Struct10 *s10) { if (d + g < 0) h = -g; } - h -= s10->cur_val; + h -= s10->curVal; } else { h = 0; } - s10->speed_hi = h / e; + s10->speedHi = h / e; if (h < 0) { h = -h; s10->direction = -1; @@ -1285,11 +1872,11 @@ void MidiDriver_ADLIB::struct10_setup(Struct10 *s10) { s10->direction = 1; } - s10->speed_lo = h % e; - s10->speed_lo_counter = 0; + s10->speedLo = h % e; + s10->speedLoCounter = 0; } -void MidiDriver_ADLIB::adlib_playnote(int channel, int note) { +void MidiDriver_ADLIB::adlibPlayNote(int channel, int note) { byte old, oct, notex; int note2; int i; @@ -1304,7 +1891,7 @@ void MidiDriver_ADLIB::adlib_playnote(int channel, int note) { oct <<= 2; notex = note2 % 12 + 3; - old = adlib_get_reg_value(channel + 0xB0); + old = adlibGetRegValue(channel + 0xB0); if (old & 0x20) { old &= ~0x20; if (oct > old) { @@ -1321,58 +1908,58 @@ void MidiDriver_ADLIB::adlib_playnote(int channel, int note) { } i = (notex << 3) + ((note >> 4) & 0x7); - adlib_write(channel + 0xA0, note_to_f_num[i]); - adlib_write(channel + 0xB0, oct | 0x20); + adlibWrite(channel + 0xA0, g_noteFrequencies[i]); + adlibWrite(channel + 0xB0, oct | 0x20); } -int MidiDriver_ADLIB::random_nr(int a) { - static byte _rand_seed = 1; - if (_rand_seed & 1) { - _rand_seed >>= 1; - _rand_seed ^= 0xB8; +int MidiDriver_ADLIB::randomNr(int a) { + static byte _randSeed = 1; + if (_randSeed & 1) { + _randSeed >>= 1; + _randSeed ^= 0xB8; } else { - _rand_seed >>= 1; + _randSeed >>= 1; } - return _rand_seed * a >> 8; + return _randSeed * a >> 8; } -void MidiDriver_ADLIB::part_key_off(AdLibPart *part, byte note) { +void MidiDriver_ADLIB::partKeyOff(AdLibPart *part, byte note) { AdLibVoice *voice; for (voice = part->_voice; voice; voice = voice->_next) { if (voice->_note == note) { if (part->_pedal) - voice->_waitforpedal = true; + voice->_waitForPedal = true; else - mc_off(voice); + mcOff(voice); } } } -void MidiDriver_ADLIB::part_key_on(AdLibPart *part, AdLibInstrument *instr, byte note, byte velocity) { +void MidiDriver_ADLIB::partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) { AdLibVoice *voice; - voice = allocate_voice(part->_pri_eff); + voice = allocateVoice(part->_priEff); if (!voice) return; - link_mc(part, voice); - mc_key_on(voice, instr, note, velocity); + linkMc(part, voice); + mcKeyOn(voice, instr, note, velocity, second, pan); } -AdLibVoice *MidiDriver_ADLIB::allocate_voice(byte pri) { +AdLibVoice *MidiDriver_ADLIB::allocateVoice(byte pri) { AdLibVoice *ac, *best = NULL; int i; for (i = 0; i < 9; i++) { - if (++_voice_index >= 9) - _voice_index = 0; - ac = &_voices[_voice_index]; + if (++_voiceIndex >= 9) + _voiceIndex = 0; + ac = &_voices[_voiceIndex]; if (!ac->_part) return ac; if (!ac->_next) { - if (ac->_part->_pri_eff <= pri) { - pri = ac->_part->_pri_eff; + if (ac->_part->_priEff <= pri) { + pri = ac->_part->_priEff; best = ac; } } @@ -1383,11 +1970,11 @@ AdLibVoice *MidiDriver_ADLIB::allocate_voice(byte pri) { return NULL; if (best) - mc_off(best); + mcOff(best); return best; } -void MidiDriver_ADLIB::link_mc(AdLibPart *part, AdLibVoice *voice) { +void MidiDriver_ADLIB::linkMc(AdLibPart *part, AdLibVoice *voice) { voice->_part = part; voice->_next = (AdLibVoice *)part->_voice; part->_voice = voice; @@ -1397,153 +1984,229 @@ void MidiDriver_ADLIB::link_mc(AdLibPart *part, AdLibVoice *voice) { voice->_next->_prev = voice; } -void MidiDriver_ADLIB::mc_key_on(AdLibVoice *voice, AdLibInstrument *instr, byte note, byte velocity) { +void MidiDriver_ADLIB::mcKeyOn(AdLibVoice *voice, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) { AdLibPart *part = voice->_part; - int c; - byte vol_1, vol_2; + byte vol1, vol2; +#ifdef ENABLE_OPL3 + byte secVol1 = 0, secVol2 = 0; +#endif - voice->_twochan = instr->feedback & 1; + voice->_twoChan = instr->feedback & 1; voice->_note = note; - voice->_waitforpedal = false; + voice->_waitForPedal = false; voice->_duration = instr->duration; if (voice->_duration != 0) voice->_duration *= 63; - if (!_scummSmallHeader) - vol_1 = (instr->mod_scalingOutputLevel & 0x3F) + lookup_table[velocity >> 1][instr->mod_waveformSelect >> 2]; - else - vol_1 = 0x3f - (instr->mod_scalingOutputLevel & 0x3F); - if (vol_1 > 0x3F) - vol_1 = 0x3F; - voice->_vol_1 = vol_1; - - if (!_scummSmallHeader) - vol_2 = (instr->car_scalingOutputLevel & 0x3F) + lookup_table[velocity >> 1][instr->car_waveformSelect >> 2]; - else - vol_2 = 0x3f - (instr->car_scalingOutputLevel & 0x3F); - if (vol_2 > 0x3F) - vol_2 = 0x3F; - voice->_vol_2 = vol_2; - - c = part->_vol_eff >> 2; + if (!_scummSmallHeader) { +#ifdef ENABLE_OPL3 + if (_opl3Mode) + vol1 = (instr->modScalingOutputLevel & 0x3F) + (velocity * ((instr->modWaveformSelect >> 3) + 1)) / 64; + else +#endif + vol1 = (instr->modScalingOutputLevel & 0x3F) + g_volumeLookupTable[velocity >> 1][instr->modWaveformSelect >> 2]; + } else { + vol1 = 0x3f - (instr->modScalingOutputLevel & 0x3F); + } + if (vol1 > 0x3F) + vol1 = 0x3F; + voice->_vol1 = vol1; if (!_scummSmallHeader) { - vol_2 = volume_table[lookup_table[vol_2][c]]; - if (voice->_twochan) - vol_1 = volume_table[lookup_table[vol_1][c]]; - } - - adlib_setup_channel(voice->_channel, instr, vol_1, vol_2); - adlib_note_on_ex(voice->_channel, part->_transpose_eff + note, part->_detune_eff + (part->_pitchbend * part->_pitchbend_factor >> 6)); - - if (instr->flags_a & 0x80) { - mc_init_stuff(voice, &voice->_s10a, &voice->_s11a, instr->flags_a, &instr->extra_a); +#ifdef ENABLE_OPL3 + if (_opl3Mode) + vol2 = (instr->carScalingOutputLevel & 0x3F) + (velocity * ((instr->carWaveformSelect >> 3) + 1)) / 64; + else +#endif + vol2 = (instr->carScalingOutputLevel & 0x3F) + g_volumeLookupTable[velocity >> 1][instr->carWaveformSelect >> 2]; } else { - voice->_s10a.active = 0; + vol2 = 0x3f - (instr->carScalingOutputLevel & 0x3F); + } + if (vol2 > 0x3F) + vol2 = 0x3F; + voice->_vol2 = vol2; + +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + voice->_secTwoChan = second->feedback & 1; + secVol1 = (second->modScalingOutputLevel & 0x3F) + (velocity * ((second->modWaveformSelect >> 3) + 1)) / 64; + if (secVol1 > 0x3F) { + secVol1 = 0x3F; + } + voice->_secVol1 = secVol1; + secVol2 = (second->carScalingOutputLevel & 0x3F) + (velocity * ((second->carWaveformSelect >> 3) + 1)) / 64; + if (secVol2 > 0x3F) { + secVol2 = 0x3F; + } + voice->_secVol2 = secVol2; + } +#endif + + if (!_scummSmallHeader) { +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + int c = part->_volEff >> 2; + vol2 = g_volumeTable[g_volumeLookupTable[vol2][c]]; + if (voice->_twoChan) + vol1 = g_volumeTable[g_volumeLookupTable[vol1][c]]; +#ifdef ENABLE_OPL3 + } else { + vol2 = g_volumeTable[((vol2 + 1) * part->_volEff) >> 7]; + secVol2 = g_volumeTable[((secVol2 + 1) * part->_volEff) >> 7]; + if (voice->_twoChan) + vol1 = g_volumeTable[((vol1 + 1) * part->_volEff) >> 7]; + if (voice->_secTwoChan) + secVol1 = g_volumeTable[((secVol1 + 1) * part->_volEff) >> 7]; + } +#endif } - if (instr->flags_b & 0x80) { - mc_init_stuff(voice, &voice->_s10b, &voice->_s11b, instr->flags_b, &instr->extra_b); + adlibSetupChannel(voice->_channel, instr, vol1, vol2); +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + adlibNoteOnEx(voice->_channel, /*part->_transposeEff + */note, part->_detuneEff + (part->_pitchBend * part->_pitchBendFactor >> 6)); + + if (instr->flagsA & 0x80) { + mcInitStuff(voice, &voice->_s10a, &voice->_s11a, instr->flagsA, &instr->extraA); + } else { + voice->_s10a.active = 0; + } + + if (instr->flagsB & 0x80) { + mcInitStuff(voice, &voice->_s10b, &voice->_s11b, instr->flagsB, &instr->extraB); + } else { + voice->_s10b.active = 0; + } +#ifdef ENABLE_OPL3 } else { - voice->_s10b.active = 0; + adlibSetupChannelSecondary(voice->_channel, second, secVol1, secVol2, pan); + adlibNoteOnEx(voice->_channel, note, part->_pitchBend >> 1); } +#endif } -void MidiDriver_ADLIB::adlib_setup_channel(int chan, AdLibInstrument *instr, byte vol_1, byte vol_2) { - byte channel; - +void MidiDriver_ADLIB::adlibSetupChannel(int chan, const AdLibInstrument *instr, byte vol1, byte vol2) { assert(chan >= 0 && chan < 9); - channel = channel_mappings[chan]; - adlib_write(channel + 0x20, instr->mod_characteristic); - adlib_write(channel + 0x40, (instr->mod_scalingOutputLevel | 0x3F) - vol_1 ); - adlib_write(channel + 0x60, 0xff & (~instr->mod_attackDecay)); - adlib_write(channel + 0x80, 0xff & (~instr->mod_sustainRelease)); - adlib_write(channel + 0xE0, instr->mod_waveformSelect); + byte channel = g_operator1Offsets[chan]; + adlibWrite(channel + 0x20, instr->modCharacteristic); + adlibWrite(channel + 0x40, (instr->modScalingOutputLevel | 0x3F) - vol1); + adlibWrite(channel + 0x60, 0xff & (~instr->modAttackDecay)); + adlibWrite(channel + 0x80, 0xff & (~instr->modSustainRelease)); + adlibWrite(channel + 0xE0, instr->modWaveformSelect); - channel = channel_mappings_2[chan]; - adlib_write(channel + 0x20, instr->car_characteristic); - adlib_write(channel + 0x40, (instr->car_scalingOutputLevel | 0x3F) - vol_2 ); - adlib_write(channel + 0x60, 0xff & (~instr->car_attackDecay)); - adlib_write(channel + 0x80, 0xff & (~instr->car_sustainRelease)); - adlib_write(channel + 0xE0, instr->car_waveformSelect); + channel = g_operator2Offsets[chan]; + adlibWrite(channel + 0x20, instr->carCharacteristic); + adlibWrite(channel + 0x40, (instr->carScalingOutputLevel | 0x3F) - vol2); + adlibWrite(channel + 0x60, 0xff & (~instr->carAttackDecay)); + adlibWrite(channel + 0x80, 0xff & (~instr->carSustainRelease)); + adlibWrite(channel + 0xE0, instr->carWaveformSelect); - adlib_write((byte)chan + 0xC0, instr->feedback); + adlibWrite((byte)chan + 0xC0, instr->feedback +#ifdef ENABLE_OPL3 + | (_opl3Mode ? 0x30 : 0) +#endif + ); } -void MidiDriver_ADLIB::adlib_note_on_ex(int chan, byte note, int mod) { - int code; +#ifdef ENABLE_OPL3 +void MidiDriver_ADLIB::adlibSetupChannelSecondary(int chan, const AdLibInstrument *instr, byte vol1, byte vol2, byte pan) { assert(chan >= 0 && chan < 9); - code = (note << 7) + mod; - curnote_table[chan] = code; - channel_table_2[chan] = 0; - adlib_playnote(chan, code); -} + assert(_opl3Mode); -void MidiDriver_ADLIB::mc_init_stuff(AdLibVoice *voice, Struct10 *s10, - Struct11 *s11, byte flags, InstrumentExtra *ie) { + byte channel = g_operator1Offsets[chan]; + adlibWriteSecondary(channel + 0x20, instr->modCharacteristic); + adlibWriteSecondary(channel + 0x40, (instr->modScalingOutputLevel | 0x3F) - vol1); + adlibWriteSecondary(channel + 0x60, 0xff & (~instr->modAttackDecay)); + adlibWriteSecondary(channel + 0x80, 0xff & (~instr->modSustainRelease)); + adlibWriteSecondary(channel + 0xE0, instr->modWaveformSelect); + + channel = g_operator2Offsets[chan]; + adlibWriteSecondary(channel + 0x20, instr->carCharacteristic); + adlibWriteSecondary(channel + 0x40, (instr->carScalingOutputLevel | 0x3F) - vol2); + adlibWriteSecondary(channel + 0x60, 0xff & (~instr->carAttackDecay)); + adlibWriteSecondary(channel + 0x80, 0xff & (~instr->carSustainRelease)); + adlibWriteSecondary(channel + 0xE0, instr->carWaveformSelect); + + // The original uses the following (strange) behavior: +#if 0 + if (instr->feedback | (pan > 64)) { + adlibWriteSecondary((byte)chan + 0xC0, 0x20); + } else { + adlibWriteSecondary((byte)chan + 0xC0, 0x10); + } +#else + adlibWriteSecondary((byte)chan + 0xC0, instr->feedback | ((pan > 64) ? 0x20 : 0x10)); +#endif +} +#endif + +void MidiDriver_ADLIB::mcInitStuff(AdLibVoice *voice, Struct10 *s10, + Struct11 *s11, byte flags, const InstrumentExtra *ie) { AdLibPart *part = voice->_part; - s11->modify_val = 0; + s11->modifyVal = 0; s11->flag0x40 = flags & 0x40; s10->loop = flags & 0x20; s11->flag0x10 = flags & 0x10; - s11->param = param_table_1[flags & 0xF]; - s10->max_value = maxval_table[flags & 0xF]; + s11->param = g_paramTable1[flags & 0xF]; + s10->maxValue = g_maxValTable[flags & 0xF]; s10->unk3 = 31; if (s11->flag0x40) { - s10->modwheel = part->_modwheel >> 2; + s10->modWheel = part->_modWheel >> 2; } else { - s10->modwheel = 31; + s10->modWheel = 31; } switch (s11->param) { case 0: - s10->start_value = voice->_vol_2; + s10->startValue = voice->_vol2; break; case 13: - s10->start_value = voice->_vol_1; + s10->startValue = voice->_vol1; break; case 30: - s10->start_value = 31; - s11->s10->modwheel = 0; + s10->startValue = 31; + s11->s10->modWheel = 0; break; case 31: - s10->start_value = 0; + s10->startValue = 0; s11->s10->unk3 = 0; break; default: - s10->start_value = adlib_get_reg_value_param(voice->_channel, s11->param); + s10->startValue = adlibGetRegValueParam(voice->_channel, s11->param); } - struct10_init(s10, ie); + struct10Init(s10, ie); } -void MidiDriver_ADLIB::struct10_init(Struct10 *s10, InstrumentExtra *ie) { +void MidiDriver_ADLIB::struct10Init(Struct10 *s10, const InstrumentExtra *ie) { s10->active = 1; if (!_scummSmallHeader) { - s10->cur_val = 0; + s10->curVal = 0; } else { - s10->cur_val = s10->start_value; - s10->start_value = 0; + s10->curVal = s10->startValue; + s10->startValue = 0; } - s10->modwheel_last = 31; + s10->modWheelLast = 31; s10->count = ie->a; if (s10->count) s10->count *= 63; - s10->table_a[0] = ie->b; - s10->table_a[1] = ie->d; - s10->table_a[2] = ie->f; - s10->table_a[3] = ie->g; + s10->tableA[0] = ie->b; + s10->tableA[1] = ie->d; + s10->tableA[2] = ie->f; + s10->tableA[3] = ie->g; - s10->table_b[0] = ie->c; - s10->table_b[1] = ie->e; - s10->table_b[2] = 0; - s10->table_b[3] = ie->h; + s10->tableB[0] = ie->c; + s10->tableB[1] = ie->e; + s10->tableB[2] = 0; + s10->tableB[3] = ie->h; - struct10_setup(s10); + struct10Setup(s10); } -int MidiDriver_ADLIB::adlib_get_reg_value_param(int chan, byte param) { +int MidiDriver_ADLIB::adlibGetRegValueParam(int chan, byte param) { const AdLibSetParams *as; byte val; byte channel; @@ -1551,10 +2214,10 @@ int MidiDriver_ADLIB::adlib_get_reg_value_param(int chan, byte param) { assert(chan >= 0 && chan < 9); if (param <= 12) { - channel = channel_mappings_2[chan]; + channel = g_operator2Offsets[chan]; } else if (param <= 25) { param -= 13; - channel = channel_mappings[chan]; + channel = g_operator1Offsets[chan]; } else if (param <= 27) { param -= 13; channel = chan; @@ -1566,24 +2229,52 @@ int MidiDriver_ADLIB::adlib_get_reg_value_param(int chan, byte param) { return 0; } - as = &adlib_setparam_table[param]; - val = adlib_get_reg_value(channel + as->a); - val &= as->c; - val >>= as->b; - if (as->d) - val = as->d - val; + as = &g_setParamTable[param]; + val = adlibGetRegValue(channel + as->registerBase); + val &= as->mask; + val >>= as->shift; + if (as->inversion) + val = as->inversion - val; return val; } -void MidiDriver_ADLIB::adlib_note_on(int chan, byte note, int mod) { - int code; +void MidiDriver_ADLIB::adlibNoteOn(int chan, byte note, int mod) { +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + adlibNoteOnEx(chan, note, mod); + return; + } +#endif + assert(chan >= 0 && chan < 9); - code = (note << 7) + mod; - curnote_table[chan] = code; - adlib_playnote(chan, (int16) channel_table_2[chan] + code); + int code = (note << 7) + mod; + _curNotTable[chan] = code; + adlibPlayNote(chan, (int16)_channelTable2[chan] + code); } +void MidiDriver_ADLIB::adlibNoteOnEx(int chan, byte note, int mod) { + assert(chan >= 0 && chan < 9); + +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + const int noteAdjusted = note + (mod >> 8) - 7; + const int pitchAdjust = (mod >> 5) & 7; + + adlibWrite(0xA0 + chan, g_noteFrequencies[(noteAdjusted % 12) * 8 + pitchAdjust + 6 * 8]); + adlibWriteSecondary(0xA0 + chan, g_noteFrequencies[(noteAdjusted % 12) * 8 + pitchAdjust + 6 * 8]); + adlibWrite(0xB0 + chan, (CLIP(noteAdjusted / 12, 0, 7) << 2) | 0x20); + adlibWriteSecondary(0xB0 + chan, (CLIP(noteAdjusted / 12, 0, 7) << 2) | 0x20); + } else { +#endif + int code = (note << 7) + mod; + _curNotTable[chan] = code; + _channelTable2[chan] = 0; + adlibPlayNote(chan, code); +#ifdef ENABLE_OPL3 + } +#endif +} // Plugin interface diff --git a/audio/softsynth/fluidsynth.cpp b/audio/softsynth/fluidsynth.cpp index 2451336784d..518e260175a 100644 --- a/audio/softsynth/fluidsynth.cpp +++ b/audio/softsynth/fluidsynth.cpp @@ -127,12 +127,54 @@ int MidiDriver_FluidSynth::open() { _synth = new_fluid_synth(_settings); - // In theory, this ought to reduce CPU load... but it doesn't make any - // noticeable difference for me, so disable it for now. + if (ConfMan.getBool("fluidsynth_chorus_activate")) { + fluid_synth_set_chorus_on(_synth, 1); - // fluid_synth_set_interp_method(_synth, -1, FLUID_INTERP_LINEAR); - // fluid_synth_set_reverb_on(_synth, 0); - // fluid_synth_set_chorus_on(_synth, 0); + int chorusNr = ConfMan.getInt("fluidsynth_chorus_nr"); + double chorusLevel = (double)ConfMan.getInt("fluidsynth_chorus_level") / 100.0; + double chorusSpeed = (double)ConfMan.getInt("fluidsynth_chorus_speed") / 100.0; + double chorusDepthMs = (double)ConfMan.getInt("fluidsynth_chorus_depth") / 10.0; + + Common::String chorusWaveForm = ConfMan.get("fluidsynth_chorus_waveform"); + int chorusType = FLUID_CHORUS_MOD_SINE; + if (chorusWaveForm == "sine") { + chorusType = FLUID_CHORUS_MOD_SINE; + } else { + chorusType = FLUID_CHORUS_MOD_TRIANGLE; + } + + fluid_synth_set_chorus(_synth, chorusNr, chorusLevel, chorusSpeed, chorusDepthMs, chorusType); + } else { + fluid_synth_set_chorus_on(_synth, 0); + } + + if (ConfMan.getBool("fluidsynth_reverb_activate")) { + fluid_synth_set_reverb_on(_synth, 1); + + double reverbRoomSize = (double)ConfMan.getInt("fluidsynth_reverb_roomsize") / 100.0; + double reverbDamping = (double)ConfMan.getInt("fluidsynth_reverb_damping") / 100.0; + int reverbWidth = ConfMan.getInt("fluidsynth_reverb_width"); + double reverbLevel = (double)ConfMan.getInt("fluidsynth_reverb_level") / 100.0; + + fluid_synth_set_reverb(_synth, reverbRoomSize, reverbDamping, reverbWidth, reverbLevel); + } else { + fluid_synth_set_reverb_on(_synth, 0); + } + + Common::String interpolation = ConfMan.get("fluidsynth_misc_interpolation"); + int interpMethod = FLUID_INTERP_4THORDER; + + if (interpolation == "none") { + interpMethod = FLUID_INTERP_NONE; + } else if (interpolation == "linear") { + interpMethod = FLUID_INTERP_LINEAR; + } else if (interpolation == "4th") { + interpMethod = FLUID_INTERP_4THORDER; + } else if (interpolation == "7th") { + interpMethod = FLUID_INTERP_7THORDER; + } + + fluid_synth_set_interp_method(_synth, -1, interpMethod); const char *soundfont = ConfMan.get("soundfont").c_str(); diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp index 6a4db75c5d8..d538f30dd1c 100644 --- a/audio/softsynth/mt32.cpp +++ b/audio/softsynth/mt32.cpp @@ -25,6 +25,7 @@ #ifdef USE_MT32EMU #include "audio/softsynth/mt32/mt32emu.h" +#include "audio/softsynth/mt32/ROMInfo.h" #include "audio/softsynth/emumidi.h" #include "audio/musicplugin.h" @@ -47,6 +48,51 @@ #include "graphics/palette.h" #include "graphics/font.h" +#include "gui/message.h" + +namespace MT32Emu { + +class ReportHandlerScummVM : public ReportHandler { +friend class Synth; + +public: + virtual ~ReportHandlerScummVM() {} + +protected: + + // Callback for debug messages, in vprintf() format + void printDebug(const char *fmt, va_list list) { + Common::String out = Common::String::vformat(fmt, list); + debug(4, "%s", out.c_str()); + } + + // Callbacks for reporting various errors and information + void onErrorControlROM() { + GUI::MessageDialog dialog("MT32emu: Init Error - Missing or invalid Control ROM image", "OK"); + dialog.runModal(); + error("MT32emu: Init Error - Missing or invalid Control ROM image"); + } + void onErrorPCMROM() { + GUI::MessageDialog dialog("MT32emu: Init Error - Missing PCM ROM image", "OK"); + dialog.runModal(); + error("MT32emu: Init Error - Missing PCM ROM image"); + } + void showLCDMessage(const char *message) { + g_system->displayMessageOnOSD(message); + } + void onDeviceReset() {} + void onDeviceReconfig() {} + void onNewReverbMode(Bit8u /* mode */) {} + void onNewReverbTime(Bit8u /* time */) {} + void onNewReverbLevel(Bit8u /* level */) {} + void onPartStateChanged(int /* partNum */, bool /* isActive */) {} + void onPolyStateChanged(int /* partNum */) {} + void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {} + void onProgramChanged(int /* partNum */, char * /* patchName */) {} +}; + +} // end of namespace MT32Emu + class MidiChannel_MT32 : public MidiChannel_MPU401 { void effectLevel(byte value) { } void chorusLevel(byte value) { } @@ -57,6 +103,10 @@ private: MidiChannel_MT32 _midiChannels[16]; uint16 _channelMask; MT32Emu::Synth *_synth; + MT32Emu::ReportHandlerScummVM *_reportHandler; + const MT32Emu::ROMImage *_controlROM, *_pcmROM; + Common::File *_controlFile, *_pcmFile; + void deleteMuntStructures(); int _outputRate; @@ -84,149 +134,6 @@ public: int getRate() const { return _outputRate; } }; -static int eatSystemEvents() { - Common::Event event; - Common::EventManager *eventMan = g_system->getEventManager(); - while (eventMan->pollEvent(event)) { - switch (event.type) { - case Common::EVENT_QUIT: - return 1; - default: - break; - } - } - return 0; -} - -static void drawProgress(float progress) { - const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kGUIFont)); - Graphics::Surface *screen = g_system->lockScreen(); - - assert(screen); - assert(screen->pixels); - - Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); - - int16 w = g_system->getWidth() / 7 * 5; - int16 h = font.getFontHeight(); - int16 x = g_system->getWidth() / 7; - int16 y = g_system->getHeight() / 2 - h / 2; - - Common::Rect r(x, y, x + w, y + h); - - uint32 col; - - if (screenFormat.bytesPerPixel > 1) - col = screenFormat.RGBToColor(0, 171, 0); - else - col = 1; - - screen->frameRect(r, col); - - r.grow(-1); - r.setWidth(uint16(progress * w)); - - if (screenFormat.bytesPerPixel > 1) - col = screenFormat.RGBToColor(171, 0, 0); - else - col = 2; - - screen->fillRect(r, col); - - g_system->unlockScreen(); - g_system->updateScreen(); -} - -static void drawMessage(int offset, const Common::String &text) { - const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kGUIFont)); - Graphics::Surface *screen = g_system->lockScreen(); - - assert(screen); - assert(screen->pixels); - - Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); - - uint16 h = font.getFontHeight(); - uint16 y = g_system->getHeight() / 2 - h / 2 + offset * (h + 1); - - uint32 col; - - if (screenFormat.bytesPerPixel > 1) - col = screenFormat.RGBToColor(0, 0, 0); - else - col = 0; - - Common::Rect r(0, y, screen->w, y + h); - screen->fillRect(r, col); - - if (screenFormat.bytesPerPixel > 1) - col = screenFormat.RGBToColor(0, 171, 0); - else - col = 1; - - font.drawString(screen, text, 0, y, screen->w, col, Graphics::kTextAlignCenter); - - g_system->unlockScreen(); - g_system->updateScreen(); -} - -static Common::File *MT32_OpenFile(void *userData, const char *filename) { - Common::File *file = new Common::File(); - if (!file->open(filename)) { - delete file; - return NULL; - } - return file; -} - -static void MT32_PrintDebug(void *userData, const char *fmt, va_list list) { - if (((MidiDriver_MT32 *)userData)->_initializing) { - char buf[512]; - - vsnprintf(buf, 512, fmt, list); - buf[70] = 0; // Truncate to a reasonable length - - drawMessage(1, buf); - } - - //vdebug(0, fmt, list); // FIXME: Use a higher debug level -} - -static int MT32_Report(void *userData, MT32Emu::ReportType type, const void *reportData) { - switch (type) { - case MT32Emu::ReportType_lcdMessage: - g_system->displayMessageOnOSD((const char *)reportData); - break; - case MT32Emu::ReportType_errorControlROM: - error("Failed to load MT32_CONTROL.ROM"); - break; - case MT32Emu::ReportType_errorPCMROM: - error("Failed to load MT32_PCM.ROM"); - break; - case MT32Emu::ReportType_progressInit: - if (((MidiDriver_MT32 *)userData)->_initializing) { - drawProgress(*((const float *)reportData)); - return eatSystemEvents(); - } - break; - case MT32Emu::ReportType_availableSSE: - debug(1, "MT32emu: SSE is available"); - break; - case MT32Emu::ReportType_usingSSE: - debug(1, "MT32emu: using SSE"); - break; - case MT32Emu::ReportType_available3DNow: - debug(1, "MT32emu: 3DNow! is available"); - break; - case MT32Emu::ReportType_using3DNow: - debug(1, "MT32emu: using 3DNow!"); - break; - default: - break; - } - return 0; -} - //////////////////////////////////////// // // MidiDriver_MT32 @@ -239,43 +146,51 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) { _midiChannels[i].init(this, i); } + _reportHandler = NULL; _synth = NULL; - // A higher baseFreq reduces the length used in generateSamples(), - // and means that the timer callback will be called more often. - // That results in more accurate timing. - _baseFreq = 10000; // Unfortunately bugs in the emulator cause inaccurate tuning // at rates other than 32KHz, thus we produce data at 32KHz and // rely on Mixer to convert. _outputRate = 32000; //_mixer->getOutputRate(); _initializing = false; + + // Initialized in open() + _controlROM = NULL; + _pcmROM = NULL; + _controlFile = NULL; + _pcmFile = NULL; } MidiDriver_MT32::~MidiDriver_MT32() { + deleteMuntStructures(); +} + +void MidiDriver_MT32::deleteMuntStructures() { delete _synth; + _synth = NULL; + delete _reportHandler; + _reportHandler = NULL; + + if (_controlROM) + MT32Emu::ROMImage::freeROMImage(_controlROM); + _controlROM = NULL; + if (_pcmROM) + MT32Emu::ROMImage::freeROMImage(_pcmROM); + _pcmROM = NULL; + + delete _controlFile; + _controlFile = NULL; + delete _pcmFile; + _pcmFile = NULL; } int MidiDriver_MT32::open() { - MT32Emu::SynthProperties prop; - if (_isOpen) return MERR_ALREADY_OPEN; MidiDriver_Emulated::open(); - - memset(&prop, 0, sizeof(prop)); - prop.sampleRate = getRate(); - prop.useReverb = true; - prop.useDefaultReverb = false; - prop.reverbType = 0; - prop.reverbTime = 5; - prop.reverbLevel = 3; - prop.userData = this; - prop.printDebug = MT32_PrintDebug; - prop.report = MT32_Report; - prop.openFile = MT32_OpenFile; - - _synth = new MT32Emu::Synth(); + _reportHandler = new MT32Emu::ReportHandlerScummVM(); + _synth = new MT32Emu::Synth(_reportHandler); Graphics::PixelFormat screenFormat = g_system->getScreenFormat(); @@ -290,8 +205,16 @@ int MidiDriver_MT32::open() { } _initializing = true; - drawMessage(-1, _s("Initializing MT-32 Emulator")); - if (!_synth->open(prop)) + debug(4, _s("Initializing MT-32 Emulator")); + _controlFile = new Common::File(); + if (!_controlFile->open("MT32_CONTROL.ROM") && !_controlFile->open("CM32L_CONTROL.ROM")) + error("Error opening MT32_CONTROL.ROM / CM32L_CONTROL.ROM"); + _pcmFile = new Common::File(); + if (!_pcmFile->open("MT32_PCM.ROM") && !_pcmFile->open("CM32L_PCM.ROM")) + error("Error opening MT32_PCM.ROM / CM32L_PCM.ROM"); + _controlROM = MT32Emu::ROMImage::makeROMImage(_controlFile); + _pcmROM = MT32Emu::ROMImage::makeROMImage(_pcmFile); + if (!_synth->open(*_controlROM, *_pcmROM)) return MERR_DEVICE_NOT_AVAILABLE; double gain = (double)ConfMan.getInt("midi_gain") / 100.0; @@ -352,8 +275,7 @@ void MidiDriver_MT32::close() { _mixer->stopHandle(_mixerSoundHandle); _synth->close(); - delete _synth; - _synth = NULL; + deleteMuntStructures(); } void MidiDriver_MT32::generateSamples(int16 *data, int len) { diff --git a/audio/softsynth/mt32/AReverbModel.cpp b/audio/softsynth/mt32/AReverbModel.cpp index 151f6c2c81c..1d638321575 100644 --- a/audio/softsynth/mt32/AReverbModel.cpp +++ b/audio/softsynth/mt32/AReverbModel.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -16,64 +16,97 @@ */ #include "mt32emu.h" + +#if MT32EMU_USE_REVERBMODEL == 1 + #include "AReverbModel.h" +// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that +// the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF) +// and followed by three parallel comb filters + namespace MT32Emu { -// Default reverb settings for modes 0-2 +// Because LA-32 chip makes it's output available to process by the Boss chip with a significant delay, +// the Boss chip puts to the buffer the LA32 dry output when it is ready and performs processing of the _previously_ latched data. +// Of course, the right way would be to use a dedicated variable for this, but our reverb model is way higher level, +// so we can simply increase the input buffer size. +static const Bit32u PROCESS_DELAY = 1; -static const unsigned int NUM_ALLPASSES = 6; -static const unsigned int NUM_DELAYS = 5; +// Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different. +// Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog). -static const Bit32u MODE_0_ALLPASSES[] = {729, 78, 394, 994, 1250, 1889}; -static const Bit32u MODE_0_DELAYS[] = {846, 4, 1819, 778, 346}; -static const float MODE_0_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.9f}; -static const float MODE_0_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 1.01575f}; +static const Bit32u NUM_ALLPASSES = 3; +static const Bit32u NUM_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be perfectly processed via a comb here. -static const Bit32u MODE_1_ALLPASSES[] = {176, 809, 1324, 1258}; -static const Bit32u MODE_1_DELAYS[] = {2262, 124, 974, 2516, 356}; -static const float MODE_1_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.95f}; -static const float MODE_1_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 1.01575f}; +static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78}; +static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; +static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; +static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; +static const Bit32u MODE_0_COMB_FACTOR[] = {0x3C, 0x60, 0x60, 0x60}; +static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_0_LEVELS[] = {10*1, 10*3, 10*5, 10*7, 11*9, 11*12, 11*15, 13*15}; +static const Bit32u MODE_0_LPF_AMP = 6; -static const Bit32u MODE_2_ALLPASSES[] = {78, 729, 994, 389}; -static const Bit32u MODE_2_DELAYS[] = {846, 4, 1819, 778, 346}; -static const float MODE_2_TIMES[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f}; -static const float MODE_2_LEVELS[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f}; +static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; +static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; +static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; +static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; +static const Bit32u MODE_1_COMB_FACTOR[] = {0x30, 0x60, 0x60, 0x60}; +static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_1_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 11*15, 14*15}; +static const Bit32u MODE_1_LPF_AMP = 6; -const AReverbSettings AReverbModel::REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_DELAYS, MODE_0_TIMES, MODE_0_LEVELS, 0.687770909f, 0.5f, 0.5f}; -const AReverbSettings AReverbModel::REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_DELAYS, MODE_1_TIMES, MODE_1_LEVELS, 0.712025098f, 0.375f, 0.625f}; -const AReverbSettings AReverbModel::REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_DELAYS, MODE_2_TIMES, MODE_2_LEVELS, 0.939522749f, 0.0f, 0.0f}; +static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; +static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; +static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; +static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; +static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; +static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; +static const Bit32u MODE_2_LEVELS[] = {10*1, 10*3, 11*5, 11*7, 11*9, 11*12, 12*15, 14*15}; +static const Bit32u MODE_2_LPF_AMP = 8; -RingBuffer::RingBuffer(Bit32u newsize) { - index = 0; - size = newsize; +static const AReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_ALLPASSES, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_LEVELS, MODE_0_LPF_AMP}; +static const AReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_ALLPASSES, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_LEVELS, MODE_1_LPF_AMP}; +static const AReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_ALLPASSES, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_LEVELS, MODE_2_LPF_AMP}; + +static const AReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_0_SETTINGS}; + +RingBuffer::RingBuffer(const Bit32u newsize) : size(newsize), index(0) { buffer = new float[size]; } RingBuffer::~RingBuffer() { delete[] buffer; buffer = NULL; - size = 0; } float RingBuffer::next() { - index++; - if (index >= size) { + if (++index >= size) { index = 0; } return buffer[index]; } -bool RingBuffer::isEmpty() { +bool RingBuffer::isEmpty() const { if (buffer == NULL) return true; float *buf = buffer; - float total = 0; + float max = 0.001f; for (Bit32u i = 0; i < size; i++) { - total += (*buf < 0 ? -*buf : *buf); + if ((*buf < -max) || (*buf > max)) return false; buf++; } - return ((total / size) < .0002 ? true : false); + return true; } void RingBuffer::mute() { @@ -83,59 +116,66 @@ void RingBuffer::mute() { } } -AllpassFilter::AllpassFilter(Bit32u useSize) : RingBuffer(useSize) { -} +AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {} -Delay::Delay(Bit32u useSize) : RingBuffer(useSize) { -} - -float AllpassFilter::process(float in) { - // This model corresponds to the allpass filter implementation in the real CM-32L device +float AllpassFilter::process(const float in) { + // This model corresponds to the allpass filter implementation of the real CM-32L device // found from sample analysis - float out; - - out = next(); + const float bufferOut = next(); // store input - feedback / 2 - buffer[index] = in - 0.5f * out; + buffer[index] = in - 0.5f * bufferOut; // return buffer output + feedforward / 2 - return out + 0.5f * buffer[index]; + return bufferOut + 0.5f * buffer[index]; } -float Delay::process(float in) { - // Implements a very simple delay +CombFilter::CombFilter(const Bit32u useSize) : RingBuffer(useSize) {} - float out; +void CombFilter::process(const float in) { + // This model corresponds to the comb filter implementation of the real CM-32L device + // found from sample analysis - out = next(); + // the previously stored value + float last = buffer[index]; - // store input - buffer[index] = in; + // prepare input + feedback + float filterIn = in + next() * feedbackFactor; - // return buffer output - return out; + // store input + feedback processed by a low-pass filter + buffer[index] = filterFactor * last - filterIn; } -AReverbModel::AReverbModel(const AReverbSettings *useSettings) : allpasses(NULL), delays(NULL), currentSettings(useSettings) { +float CombFilter::getOutputAt(const Bit32u outIndex) const { + return buffer[(size + index - outIndex) % size]; } +void CombFilter::setFeedbackFactor(const float useFeedbackFactor) { + feedbackFactor = useFeedbackFactor; +} + +void CombFilter::setFilterFactor(const float useFilterFactor) { + filterFactor = useFilterFactor; +} + +AReverbModel::AReverbModel(const ReverbMode mode) : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]) {} + AReverbModel::~AReverbModel() { close(); } -void AReverbModel::open(unsigned int /*sampleRate*/) { - // FIXME: filter sizes must be multiplied by sample rate to 32000Hz ratio - // IIR filter values depend on sample rate as well +void AReverbModel::open() { allpasses = new AllpassFilter*[NUM_ALLPASSES]; for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { - allpasses[i] = new AllpassFilter(currentSettings->allpassSizes[i]); + allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]); } - delays = new Delay*[NUM_DELAYS]; - for (Bit32u i = 0; i < NUM_DELAYS; i++) { - delays[i] = new Delay(currentSettings->delaySizes[i]); + combs = new CombFilter*[NUM_COMBS]; + for (Bit32u i = 0; i < NUM_COMBS; i++) { + combs[i] = new CombFilter(currentSettings.combSizes[i]); + combs[i]->setFilterFactor(currentSettings.filterFactor[i] / 256.0f); } + lpfAmp = currentSettings.lpfAmp / 16.0f; mute(); } @@ -150,84 +190,80 @@ void AReverbModel::close() { delete[] allpasses; allpasses = NULL; } - if (delays != NULL) { - for (Bit32u i = 0; i < NUM_DELAYS; i++) { - if (delays[i] != NULL) { - delete delays[i]; - delays[i] = NULL; + if (combs != NULL) { + for (Bit32u i = 0; i < NUM_COMBS; i++) { + if (combs[i] != NULL) { + delete combs[i]; + combs[i] = NULL; } } - delete[] delays; - delays = NULL; + delete[] combs; + combs = NULL; } } void AReverbModel::mute() { + if (allpasses == NULL || combs == NULL) return; for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { allpasses[i]->mute(); } - for (Bit32u i = 0; i < NUM_DELAYS; i++) { - delays[i]->mute(); + for (Bit32u i = 0; i < NUM_COMBS; i++) { + combs[i]->mute(); } - filterhist1 = 0; - filterhist2 = 0; - combhist = 0; } void AReverbModel::setParameters(Bit8u time, Bit8u level) { // FIXME: wetLevel definitely needs ramping when changed // Although, most games don't set reverb level during MIDI playback - decayTime = currentSettings->decayTimes[time]; - wetLevel = currentSettings->wetLevels[level]; + if (combs == NULL) return; + level &= 7; + time &= 7; + for (Bit32u i = 0; i < NUM_COMBS; i++) { + combs[i]->setFeedbackFactor(currentSettings.decayTimes[(i << 3) + time] / 256.0f); + } + wetLevel = (level == 0 && time == 0) ? 0.0f : 0.5f * lpfAmp * currentSettings.wetLevels[level] / 256.0f; } bool AReverbModel::isActive() const { - bool bActive = false; for (Bit32u i = 0; i < NUM_ALLPASSES; i++) { - bActive |= !allpasses[i]->isEmpty(); + if (!allpasses[i]->isEmpty()) return true; } - for (Bit32u i = 0; i < NUM_DELAYS; i++) { - bActive |= !delays[i]->isEmpty(); + for (Bit32u i = 0; i < NUM_COMBS; i++) { + if (!combs[i]->isEmpty()) return true; } - return bActive; + return false; } void AReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { -// Three series allpass filters followed by a delay, fourth allpass filter and another delay - float dry, link, outL1, outL2, outR1, outR2; + float dry, link, outL1; for (unsigned long i = 0; i < numSamples; i++) { - dry = *inLeft + *inRight; + dry = wetLevel * (*inLeft + *inRight); - // Implementation of 2-stage IIR single-pole low-pass filter - // found at the entrance of reverb processing on real devices - filterhist1 += (dry - filterhist1) * currentSettings->filtVal; - filterhist2 += (filterhist1 - filterhist2) * currentSettings->filtVal; + // Get the last stored sample before processing in order not to loose it + link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1); - link = allpasses[0]->process(-filterhist2); + combs[0]->process(-dry); + + link = allpasses[0]->process(link); link = allpasses[1]->process(link); - - // this implements a comb filter cross-linked with the fourth allpass filter - link += combhist * decayTime; link = allpasses[2]->process(link); - link = delays[0]->process(link); - outL1 = link; - link = allpasses[3]->process(link); - link = delays[1]->process(link); - outR1 = link; - link = allpasses[4]->process(link); - link = delays[2]->process(link); - outL2 = link; - link = allpasses[5]->process(link); - link = delays[3]->process(link); - outR2 = link; - link = delays[4]->process(link); - // comb filter end point - combhist = combhist * currentSettings->damp1 + link * currentSettings->damp2; + // If the output position is equal to the comb size, get it now in order not to loose it + outL1 = 1.5f * combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1); - *outLeft = (outL1 + outL2) * wetLevel; - *outRight = (outR1 + outR2) * wetLevel; + combs[1]->process(link); + combs[2]->process(link); + combs[3]->process(link); + + link = outL1 + 1.5f * combs[2]->getOutputAt(currentSettings.outLPositions[1]); + link += combs[3]->getOutputAt(currentSettings.outLPositions[2]); + *outLeft = link; + + link = 1.5f * combs[1]->getOutputAt(currentSettings.outRPositions[0]); + link += 1.5f * combs[2]->getOutputAt(currentSettings.outRPositions[1]); + link += combs[3]->getOutputAt(currentSettings.outRPositions[2]); + *outRight = link; inLeft++; inRight++; @@ -237,3 +273,5 @@ void AReverbModel::process(const float *inLeft, const float *inRight, float *out } } + +#endif diff --git a/audio/softsynth/mt32/AReverbModel.h b/audio/softsynth/mt32/AReverbModel.h index 3fae08c34c4..c992478907b 100644 --- a/audio/softsynth/mt32/AReverbModel.h +++ b/audio/softsynth/mt32/AReverbModel.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -21,66 +21,67 @@ namespace MT32Emu { struct AReverbSettings { - const Bit32u *allpassSizes; - const Bit32u *delaySizes; - const float *decayTimes; - const float *wetLevels; - float filtVal; - float damp1; - float damp2; + const Bit32u * const allpassSizes; + const Bit32u * const combSizes; + const Bit32u * const outLPositions; + const Bit32u * const outRPositions; + const Bit32u * const filterFactor; + const Bit32u * const decayTimes; + const Bit32u * const wetLevels; + const Bit32u lpfAmp; }; class RingBuffer { protected: float *buffer; - Bit32u size; + const Bit32u size; Bit32u index; + public: - RingBuffer(Bit32u size); + RingBuffer(const Bit32u size); virtual ~RingBuffer(); float next(); - bool isEmpty(); + bool isEmpty() const; void mute(); }; class AllpassFilter : public RingBuffer { public: - AllpassFilter(Bit32u size); - float process(float in); + AllpassFilter(const Bit32u size); + float process(const float in); }; -class Delay : public RingBuffer { +class CombFilter : public RingBuffer { + float feedbackFactor; + float filterFactor; + public: - Delay(Bit32u size); - float process(float in); + CombFilter(const Bit32u size); + void process(const float in); + float getOutputAt(const Bit32u outIndex) const; + void setFeedbackFactor(const float useFeedbackFactor); + void setFilterFactor(const float useFilterFactor); }; class AReverbModel : public ReverbModel { AllpassFilter **allpasses; - Delay **delays; + CombFilter **combs; - const AReverbSettings *currentSettings; - float decayTime; + const AReverbSettings ¤tSettings; + float lpfAmp; float wetLevel; - float filterhist1, filterhist2; - float combhist; void mute(); + public: - AReverbModel(const AReverbSettings *newSettings); + AReverbModel(const ReverbMode mode); ~AReverbModel(); - void open(unsigned int sampleRate); + void open(); void close(); void setParameters(Bit8u time, Bit8u level); void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); bool isActive() const; - - static const AReverbSettings REVERB_MODE_0_SETTINGS; - static const AReverbSettings REVERB_MODE_1_SETTINGS; - static const AReverbSettings REVERB_MODE_2_SETTINGS; }; -// Default reverb settings for modes 0-2 - } #endif diff --git a/audio/softsynth/mt32/BReverbModel.cpp b/audio/softsynth/mt32/BReverbModel.cpp new file mode 100644 index 00000000000..cc0219b741a --- /dev/null +++ b/audio/softsynth/mt32/BReverbModel.cpp @@ -0,0 +1,393 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "mt32emu.h" + +#if MT32EMU_USE_REVERBMODEL == 2 + +#include "BReverbModel.h" + +// Analysing of state of reverb RAM address lines gives exact sizes of the buffers of filters used. This also indicates that +// the reverb model implemented in the real devices consists of three series allpass filters preceded by a non-feedback comb (or a delay with a LPF) +// and followed by three parallel comb filters + +namespace MT32Emu { + +// Because LA-32 chip makes it's output available to process by the Boss chip with a significant delay, +// the Boss chip puts to the buffer the LA32 dry output when it is ready and performs processing of the _previously_ latched data. +// Of course, the right way would be to use a dedicated variable for this, but our reverb model is way higher level, +// so we can simply increase the input buffer size. +static const Bit32u PROCESS_DELAY = 1; + +static const Bit32u MODE_3_ADDITIONAL_DELAY = 1; +static const Bit32u MODE_3_FEEDBACK_DELAY = 1; + +// Default reverb settings for modes 0-2. These correspond to CM-32L / LAPC-I "new" reverb settings. MT-32 reverb is a bit different. +// Found by tracing reverb RAM data lines (thanks go to Lord_Nightmare & balrog). + +static const Bit32u MODE_0_NUMBER_OF_ALLPASSES = 3; +static const Bit32u MODE_0_ALLPASSES[] = {994, 729, 78}; +static const Bit32u MODE_0_NUMBER_OF_COMBS = 4; // Well, actually there are 3 comb filters, but the entrance LPF + delay can be processed via a hacked comb. +static const Bit32u MODE_0_COMBS[] = {705 + PROCESS_DELAY, 2349, 2839, 3632}; +static const Bit32u MODE_0_OUTL[] = {2349, 141, 1960}; +static const Bit32u MODE_0_OUTR[] = {1174, 1570, 145}; +static const Bit32u MODE_0_COMB_FACTOR[] = {0xA0, 0x60, 0x60, 0x60}; +static const Bit32u MODE_0_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_0_DRY_AMP[] = {0xA0, 0xA0, 0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xD0}; +static const Bit32u MODE_0_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; +static const Bit32u MODE_0_LPF_AMP = 0x60; + +static const Bit32u MODE_1_NUMBER_OF_ALLPASSES = 3; +static const Bit32u MODE_1_ALLPASSES[] = {1324, 809, 176}; +static const Bit32u MODE_1_NUMBER_OF_COMBS = 4; // Same as for mode 0 above +static const Bit32u MODE_1_COMBS[] = {961 + PROCESS_DELAY, 2619, 3545, 4519}; +static const Bit32u MODE_1_OUTL[] = {2618, 1760, 4518}; +static const Bit32u MODE_1_OUTR[] = {1300, 3532, 2274}; +static const Bit32u MODE_1_COMB_FACTOR[] = {0x80, 0x60, 0x60, 0x60}; +static const Bit32u MODE_1_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x48, 0x60, 0x70, 0x78, 0x80, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98, + 0x28, 0x48, 0x60, 0x78, 0x80, 0x88, 0x90, 0x98}; +static const Bit32u MODE_1_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xE0}; +static const Bit32u MODE_1_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; +static const Bit32u MODE_1_LPF_AMP = 0x60; + +static const Bit32u MODE_2_NUMBER_OF_ALLPASSES = 3; +static const Bit32u MODE_2_ALLPASSES[] = {969, 644, 157}; +static const Bit32u MODE_2_NUMBER_OF_COMBS = 4; // Same as for mode 0 above +static const Bit32u MODE_2_COMBS[] = {116 + PROCESS_DELAY, 2259, 2839, 3539}; +static const Bit32u MODE_2_OUTL[] = {2259, 718, 1769}; +static const Bit32u MODE_2_OUTR[] = {1136, 2128, 1}; +static const Bit32u MODE_2_COMB_FACTOR[] = {0, 0x20, 0x20, 0x20}; +static const Bit32u MODE_2_COMB_FEEDBACK[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0, + 0x30, 0x58, 0x78, 0x88, 0xA0, 0xB8, 0xC0, 0xD0}; +static const Bit32u MODE_2_DRY_AMP[] = {0xA0, 0xA0, 0xB0, 0xB0, 0xB0, 0xB0, 0xC0, 0xE0}; +static const Bit32u MODE_2_WET_AMP[] = {0x10, 0x30, 0x50, 0x70, 0x90, 0xC0, 0xF0, 0xF0}; +static const Bit32u MODE_2_LPF_AMP = 0x80; + +static const Bit32u MODE_3_NUMBER_OF_ALLPASSES = 0; +static const Bit32u MODE_3_NUMBER_OF_COMBS = 1; +static const Bit32u MODE_3_DELAY[] = {16000 + MODE_3_FEEDBACK_DELAY + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY}; +static const Bit32u MODE_3_OUTL[] = {400, 624, 960, 1488, 2256, 3472, 5280, 8000}; +static const Bit32u MODE_3_OUTR[] = {800, 1248, 1920, 2976, 4512, 6944, 10560, 16000}; +static const Bit32u MODE_3_COMB_FACTOR[] = {0x68}; +static const Bit32u MODE_3_COMB_FEEDBACK[] = {0x68, 0x60}; +static const Bit32u MODE_3_DRY_AMP[] = {0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50}; +static const Bit32u MODE_3_WET_AMP[] = {0x18, 0x18, 0x28, 0x40, 0x60, 0x80, 0xA8, 0xF8}; + +static const BReverbSettings REVERB_MODE_0_SETTINGS = {MODE_0_NUMBER_OF_ALLPASSES, MODE_0_ALLPASSES, MODE_0_NUMBER_OF_COMBS, MODE_0_COMBS, MODE_0_OUTL, MODE_0_OUTR, MODE_0_COMB_FACTOR, MODE_0_COMB_FEEDBACK, MODE_0_DRY_AMP, MODE_0_WET_AMP, MODE_0_LPF_AMP}; +static const BReverbSettings REVERB_MODE_1_SETTINGS = {MODE_1_NUMBER_OF_ALLPASSES, MODE_1_ALLPASSES, MODE_1_NUMBER_OF_COMBS, MODE_1_COMBS, MODE_1_OUTL, MODE_1_OUTR, MODE_1_COMB_FACTOR, MODE_1_COMB_FEEDBACK, MODE_1_DRY_AMP, MODE_1_WET_AMP, MODE_1_LPF_AMP}; +static const BReverbSettings REVERB_MODE_2_SETTINGS = {MODE_2_NUMBER_OF_ALLPASSES, MODE_2_ALLPASSES, MODE_2_NUMBER_OF_COMBS, MODE_2_COMBS, MODE_2_OUTL, MODE_2_OUTR, MODE_2_COMB_FACTOR, MODE_2_COMB_FEEDBACK, MODE_2_DRY_AMP, MODE_2_WET_AMP, MODE_2_LPF_AMP}; +static const BReverbSettings REVERB_MODE_3_SETTINGS = {MODE_3_NUMBER_OF_ALLPASSES, NULL, MODE_3_NUMBER_OF_COMBS, MODE_3_DELAY, MODE_3_OUTL, MODE_3_OUTR, MODE_3_COMB_FACTOR, MODE_3_COMB_FEEDBACK, MODE_3_DRY_AMP, MODE_3_WET_AMP, 0}; + +static const BReverbSettings * const REVERB_SETTINGS[] = {&REVERB_MODE_0_SETTINGS, &REVERB_MODE_1_SETTINGS, &REVERB_MODE_2_SETTINGS, &REVERB_MODE_3_SETTINGS}; + +// This algorithm tries to emulate exactly Boss multiplication operation (at least this is what we see on reverb RAM data lines). +// Also LA32 is suspected to use the similar one to perform PCM interpolation and ring modulation. +static Bit32s weirdMul(Bit32s a, Bit8u addMask, Bit8u carryMask) { + Bit8u mask = 0x80; + Bit32s res = 0; + for (int i = 0; i < 8; i++) { + Bit32s carry = (a < 0) && (mask & carryMask) > 0 ? a & 1 : 0; + a >>= 1; + res += (mask & addMask) > 0 ? a + carry : 0; + mask >>= 1; + } + return res; +} + +RingBuffer::RingBuffer(Bit32u newsize) : size(newsize), index(0) { + buffer = new Bit16s[size]; +} + +RingBuffer::~RingBuffer() { + delete[] buffer; + buffer = NULL; +} + +Bit32s RingBuffer::next() { + if (++index >= size) { + index = 0; + } + return buffer[index]; +} + +bool RingBuffer::isEmpty() const { + if (buffer == NULL) return true; + + Bit16s *buf = buffer; + for (Bit32u i = 0; i < size; i++) { + if (*buf < -8 || *buf > 8) return false; + buf++; + } + return true; +} + +void RingBuffer::mute() { + Bit16s *buf = buffer; + for (Bit32u i = 0; i < size; i++) { + *buf++ = 0; + } +} + +AllpassFilter::AllpassFilter(const Bit32u useSize) : RingBuffer(useSize) {} + +Bit32s AllpassFilter::process(const Bit32s in) { + // This model corresponds to the allpass filter implementation of the real CM-32L device + // found from sample analysis + + Bit16s bufferOut = next(); + + // store input - feedback / 2 + buffer[index] = in - (bufferOut >> 1); + + // return buffer output + feedforward / 2 + return bufferOut + (buffer[index] >> 1); +} + +CombFilter::CombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : RingBuffer(useSize), filterFactor(useFilterFactor) {} + +void CombFilter::process(const Bit32s in) { + // This model corresponds to the comb filter implementation of the real CM-32L device + + // the previously stored value + Bit32s last = buffer[index]; + + // prepare input + feedback + Bit32s filterIn = in + weirdMul(next(), feedbackFactor, 0xF0 /* Maybe 0x80 ? */); + + // store input + feedback processed by a low-pass filter + buffer[index] = weirdMul(last, filterFactor, 0x40) - filterIn; +} + +Bit32s CombFilter::getOutputAt(const Bit32u outIndex) const { + return buffer[(size + index - outIndex) % size]; +} + +void CombFilter::setFeedbackFactor(const Bit32u useFeedbackFactor) { + feedbackFactor = useFeedbackFactor; +} + +DelayWithLowPassFilter::DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp) + : CombFilter(useSize, useFilterFactor), amp(useAmp) {} + +void DelayWithLowPassFilter::process(const Bit32s in) { + // the previously stored value + Bit32s last = buffer[index]; + + // move to the next index + next(); + + // low-pass filter process + Bit32s lpfOut = weirdMul(last, filterFactor, 0xFF) + in; + + // store lpfOut multiplied by LPF amp factor + buffer[index] = weirdMul(lpfOut, amp, 0xFF); +} + +TapDelayCombFilter::TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor) : CombFilter(useSize, useFilterFactor) {} + +void TapDelayCombFilter::process(const Bit32s in) { + // the previously stored value + Bit32s last = buffer[index]; + + // move to the next index + next(); + + // prepare input + feedback + // Actually, the size of the filter varies with the TIME parameter, the feedback sample is taken from the position just below the right output + Bit32s filterIn = in + weirdMul(getOutputAt(outR + MODE_3_FEEDBACK_DELAY), feedbackFactor, 0xF0); + + // store input + feedback processed by a low-pass filter + buffer[index] = weirdMul(last, filterFactor, 0xF0) - filterIn; +} + +Bit32s TapDelayCombFilter::getLeftOutput() const { + return getOutputAt(outL + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY); +} + +Bit32s TapDelayCombFilter::getRightOutput() const { + return getOutputAt(outR + PROCESS_DELAY + MODE_3_ADDITIONAL_DELAY); +} + +void TapDelayCombFilter::setOutputPositions(const Bit32u useOutL, const Bit32u useOutR) { + outL = useOutL; + outR = useOutR; +} + +BReverbModel::BReverbModel(const ReverbMode mode) + : allpasses(NULL), combs(NULL), currentSettings(*REVERB_SETTINGS[mode]), tapDelayMode(mode == REVERB_MODE_TAP_DELAY) {} + +BReverbModel::~BReverbModel() { + close(); +} + +void BReverbModel::open() { + if (currentSettings.numberOfAllpasses > 0) { + allpasses = new AllpassFilter*[currentSettings.numberOfAllpasses]; + for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { + allpasses[i] = new AllpassFilter(currentSettings.allpassSizes[i]); + } + } + combs = new CombFilter*[currentSettings.numberOfCombs]; + if (tapDelayMode) { + *combs = new TapDelayCombFilter(*currentSettings.combSizes, *currentSettings.filterFactors); + } else { + combs[0] = new DelayWithLowPassFilter(currentSettings.combSizes[0], currentSettings.filterFactors[0], currentSettings.lpfAmp); + for (Bit32u i = 1; i < currentSettings.numberOfCombs; i++) { + combs[i] = new CombFilter(currentSettings.combSizes[i], currentSettings.filterFactors[i]); + } + } + mute(); +} + +void BReverbModel::close() { + if (allpasses != NULL) { + for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { + if (allpasses[i] != NULL) { + delete allpasses[i]; + allpasses[i] = NULL; + } + } + delete[] allpasses; + allpasses = NULL; + } + if (combs != NULL) { + for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { + if (combs[i] != NULL) { + delete combs[i]; + combs[i] = NULL; + } + } + delete[] combs; + combs = NULL; + } +} + +void BReverbModel::mute() { + if (allpasses != NULL) { + for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { + allpasses[i]->mute(); + } + } + if (combs != NULL) { + for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { + combs[i]->mute(); + } + } +} + +void BReverbModel::setParameters(Bit8u time, Bit8u level) { + if (combs == NULL) return; + level &= 7; + time &= 7; + if (tapDelayMode) { + TapDelayCombFilter *comb = static_cast (*combs); + comb->setOutputPositions(currentSettings.outLPositions[time], currentSettings.outRPositions[time & 7]); + comb->setFeedbackFactor(currentSettings.feedbackFactors[((level < 3) || (time < 6)) ? 0 : 1]); + } else { + for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { + combs[i]->setFeedbackFactor(currentSettings.feedbackFactors[(i << 3) + time]); + } + } + if (time == 0 && level == 0) { + dryAmp = wetLevel = 0; + } else { + dryAmp = currentSettings.dryAmps[level]; + wetLevel = currentSettings.wetLevels[level]; + } +} + +bool BReverbModel::isActive() const { + for (Bit32u i = 0; i < currentSettings.numberOfAllpasses; i++) { + if (!allpasses[i]->isEmpty()) return true; + } + for (Bit32u i = 0; i < currentSettings.numberOfCombs; i++) { + if (!combs[i]->isEmpty()) return true; + } + return false; +} + +void BReverbModel::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { + Bit32s dry, link, outL1, outR1; + + for (unsigned long i = 0; i < numSamples; i++) { + if (tapDelayMode) { + dry = Bit32s(*inLeft * 8192.0f) + Bit32s(*inRight * 8192.0f); + } else { + dry = Bit32s(*inLeft * 8192.0f) / 2 + Bit32s(*inRight * 8192.0f) / 2; + } + + // Looks like dryAmp doesn't change in MT-32 but it does in CM-32L / LAPC-I + dry = weirdMul(dry, dryAmp, 0xFF); + + if (tapDelayMode) { + TapDelayCombFilter *comb = static_cast (*combs); + comb->process(dry); + *outLeft = weirdMul(comb->getLeftOutput(), wetLevel, 0xFF) / 8192.0f; + *outRight = weirdMul(comb->getRightOutput(), wetLevel, 0xFF) / 8192.0f; + } else { + // Get the last stored sample before processing in order not to loose it + link = combs[0]->getOutputAt(currentSettings.combSizes[0] - 1); + + // Entrance LPF. Note, comb.process() differs a bit here. + combs[0]->process(dry); + + // This introduces reverb noise which actually makes output from the real Boss chip nondeterministic + link = link - 1; + link = allpasses[0]->process(link); + link = allpasses[1]->process(link); + link = allpasses[2]->process(link); + + // If the output position is equal to the comb size, get it now in order not to loose it + outL1 = combs[1]->getOutputAt(currentSettings.outLPositions[0] - 1); + outL1 += outL1 >> 1; + + combs[1]->process(link); + combs[2]->process(link); + combs[3]->process(link); + + link = combs[2]->getOutputAt(currentSettings.outLPositions[1]); + link += link >> 1; + link += outL1; + link += combs[3]->getOutputAt(currentSettings.outLPositions[2]); + *outLeft = weirdMul(link, wetLevel, 0xFF) / 8192.0f; + + outR1 = combs[1]->getOutputAt(currentSettings.outRPositions[0]); + outR1 += outR1 >> 1; + link = combs[2]->getOutputAt(currentSettings.outRPositions[1]); + link += link >> 1; + link += outR1; + link += combs[3]->getOutputAt(currentSettings.outRPositions[2]); + *outRight = weirdMul(link, wetLevel, 0xFF) / 8192.0f; + } + + inLeft++; + inRight++; + outLeft++; + outRight++; + } +} + +} + +#endif diff --git a/audio/softsynth/mt32/BReverbModel.h b/audio/softsynth/mt32/BReverbModel.h new file mode 100644 index 00000000000..d6fcb73c139 --- /dev/null +++ b/audio/softsynth/mt32/BReverbModel.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef MT32EMU_B_REVERB_MODEL_H +#define MT32EMU_B_REVERB_MODEL_H + +namespace MT32Emu { + +struct BReverbSettings { + const Bit32u numberOfAllpasses; + const Bit32u * const allpassSizes; + const Bit32u numberOfCombs; + const Bit32u * const combSizes; + const Bit32u * const outLPositions; + const Bit32u * const outRPositions; + const Bit32u * const filterFactors; + const Bit32u * const feedbackFactors; + const Bit32u * const dryAmps; + const Bit32u * const wetLevels; + const Bit32u lpfAmp; +}; + +class RingBuffer { +protected: + Bit16s *buffer; + const Bit32u size; + Bit32u index; + +public: + RingBuffer(const Bit32u size); + virtual ~RingBuffer(); + Bit32s next(); + bool isEmpty() const; + void mute(); +}; + +class AllpassFilter : public RingBuffer { +public: + AllpassFilter(const Bit32u size); + Bit32s process(const Bit32s in); +}; + +class CombFilter : public RingBuffer { +protected: + const Bit32u filterFactor; + Bit32u feedbackFactor; + +public: + CombFilter(const Bit32u size, const Bit32u useFilterFactor); + virtual void process(const Bit32s in); // Actually, no need to make it virtual, but for sure + Bit32s getOutputAt(const Bit32u outIndex) const; + void setFeedbackFactor(const Bit32u useFeedbackFactor); +}; + +class DelayWithLowPassFilter : public CombFilter { + Bit32u amp; + +public: + DelayWithLowPassFilter(const Bit32u useSize, const Bit32u useFilterFactor, const Bit32u useAmp); + void process(const Bit32s in); + void setFeedbackFactor(const Bit32u) {} +}; + +class TapDelayCombFilter : public CombFilter { + Bit32u outL; + Bit32u outR; + +public: + TapDelayCombFilter(const Bit32u useSize, const Bit32u useFilterFactor); + void process(const Bit32s in); + Bit32s getLeftOutput() const; + Bit32s getRightOutput() const; + void setOutputPositions(const Bit32u useOutL, const Bit32u useOutR); +}; + +class BReverbModel : public ReverbModel { + AllpassFilter **allpasses; + CombFilter **combs; + + const BReverbSettings ¤tSettings; + const bool tapDelayMode; + Bit32u dryAmp; + Bit32u wetLevel; + void mute(); + +public: + BReverbModel(const ReverbMode mode); + ~BReverbModel(); + void open(); + void close(); + void setParameters(Bit8u time, Bit8u level); + void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); + bool isActive() const; +}; + +} + +#endif diff --git a/audio/softsynth/mt32/DelayReverb.cpp b/audio/softsynth/mt32/DelayReverb.cpp index 23d25a596e7..d80c98acbc2 100644 --- a/audio/softsynth/mt32/DelayReverb.cpp +++ b/audio/softsynth/mt32/DelayReverb.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -22,7 +22,7 @@ namespace MT32Emu { -// CONFIRMED: The values below are found via analysis of digital samples. Checked with all time and level combinations. +// CONFIRMED: The values below are found via analysis of digital samples and tracing reverb RAM address / data lines. Checked with all time and level combinations. // Obviously: // rightDelay = (leftDelay - 2) * 2 + 2 // echoDelay = rightDelay - 1 @@ -39,14 +39,16 @@ static const Bit32u REVERB_TIMINGS[8][3]= { {8002, 16002, 16001} }; -static const float REVERB_FADE[8] = {0.0f, -0.049400051f, -0.08220577f, -0.131861118f, -0.197344907f, -0.262956344f, -0.345162114f, -0.509508615f}; -const float REVERB_FEEDBACK67 = -0.629960524947437f; // = -EXP2F(-2 / 3) -const float REVERB_FEEDBACK = -0.682034520443118f; // = -EXP2F(-53 / 96) -const float LPF_VALUE = 0.594603558f; // = EXP2F(-0.75f) +// Reverb amp is found as dryAmp * wetAmp +static const Bit32u REVERB_AMP[8] = {0x20*0x18, 0x50*0x18, 0x50*0x28, 0x50*0x40, 0x50*0x60, 0x50*0x80, 0x50*0xA8, 0x50*0xF8}; +static const Bit32u REVERB_FEEDBACK67 = 0x60; +static const Bit32u REVERB_FEEDBACK = 0x68; +static const float LPF_VALUE = 0x68 / 256.0f; + +static const Bit32u BUFFER_SIZE = 16384; DelayReverb::DelayReverb() { buf = NULL; - sampleRate = 0; setParameters(0, 0); } @@ -54,27 +56,22 @@ DelayReverb::~DelayReverb() { delete[] buf; } -void DelayReverb::open(unsigned int newSampleRate) { - if (newSampleRate != sampleRate || buf == NULL) { - sampleRate = newSampleRate; - +void DelayReverb::open() { + if (buf == NULL) { delete[] buf; - // If we ever need a speedup, set bufSize to EXP2F(ceil(log2(bufSize))) and use & instead of % to find buf indexes - bufSize = 16384 * sampleRate / 32000; - buf = new float[bufSize]; + buf = new float[BUFFER_SIZE]; recalcParameters(); // mute buffer bufIx = 0; if (buf != NULL) { - for (unsigned int i = 0; i < bufSize; i++) { + for (unsigned int i = 0; i < BUFFER_SIZE; i++) { buf[i] = 0.0f; } } } - // FIXME: IIR filter value depends on sample rate as well } void DelayReverb::close() { @@ -91,59 +88,53 @@ void DelayReverb::setParameters(Bit8u newTime, Bit8u newLevel) { void DelayReverb::recalcParameters() { // Number of samples between impulse and eventual appearance on the left channel - delayLeft = REVERB_TIMINGS[time][0] * sampleRate / 32000; + delayLeft = REVERB_TIMINGS[time][0]; // Number of samples between impulse and eventual appearance on the right channel - delayRight = REVERB_TIMINGS[time][1] * sampleRate / 32000; + delayRight = REVERB_TIMINGS[time][1]; // Number of samples between a response and that response feeding back/echoing - delayFeedback = REVERB_TIMINGS[time][2] * sampleRate / 32000; + delayFeedback = REVERB_TIMINGS[time][2]; - if (time < 6) { - feedback = REVERB_FEEDBACK; + if (level < 3 || time < 6) { + feedback = REVERB_FEEDBACK / 256.0f; } else { - feedback = REVERB_FEEDBACK67; + feedback = REVERB_FEEDBACK67 / 256.0f; } - // Fading speed, i.e. amplitude ratio of neighbor responses - fade = REVERB_FADE[level]; + // Overall output amp + amp = (level == 0 && time == 0) ? 0.0f : REVERB_AMP[level] / 65536.0f; } void DelayReverb::process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples) { - if (buf == NULL) { - return; - } + if (buf == NULL) return; for (unsigned int sampleIx = 0; sampleIx < numSamples; sampleIx++) { // The ring buffer write index moves backwards; reads are all done with positive offsets. - Bit32u bufIxPrev = (bufIx + 1) % bufSize; - Bit32u bufIxLeft = (bufIx + delayLeft) % bufSize; - Bit32u bufIxRight = (bufIx + delayRight) % bufSize; - Bit32u bufIxFeedback = (bufIx + delayFeedback) % bufSize; + Bit32u bufIxPrev = (bufIx + 1) % BUFFER_SIZE; + Bit32u bufIxLeft = (bufIx + delayLeft) % BUFFER_SIZE; + Bit32u bufIxRight = (bufIx + delayRight) % BUFFER_SIZE; + Bit32u bufIxFeedback = (bufIx + delayFeedback) % BUFFER_SIZE; // Attenuated input samples and feedback response are directly added to the current ring buffer location - float sample = fade * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback]; + float lpfIn = amp * (inLeft[sampleIx] + inRight[sampleIx]) + feedback * buf[bufIxFeedback]; // Single-pole IIR filter found on real devices - buf[bufIx] = buf[bufIxPrev] + (sample - buf[bufIxPrev]) * LPF_VALUE; + buf[bufIx] = buf[bufIxPrev] * LPF_VALUE - lpfIn; outLeft[sampleIx] = buf[bufIxLeft]; outRight[sampleIx] = buf[bufIxRight]; - bufIx = (bufSize + bufIx - 1) % bufSize; + bufIx = (BUFFER_SIZE + bufIx - 1) % BUFFER_SIZE; } } bool DelayReverb::isActive() const { - // Quick hack: Return true iff all samples in the left buffer are the same and - // all samples in the right buffers are the same (within the sample output threshold). - if (buf == NULL) { - return false; - } - float last = buf[0] * 8192.0f; - for (unsigned int i = 1; i < bufSize; i++) { - float s = (buf[i] * 8192.0f); - if (fabs(s - last) > 1.0f) { - return true; - } + if (buf == NULL) return false; + + float *b = buf; + float max = 0.001f; + for (Bit32u i = 0; i < BUFFER_SIZE; i++) { + if ((*b < -max) || (*b > max)) return true; + b++; } return false; } diff --git a/audio/softsynth/mt32/DelayReverb.h b/audio/softsynth/mt32/DelayReverb.h index 7c030fb839d..c8003832b58 100644 --- a/audio/softsynth/mt32/DelayReverb.h +++ b/audio/softsynth/mt32/DelayReverb.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -25,17 +25,14 @@ private: Bit8u time; Bit8u level; - unsigned int sampleRate; - Bit32u bufSize; Bit32u bufIx; - float *buf; Bit32u delayLeft; Bit32u delayRight; Bit32u delayFeedback; - float fade; + float amp; float feedback; void recalcParameters(); @@ -43,7 +40,7 @@ private: public: DelayReverb(); ~DelayReverb(); - void open(unsigned int sampleRate); + void open(); void close(); void setParameters(Bit8u time, Bit8u level); void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); diff --git a/audio/softsynth/mt32/FreeverbModel.cpp b/audio/softsynth/mt32/FreeverbModel.cpp index d9bd17e62e6..bd9c70b6f45 100644 --- a/audio/softsynth/mt32/FreeverbModel.cpp +++ b/audio/softsynth/mt32/FreeverbModel.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -35,9 +35,7 @@ FreeverbModel::~FreeverbModel() { delete freeverb; } -void FreeverbModel::open(unsigned int /*sampleRate*/) { - // FIXME: scaleTuning must be multiplied by sample rate to 32000Hz ratio - // IIR filter values depend on sample rate as well +void FreeverbModel::open() { if (freeverb == NULL) { freeverb = new revmodel(scaleTuning); } diff --git a/audio/softsynth/mt32/FreeverbModel.h b/audio/softsynth/mt32/FreeverbModel.h index 925b2dbf96e..5ea11f1f40e 100644 --- a/audio/softsynth/mt32/FreeverbModel.h +++ b/audio/softsynth/mt32/FreeverbModel.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -32,7 +32,7 @@ class FreeverbModel : public ReverbModel { public: FreeverbModel(float useScaleTuning, float useFiltVal, float useWet, Bit8u useRoom, float useDamp); ~FreeverbModel(); - void open(unsigned int sampleRate); + void open(); void close(); void setParameters(Bit8u time, Bit8u level); void process(const float *inLeft, const float *inRight, float *outLeft, float *outRight, unsigned long numSamples); diff --git a/audio/softsynth/mt32/LA32Ramp.cpp b/audio/softsynth/mt32/LA32Ramp.cpp index 4e4d6b4f309..b4ac6f1d466 100644 --- a/audio/softsynth/mt32/LA32Ramp.cpp +++ b/audio/softsynth/mt32/LA32Ramp.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -82,9 +82,13 @@ void LA32Ramp::startRamp(Bit8u target, Bit8u increment) { if (increment == 0) { largeIncrement = 0; } else { - // Using integer argument here, no precision loss: + // Three bits in the fractional part, no need to interpolate // (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f) - largeIncrement = (unsigned int)(EXP2I(((increment & 0x7F) + 24) << 9) + 0.125f); + Bit32u expArg = increment & 0x7F; + largeIncrement = 8191 - Tables::getInstance().exp9[~(expArg << 6) & 511]; + largeIncrement <<= expArg >> 3; + largeIncrement += 64; + largeIncrement >>= 9; } descending = (increment & 0x80) != 0; if (descending) { diff --git a/audio/softsynth/mt32/LA32Ramp.h b/audio/softsynth/mt32/LA32Ramp.h index ae937eb7e1d..8f55941a127 100644 --- a/audio/softsynth/mt32/LA32Ramp.h +++ b/audio/softsynth/mt32/LA32Ramp.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/audio/softsynth/mt32/LA32WaveGenerator.cpp b/audio/softsynth/mt32/LA32WaveGenerator.cpp new file mode 100644 index 00000000000..80650699fb2 --- /dev/null +++ b/audio/softsynth/mt32/LA32WaveGenerator.cpp @@ -0,0 +1,418 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +//#include +#include "mt32emu.h" +#include "mmath.h" +#include "LA32WaveGenerator.h" + +#if MT32EMU_ACCURATE_WG == 0 + +namespace MT32Emu { + +static const Bit32u SINE_SEGMENT_RELATIVE_LENGTH = 1 << 18; +static const Bit32u MIDDLE_CUTOFF_VALUE = 128 << 18; +static const Bit32u RESONANCE_DECAY_THRESHOLD_CUTOFF_VALUE = 144 << 18; +static const Bit32u MAX_CUTOFF_VALUE = 240 << 18; +static const LogSample SILENCE = {65535, LogSample::POSITIVE}; + +Bit16u LA32Utilites::interpolateExp(const Bit16u fract) { + Bit16u expTabIndex = fract >> 3; + Bit16u extraBits = ~fract & 7; + Bit16u expTabEntry2 = 8191 - Tables::getInstance().exp9[expTabIndex]; + Bit16u expTabEntry1 = expTabIndex == 0 ? 8191 : (8191 - Tables::getInstance().exp9[expTabIndex - 1]); + return expTabEntry2 + (((expTabEntry1 - expTabEntry2) * extraBits) >> 3); +} + +Bit16s LA32Utilites::unlog(const LogSample &logSample) { + //Bit16s sample = (Bit16s)EXP2F(13.0f - logSample.logValue / 1024.0f); + Bit32u intLogValue = logSample.logValue >> 12; + Bit16u fracLogValue = logSample.logValue & 4095; + Bit16s sample = interpolateExp(fracLogValue) >> intLogValue; + return logSample.sign == LogSample::POSITIVE ? sample : -sample; +} + +void LA32Utilites::addLogSamples(LogSample &logSample1, const LogSample &logSample2) { + Bit32u logSampleValue = logSample1.logValue + logSample2.logValue; + logSample1.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + logSample1.sign = logSample1.sign == logSample2.sign ? LogSample::POSITIVE : LogSample::NEGATIVE; +} + +Bit32u LA32WaveGenerator::getSampleStep() { + // sampleStep = EXP2F(pitch / 4096.0f + 4.0f) + Bit32u sampleStep = LA32Utilites::interpolateExp(~pitch & 4095); + sampleStep <<= pitch >> 12; + sampleStep >>= 8; + sampleStep &= ~1; + return sampleStep; +} + +Bit32u LA32WaveGenerator::getResonanceWaveLengthFactor(Bit32u effectiveCutoffValue) { + // resonanceWaveLengthFactor = (Bit32u)EXP2F(12.0f + effectiveCutoffValue / 4096.0f); + Bit32u resonanceWaveLengthFactor = LA32Utilites::interpolateExp(~effectiveCutoffValue & 4095); + resonanceWaveLengthFactor <<= effectiveCutoffValue >> 12; + return resonanceWaveLengthFactor; +} + +Bit32u LA32WaveGenerator::getHighLinearLength(Bit32u effectiveCutoffValue) { + // Ratio of positive segment to wave length + Bit32u effectivePulseWidthValue = 0; + if (pulseWidth > 128) { + effectivePulseWidthValue = (pulseWidth - 128) << 6; + } + + Bit32u highLinearLength = 0; + // highLinearLength = EXP2F(19.0f - effectivePulseWidthValue / 4096.0f + effectiveCutoffValue / 4096.0f) - 2 * SINE_SEGMENT_RELATIVE_LENGTH; + if (effectivePulseWidthValue < effectiveCutoffValue) { + Bit32u expArg = effectiveCutoffValue - effectivePulseWidthValue; + highLinearLength = LA32Utilites::interpolateExp(~expArg & 4095); + highLinearLength <<= 7 + (expArg >> 12); + highLinearLength -= 2 * SINE_SEGMENT_RELATIVE_LENGTH; + } + return highLinearLength; +} + +void LA32WaveGenerator::computePositions(Bit32u highLinearLength, Bit32u lowLinearLength, Bit32u resonanceWaveLengthFactor) { + // Assuming 12-bit multiplication used here + squareWavePosition = resonanceSinePosition = (wavePosition >> 8) * (resonanceWaveLengthFactor >> 4); + if (squareWavePosition < SINE_SEGMENT_RELATIVE_LENGTH) { + phase = POSITIVE_RISING_SINE_SEGMENT; + return; + } + squareWavePosition -= SINE_SEGMENT_RELATIVE_LENGTH; + if (squareWavePosition < highLinearLength) { + phase = POSITIVE_LINEAR_SEGMENT; + return; + } + squareWavePosition -= highLinearLength; + if (squareWavePosition < SINE_SEGMENT_RELATIVE_LENGTH) { + phase = POSITIVE_FALLING_SINE_SEGMENT; + return; + } + squareWavePosition -= SINE_SEGMENT_RELATIVE_LENGTH; + resonanceSinePosition = squareWavePosition; + if (squareWavePosition < SINE_SEGMENT_RELATIVE_LENGTH) { + phase = NEGATIVE_FALLING_SINE_SEGMENT; + return; + } + squareWavePosition -= SINE_SEGMENT_RELATIVE_LENGTH; + if (squareWavePosition < lowLinearLength) { + phase = NEGATIVE_LINEAR_SEGMENT; + return; + } + squareWavePosition -= lowLinearLength; + phase = NEGATIVE_RISING_SINE_SEGMENT; +} + +void LA32WaveGenerator::advancePosition() { + wavePosition += getSampleStep(); + wavePosition %= 4 * SINE_SEGMENT_RELATIVE_LENGTH; + + Bit32u effectiveCutoffValue = (cutoffVal > MIDDLE_CUTOFF_VALUE) ? (cutoffVal - MIDDLE_CUTOFF_VALUE) >> 10 : 0; + Bit32u resonanceWaveLengthFactor = getResonanceWaveLengthFactor(effectiveCutoffValue); + Bit32u highLinearLength = getHighLinearLength(effectiveCutoffValue); + Bit32u lowLinearLength = (resonanceWaveLengthFactor << 8) - 4 * SINE_SEGMENT_RELATIVE_LENGTH - highLinearLength; + computePositions(highLinearLength, lowLinearLength, resonanceWaveLengthFactor); + + // resonancePhase computation hack + *(int*)&resonancePhase = ((resonanceSinePosition >> 18) + (phase > POSITIVE_FALLING_SINE_SEGMENT ? 2 : 0)) & 3; +} + +void LA32WaveGenerator::generateNextSquareWaveLogSample() { + Bit32u logSampleValue; + switch (phase) { + case POSITIVE_RISING_SINE_SEGMENT: + case NEGATIVE_FALLING_SINE_SEGMENT: + logSampleValue = Tables::getInstance().logsin9[(squareWavePosition >> 9) & 511]; + break; + case POSITIVE_FALLING_SINE_SEGMENT: + case NEGATIVE_RISING_SINE_SEGMENT: + logSampleValue = Tables::getInstance().logsin9[~(squareWavePosition >> 9) & 511]; + break; + case POSITIVE_LINEAR_SEGMENT: + case NEGATIVE_LINEAR_SEGMENT: + default: + logSampleValue = 0; + break; + } + logSampleValue <<= 2; + logSampleValue += amp >> 10; + if (cutoffVal < MIDDLE_CUTOFF_VALUE) { + logSampleValue += (MIDDLE_CUTOFF_VALUE - cutoffVal) >> 9; + } + + squareLogSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + squareLogSample.sign = phase < NEGATIVE_FALLING_SINE_SEGMENT ? LogSample::POSITIVE : LogSample::NEGATIVE; +} + +void LA32WaveGenerator::generateNextResonanceWaveLogSample() { + Bit32u logSampleValue; + if (resonancePhase == POSITIVE_FALLING_RESONANCE_SINE_SEGMENT || resonancePhase == NEGATIVE_RISING_RESONANCE_SINE_SEGMENT) { + logSampleValue = Tables::getInstance().logsin9[~(resonanceSinePosition >> 9) & 511]; + } else { + logSampleValue = Tables::getInstance().logsin9[(resonanceSinePosition >> 9) & 511]; + } + logSampleValue <<= 2; + logSampleValue += amp >> 10; + + // From the digital captures, the decaying speed of the resonance sine is found a bit different for the positive and the negative segments + Bit32u decayFactor = phase < NEGATIVE_FALLING_SINE_SEGMENT ? resAmpDecayFactor : resAmpDecayFactor + 1; + // Unsure about resonanceSinePosition here. It's possible that dedicated counter & decrement are used. Although, cutoff is finely ramped, so maybe not. + logSampleValue += resonanceAmpSubtraction + (((resonanceSinePosition >> 4) * decayFactor) >> 8); + + // To ensure the output wave has no breaks, two different windows are appied to the beginning and the ending of the resonance sine segment + if (phase == POSITIVE_RISING_SINE_SEGMENT || phase == NEGATIVE_FALLING_SINE_SEGMENT) { + // The window is synchronous sine here + logSampleValue += Tables::getInstance().logsin9[(squareWavePosition >> 9) & 511] << 2; + } else if (phase == POSITIVE_FALLING_SINE_SEGMENT || phase == NEGATIVE_RISING_SINE_SEGMENT) { + // The window is synchronous square sine here + logSampleValue += Tables::getInstance().logsin9[~(squareWavePosition >> 9) & 511] << 3; + } + + if (cutoffVal < MIDDLE_CUTOFF_VALUE) { + // For the cutoff values below the cutoff middle point, it seems the amp of the resonance wave is expotentially decayed + logSampleValue += 31743 + ((MIDDLE_CUTOFF_VALUE - cutoffVal) >> 9); + } else if (cutoffVal < RESONANCE_DECAY_THRESHOLD_CUTOFF_VALUE) { + // For the cutoff values below this point, the amp of the resonance wave is sinusoidally decayed + Bit32u sineIx = (cutoffVal - MIDDLE_CUTOFF_VALUE) >> 13; + logSampleValue += Tables::getInstance().logsin9[sineIx] << 2; + } + + // After all the amp decrements are added, it should be safe now to adjust the amp of the resonance wave to what we see on captures + logSampleValue -= 1 << 12; + + resonanceLogSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + resonanceLogSample.sign = resonancePhase < NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT ? LogSample::POSITIVE : LogSample::NEGATIVE; +} + +void LA32WaveGenerator::generateNextSawtoothCosineLogSample(LogSample &logSample) const { + Bit32u sawtoothCosinePosition = wavePosition + (1 << 18); + if ((sawtoothCosinePosition & (1 << 18)) > 0) { + logSample.logValue = Tables::getInstance().logsin9[~(sawtoothCosinePosition >> 9) & 511]; + } else { + logSample.logValue = Tables::getInstance().logsin9[(sawtoothCosinePosition >> 9) & 511]; + } + logSample.logValue <<= 2; + logSample.sign = ((sawtoothCosinePosition & (1 << 19)) == 0) ? LogSample::POSITIVE : LogSample::NEGATIVE; +} + +void LA32WaveGenerator::pcmSampleToLogSample(LogSample &logSample, const Bit16s pcmSample) const { + Bit32u logSampleValue = (32787 - (pcmSample & 32767)) << 1; + logSampleValue += amp >> 10; + logSample.logValue = logSampleValue < 65536 ? (Bit16u)logSampleValue : 65535; + logSample.sign = pcmSample < 0 ? LogSample::NEGATIVE : LogSample::POSITIVE; +} + +void LA32WaveGenerator::generateNextPCMWaveLogSamples() { + // This should emulate the ladder we see in the PCM captures for pitches 01, 02, 07, etc. + // The most probable cause is the factor in the interpolation formula is one bit less + // accurate than the sample position counter + pcmInterpolationFactor = (wavePosition & 255) >> 1; + Bit32u pcmWaveTableIx = wavePosition >> 8; + pcmSampleToLogSample(firstPCMLogSample, pcmWaveAddress[pcmWaveTableIx]); + if (pcmWaveInterpolated) { + pcmWaveTableIx++; + if (pcmWaveTableIx < pcmWaveLength) { + pcmSampleToLogSample(secondPCMLogSample, pcmWaveAddress[pcmWaveTableIx]); + } else { + if (pcmWaveLooped) { + pcmWaveTableIx -= pcmWaveLength; + pcmSampleToLogSample(secondPCMLogSample, pcmWaveAddress[pcmWaveTableIx]); + } else { + secondPCMLogSample = SILENCE; + } + } + } else { + secondPCMLogSample = SILENCE; + } + // pcmSampleStep = (Bit32u)EXP2F(pitch / 4096.0f + 3.0f); + Bit32u pcmSampleStep = LA32Utilites::interpolateExp(~pitch & 4095); + pcmSampleStep <<= pitch >> 12; + // Seeing the actual lengths of the PCM wave for pitches 00..12, + // the pcmPosition counter can be assumed to have 8-bit fractions + pcmSampleStep >>= 9; + wavePosition += pcmSampleStep; + if (wavePosition >= (pcmWaveLength << 8)) { + if (pcmWaveLooped) { + wavePosition -= pcmWaveLength << 8; + } else { + deactivate(); + } + } +} + +void LA32WaveGenerator::initSynth(const bool useSawtoothWaveform, const Bit8u usePulseWidth, const Bit8u useResonance) { + sawtoothWaveform = useSawtoothWaveform; + pulseWidth = usePulseWidth; + resonance = useResonance; + + wavePosition = 0; + + squareWavePosition = 0; + phase = POSITIVE_RISING_SINE_SEGMENT; + + resonanceSinePosition = 0; + resonancePhase = POSITIVE_RISING_RESONANCE_SINE_SEGMENT; + resonanceAmpSubtraction = (32 - resonance) << 10; + resAmpDecayFactor = Tables::getInstance().resAmpDecayFactor[resonance >> 2] << 2; + + pcmWaveAddress = NULL; + active = true; +} + +void LA32WaveGenerator::initPCM(const Bit16s * const usePCMWaveAddress, const Bit32u usePCMWaveLength, const bool usePCMWaveLooped, const bool usePCMWaveInterpolated) { + pcmWaveAddress = usePCMWaveAddress; + pcmWaveLength = usePCMWaveLength; + pcmWaveLooped = usePCMWaveLooped; + pcmWaveInterpolated = usePCMWaveInterpolated; + + wavePosition = 0; + active = true; +} + +void LA32WaveGenerator::generateNextSample(const Bit32u useAmp, const Bit16u usePitch, const Bit32u useCutoffVal) { + if (!active) { + return; + } + + amp = useAmp; + pitch = usePitch; + + if (isPCMWave()) { + generateNextPCMWaveLogSamples(); + return; + } + + // The 240 cutoffVal limit was determined via sample analysis (internal Munt capture IDs: glop3, glop4). + // More research is needed to be sure that this is correct, however. + cutoffVal = (useCutoffVal > MAX_CUTOFF_VALUE) ? MAX_CUTOFF_VALUE : useCutoffVal; + + generateNextSquareWaveLogSample(); + generateNextResonanceWaveLogSample(); + if (sawtoothWaveform) { + LogSample cosineLogSample; + generateNextSawtoothCosineLogSample(cosineLogSample); + LA32Utilites::addLogSamples(squareLogSample, cosineLogSample); + LA32Utilites::addLogSamples(resonanceLogSample, cosineLogSample); + } + advancePosition(); +} + +LogSample LA32WaveGenerator::getOutputLogSample(const bool first) const { + if (!isActive()) { + return SILENCE; + } + if (isPCMWave()) { + return first ? firstPCMLogSample : secondPCMLogSample; + } + return first ? squareLogSample : resonanceLogSample; +} + +void LA32WaveGenerator::deactivate() { + active = false; +} + +bool LA32WaveGenerator::isActive() const { + return active; +} + +bool LA32WaveGenerator::isPCMWave() const { + return pcmWaveAddress != NULL; +} + +Bit32u LA32WaveGenerator::getPCMInterpolationFactor() const { + return pcmInterpolationFactor; +} + +void LA32PartialPair::init(const bool useRingModulated, const bool useMixed) { + ringModulated = useRingModulated; + mixed = useMixed; +} + +void LA32PartialPair::initSynth(const PairType useMaster, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) { + if (useMaster == MASTER) { + master.initSynth(sawtoothWaveform, pulseWidth, resonance); + } else { + slave.initSynth(sawtoothWaveform, pulseWidth, resonance); + } +} + +void LA32PartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) { + if (useMaster == MASTER) { + master.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, true); + } else { + slave.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, !ringModulated); + } +} + +void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff) { + if (useMaster == MASTER) { + master.generateNextSample(amp, pitch, cutoff); + } else { + slave.generateNextSample(amp, pitch, cutoff); + } +} + +Bit16s LA32PartialPair::unlogAndMixWGOutput(const LA32WaveGenerator &wg, const LogSample * const ringModulatingLogSample) { + if (!wg.isActive() || ((ringModulatingLogSample != NULL) && (ringModulatingLogSample->logValue == SILENCE.logValue))) { + return 0; + } + LogSample firstLogSample = wg.getOutputLogSample(true); + LogSample secondLogSample = wg.getOutputLogSample(false); + if (ringModulatingLogSample != NULL) { + LA32Utilites::addLogSamples(firstLogSample, *ringModulatingLogSample); + LA32Utilites::addLogSamples(secondLogSample, *ringModulatingLogSample); + } + Bit16s firstSample = LA32Utilites::unlog(firstLogSample); + Bit16s secondSample = LA32Utilites::unlog(secondLogSample); + if (wg.isPCMWave()) { + return Bit16s(firstSample + ((Bit32s(secondSample - firstSample) * wg.getPCMInterpolationFactor()) >> 7)); + } + return firstSample + secondSample; +} + +Bit16s LA32PartialPair::nextOutSample() { + if (ringModulated) { + LogSample slaveFirstLogSample = slave.getOutputLogSample(true); + LogSample slaveSecondLogSample = slave.getOutputLogSample(false); + Bit16s sample = unlogAndMixWGOutput(master, &slaveFirstLogSample); + if (!slave.isPCMWave()) { + sample += unlogAndMixWGOutput(master, &slaveSecondLogSample); + } + if (mixed) { + sample += unlogAndMixWGOutput(master, NULL); + } + return sample; + } + return unlogAndMixWGOutput(master, NULL) + unlogAndMixWGOutput(slave, NULL); +} + +void LA32PartialPair::deactivate(const PairType useMaster) { + if (useMaster == MASTER) { + master.deactivate(); + } else { + slave.deactivate(); + } +} + +bool LA32PartialPair::isActive(const PairType useMaster) const { + return useMaster == MASTER ? master.isActive() : slave.isActive(); +} + +} + +#endif // #if MT32EMU_ACCURATE_WG == 0 diff --git a/audio/softsynth/mt32/LA32WaveGenerator.h b/audio/softsynth/mt32/LA32WaveGenerator.h new file mode 100644 index 00000000000..37a4aead851 --- /dev/null +++ b/audio/softsynth/mt32/LA32WaveGenerator.h @@ -0,0 +1,246 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#if MT32EMU_ACCURATE_WG == 0 + +#ifndef MT32EMU_LA32_WAVE_GENERATOR_H +#define MT32EMU_LA32_WAVE_GENERATOR_H + +namespace MT32Emu { + +/** + * LA32 performs wave generation in the log-space that allows replacing multiplications by cheap additions + * It's assumed that only low-bit multiplications occur in a few places which are unavoidable like these: + * - interpolation of exponent table (obvious, a delta value has 4 bits) + * - computation of resonance amp decay envelope (the table contains values with 1-2 "1" bits except the very first value 31 but this case can be found using inversion) + * - interpolation of PCM samples (obvious, the wave position counter is in the linear space, there is no log() table in the chip) + * and it seems to be implemented in the same way as in the Boss chip, i.e. right shifted additions which involved noticeable precision loss + * Subtraction is supposed to be replaced by simple inversion + * As the logarithmic sine is always negative, all the logarithmic values are treated as decrements + */ +struct LogSample { + // 16-bit fixed point value, includes 12-bit fractional part + // 4-bit integer part allows to present any 16-bit sample in the log-space + // Obviously, the log value doesn't contain the sign of the resulting sample + Bit16u logValue; + enum { + POSITIVE, + NEGATIVE + } sign; +}; + +class LA32Utilites { +public: + static Bit16u interpolateExp(const Bit16u fract); + static Bit16s unlog(const LogSample &logSample); + static void addLogSamples(LogSample &logSample1, const LogSample &logSample2); +}; + +/** + * LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator. + * The output square wave is created by adding high / low linear segments in-between + * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis. + * Behaviour of a true resonance filter is emulated by adding decaying sine wave. + * The beginning and the ending of the resonant sine is multiplied by a cosine window. + * To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave. + */ +class LA32WaveGenerator { + //*************************************************************************** + // The local copy of partial parameters below + //*************************************************************************** + + bool active; + + // True means the resulting square wave is to be multiplied by the synchronous cosine + bool sawtoothWaveform; + + // Logarithmic amp of the wave generator + Bit32u amp; + + // Logarithmic frequency of the resulting wave + Bit16u pitch; + + // Values in range [1..31] + // Value 1 correspong to the minimum resonance + Bit8u resonance; + + // Processed value in range [0..255] + // Values in range [0..128] have no effect and the resulting wave remains symmetrical + // Value 255 corresponds to the maximum possible asymmetric of the resulting wave + Bit8u pulseWidth; + + // Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier + Bit32u cutoffVal; + + // Logarithmic PCM sample start address + const Bit16s *pcmWaveAddress; + + // Logarithmic PCM sample length + Bit32u pcmWaveLength; + + // true for looped logarithmic PCM samples + bool pcmWaveLooped; + + // false for slave PCM partials in the structures with the ring modulation + bool pcmWaveInterpolated; + + //*************************************************************************** + // Internal variables below + //*************************************************************************** + + // Relative position within either the synth wave or the PCM sampled wave + // 0 - start of the positive rising sine segment of the square wave or start of the PCM sample + // 1048576 (2^20) - end of the negative rising sine segment of the square wave + // For PCM waves, the address of the currently playing sample equals (wavePosition / 256) + Bit32u wavePosition; + + // Relative position within a square wave phase: + // 0 - start of the phase + // 262144 (2^18) - end of a sine phase in the square wave + Bit32u squareWavePosition; + + // Relative position within the positive or negative wave segment: + // 0 - start of the corresponding positive or negative segment of the square wave + // 262144 (2^18) - corresponds to end of the first sine phase in the square wave + // The same increment sampleStep is used to indicate the current position + // since the length of the resonance wave is always equal to four square wave sine segments. + Bit32u resonanceSinePosition; + + // The amp of the resonance sine wave grows with the resonance value + // As the resonance value cannot change while the partial is active, it is initialised once + Bit32u resonanceAmpSubtraction; + + // The decay speed of resonance sine wave, depends on the resonance value + Bit32u resAmpDecayFactor; + + // Fractional part of the pcmPosition + Bit32u pcmInterpolationFactor; + + // Current phase of the square wave + enum { + POSITIVE_RISING_SINE_SEGMENT, + POSITIVE_LINEAR_SEGMENT, + POSITIVE_FALLING_SINE_SEGMENT, + NEGATIVE_FALLING_SINE_SEGMENT, + NEGATIVE_LINEAR_SEGMENT, + NEGATIVE_RISING_SINE_SEGMENT + } phase; + + // Current phase of the resonance wave + enum { + POSITIVE_RISING_RESONANCE_SINE_SEGMENT, + POSITIVE_FALLING_RESONANCE_SINE_SEGMENT, + NEGATIVE_FALLING_RESONANCE_SINE_SEGMENT, + NEGATIVE_RISING_RESONANCE_SINE_SEGMENT + } resonancePhase; + + // Resulting log-space samples of the square and resonance waves + LogSample squareLogSample; + LogSample resonanceLogSample; + + // Processed neighbour log-space samples of the PCM wave + LogSample firstPCMLogSample; + LogSample secondPCMLogSample; + + //*************************************************************************** + // Internal methods below + //*************************************************************************** + + Bit32u getSampleStep(); + Bit32u getResonanceWaveLengthFactor(Bit32u effectiveCutoffValue); + Bit32u getHighLinearLength(Bit32u effectiveCutoffValue); + + void computePositions(Bit32u highLinearLength, Bit32u lowLinearLength, Bit32u resonanceWaveLengthFactor); + void advancePosition(); + + void generateNextSquareWaveLogSample(); + void generateNextResonanceWaveLogSample(); + void generateNextSawtoothCosineLogSample(LogSample &logSample) const; + + void pcmSampleToLogSample(LogSample &logSample, const Bit16s pcmSample) const; + void generateNextPCMWaveLogSamples(); + +public: + // Initialise the WG engine for generation of synth partial samples and set up the invariant parameters + void initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance); + + // Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters + void initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated); + + // Update parameters with respect to TVP, TVA and TVF, and generate next sample + void generateNextSample(const Bit32u amp, const Bit16u pitch, const Bit32u cutoff); + + // WG output in the log-space consists of two components which are to be added (or ring modulated) in the linear-space afterwards + LogSample getOutputLogSample(const bool first) const; + + // Deactivate the WG engine + void deactivate(); + + // Return active state of the WG engine + bool isActive() const; + + // Return true if the WG engine generates PCM wave samples + bool isPCMWave() const; + + // Return current PCM interpolation factor + Bit32u getPCMInterpolationFactor() const; +}; + +// LA32PartialPair contains a structure of two partials being mixed / ring modulated +class LA32PartialPair { + LA32WaveGenerator master; + LA32WaveGenerator slave; + bool ringModulated; + bool mixed; + + static Bit16s unlogAndMixWGOutput(const LA32WaveGenerator &wg, const LogSample * const ringModulatingLogSample); + +public: + enum PairType { + MASTER, + SLAVE + }; + + // ringModulated should be set to false for the structures with mixing or stereo output + // ringModulated should be set to true for the structures with ring modulation + // mixed is used for the structures with ring modulation and indicates whether the master partial output is mixed to the ring modulator output + void init(const bool ringModulated, const bool mixed); + + // Initialise the WG engine for generation of synth partial samples and set up the invariant parameters + void initSynth(const PairType master, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance); + + // Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters + void initPCM(const PairType master, const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped); + + // Update parameters with respect to TVP, TVA and TVF, and generate next sample + void generateNextSample(const PairType master, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff); + + // Perform mixing / ring modulation and return the result + Bit16s nextOutSample(); + + // Deactivate the WG engine + void deactivate(const PairType master); + + // Return active state of the WG engine + bool isActive(const PairType master) const; +}; + +} // namespace MT32Emu + +#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H + +#endif // #if MT32EMU_ACCURATE_WG == 0 diff --git a/audio/softsynth/mt32/LegacyWaveGenerator.cpp b/audio/softsynth/mt32/LegacyWaveGenerator.cpp new file mode 100644 index 00000000000..35ca9750189 --- /dev/null +++ b/audio/softsynth/mt32/LegacyWaveGenerator.cpp @@ -0,0 +1,347 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +//#include +#include "mt32emu.h" +#include "mmath.h" +#include "LegacyWaveGenerator.h" + +#if MT32EMU_ACCURATE_WG == 1 + +namespace MT32Emu { + +static const float MIDDLE_CUTOFF_VALUE = 128.0f; +static const float RESONANCE_DECAY_THRESHOLD_CUTOFF_VALUE = 144.0f; +static const float MAX_CUTOFF_VALUE = 240.0f; + +float LA32WaveGenerator::getPCMSample(unsigned int position) { + if (position >= pcmWaveLength) { + if (!pcmWaveLooped) { + return 0; + } + position = position % pcmWaveLength; + } + Bit16s pcmSample = pcmWaveAddress[position]; + float sampleValue = EXP2F(((pcmSample & 32767) - 32787.0f) / 2048.0f); + return ((pcmSample & 32768) == 0) ? sampleValue : -sampleValue; +} + +void LA32WaveGenerator::initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) { + this->sawtoothWaveform = sawtoothWaveform; + this->pulseWidth = pulseWidth; + this->resonance = resonance; + + wavePos = 0.0f; + lastFreq = 0.0f; + + pcmWaveAddress = NULL; + active = true; +} + +void LA32WaveGenerator::initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated) { + this->pcmWaveAddress = pcmWaveAddress; + this->pcmWaveLength = pcmWaveLength; + this->pcmWaveLooped = pcmWaveLooped; + this->pcmWaveInterpolated = pcmWaveInterpolated; + + pcmPosition = 0.0f; + active = true; +} + +float LA32WaveGenerator::generateNextSample(const Bit32u ampVal, const Bit16u pitch, const Bit32u cutoffRampVal) { + if (!active) { + return 0.0f; + } + + this->amp = amp; + this->pitch = pitch; + + float sample = 0.0f; + + // SEMI-CONFIRMED: From sample analysis: + // (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc. + // This gives results within +/- 2 at the output (before any DAC bitshifting) + // when sustaining at levels 156 - 255 with no modifiers. + // (2) Tested with a special square wave partial (internal capture ID tva5) at TVA envelope levels 155-255. + // This gives deltas between -1 and 0 compared to the real output. Note that this special partial only produces + // positive amps, so negative still needs to be explored, as well as lower levels. + // + // Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing. + + float amp = EXP2F(ampVal / -1024.0f / 4096.0f); + float freq = EXP2F(pitch / 4096.0f - 16.0f) * SAMPLE_RATE; + + if (isPCMWave()) { + // Render PCM waveform + int len = pcmWaveLength; + int intPCMPosition = (int)pcmPosition; + if (intPCMPosition >= len && !pcmWaveLooped) { + // We're now past the end of a non-looping PCM waveform so it's time to die. + deactivate(); + return 0.0f; + } + float positionDelta = freq * 2048.0f / SAMPLE_RATE; + + // Linear interpolation + float firstSample = getPCMSample(intPCMPosition); + // We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial. + // It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial + // is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair). + if (pcmWaveInterpolated) { + sample = firstSample + (getPCMSample(intPCMPosition + 1) - firstSample) * (pcmPosition - intPCMPosition); + } else { + sample = firstSample; + } + + float newPCMPosition = pcmPosition + positionDelta; + if (pcmWaveLooped) { + newPCMPosition = fmod(newPCMPosition, (float)pcmWaveLength); + } + pcmPosition = newPCMPosition; + } else { + // Render synthesised waveform + wavePos *= lastFreq / freq; + lastFreq = freq; + + float resAmp = EXP2F(1.0f - (32 - resonance) / 4.0f); + { + //static const float resAmpFactor = EXP2F(-7); + //resAmp = EXP2I(resonance << 10) * resAmpFactor; + } + + // The cutoffModifier may not be supposed to be directly added to the cutoff - + // it may for example need to be multiplied in some way. + // The 240 cutoffVal limit was determined via sample analysis (internal Munt capture IDs: glop3, glop4). + // More research is needed to be sure that this is correct, however. + float cutoffVal = cutoffRampVal / 262144.0f; + if (cutoffVal > MAX_CUTOFF_VALUE) { + cutoffVal = MAX_CUTOFF_VALUE; + } + + // Wave length in samples + float waveLen = SAMPLE_RATE / freq; + + // Init cosineLen + float cosineLen = 0.5f * waveLen; + if (cutoffVal > MIDDLE_CUTOFF_VALUE) { + cosineLen *= EXP2F((cutoffVal - MIDDLE_CUTOFF_VALUE) / -16.0f); // found from sample analysis + } + + // Start playing in center of first cosine segment + // relWavePos is shifted by a half of cosineLen + float relWavePos = wavePos + 0.5f * cosineLen; + if (relWavePos > waveLen) { + relWavePos -= waveLen; + } + + // Ratio of positive segment to wave length + float pulseLen = 0.5f; + if (pulseWidth > 128) { + pulseLen = EXP2F((64 - pulseWidth) / 64.0f); + //static const float pulseLenFactor = EXP2F(-192 / 64); + //pulseLen = EXP2I((256 - pulseWidthVal) << 6) * pulseLenFactor; + } + pulseLen *= waveLen; + + float hLen = pulseLen - cosineLen; + + // Ignore pulsewidths too high for given freq + if (hLen < 0.0f) { + hLen = 0.0f; + } + + // Ignore pulsewidths too high for given freq and cutoff + float lLen = waveLen - hLen - 2 * cosineLen; + if (lLen < 0.0f) { + lLen = 0.0f; + } + + // Correct resAmp for cutoff in range 50..66 + if ((cutoffVal >= 128.0f) && (cutoffVal < 144.0f)) { + resAmp *= sin(FLOAT_PI * (cutoffVal - 128.0f) / 32.0f); + } + + // Produce filtered square wave with 2 cosine waves on slopes + + // 1st cosine segment + if (relWavePos < cosineLen) { + sample = -cos(FLOAT_PI * relWavePos / cosineLen); + } else + + // high linear segment + if (relWavePos < (cosineLen + hLen)) { + sample = 1.f; + } else + + // 2nd cosine segment + if (relWavePos < (2 * cosineLen + hLen)) { + sample = cos(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen); + } else { + + // low linear segment + sample = -1.f; + } + + if (cutoffVal < 128.0f) { + + // Attenuate samples below cutoff 50 + // Found by sample analysis + sample *= EXP2F(-0.125f * (128.0f - cutoffVal)); + } else { + + // Add resonance sine. Effective for cutoff > 50 only + float resSample = 1.0f; + + // Resonance decay speed factor + float resAmpDecayFactor = Tables::getInstance().resAmpDecayFactor[resonance >> 2]; + + // Now relWavePos counts from the middle of first cosine + relWavePos = wavePos; + + // negative segments + if (!(relWavePos < (cosineLen + hLen))) { + resSample = -resSample; + relWavePos -= cosineLen + hLen; + + // From the digital captures, the decaying speed of the resonance sine is found a bit different for the positive and the negative segments + resAmpDecayFactor += 0.25f; + } + + // Resonance sine WG + resSample *= sin(FLOAT_PI * relWavePos / cosineLen); + + // Resonance sine amp + float resAmpFadeLog2 = -0.125f * resAmpDecayFactor * (relWavePos / cosineLen); // seems to be exact + float resAmpFade = EXP2F(resAmpFadeLog2); + + // Now relWavePos set negative to the left from center of any cosine + relWavePos = wavePos; + + // negative segment + if (!(wavePos < (waveLen - 0.5f * cosineLen))) { + relWavePos -= waveLen; + } else + + // positive segment + if (!(wavePos < (hLen + 0.5f * cosineLen))) { + relWavePos -= cosineLen + hLen; + } + + // To ensure the output wave has no breaks, two different windows are appied to the beginning and the ending of the resonance sine segment + if (relWavePos < 0.5f * cosineLen) { + float syncSine = sin(FLOAT_PI * relWavePos / cosineLen); + if (relWavePos < 0.0f) { + // The window is synchronous square sine here + resAmpFade *= syncSine * syncSine; + } else { + // The window is synchronous sine here + resAmpFade *= syncSine; + } + } + + sample += resSample * resAmp * resAmpFade; + } + + // sawtooth waves + if (sawtoothWaveform) { + sample *= cos(FLOAT_2PI * wavePos / waveLen); + } + + wavePos++; + + // wavePos isn't supposed to be > waveLen + if (wavePos > waveLen) { + wavePos -= waveLen; + } + } + + // Multiply sample with current TVA value + sample *= amp; + return sample; +} + +void LA32WaveGenerator::deactivate() { + active = false; +} + +bool LA32WaveGenerator::isActive() const { + return active; +} + +bool LA32WaveGenerator::isPCMWave() const { + return pcmWaveAddress != NULL; +} + +void LA32PartialPair::init(const bool ringModulated, const bool mixed) { + this->ringModulated = ringModulated; + this->mixed = mixed; + masterOutputSample = 0.0f; + slaveOutputSample = 0.0f; +} + +void LA32PartialPair::initSynth(const PairType useMaster, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance) { + if (useMaster == MASTER) { + master.initSynth(sawtoothWaveform, pulseWidth, resonance); + } else { + slave.initSynth(sawtoothWaveform, pulseWidth, resonance); + } +} + +void LA32PartialPair::initPCM(const PairType useMaster, const Bit16s *pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped) { + if (useMaster == MASTER) { + master.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, true); + } else { + slave.initPCM(pcmWaveAddress, pcmWaveLength, pcmWaveLooped, !ringModulated); + } +} + +void LA32PartialPair::generateNextSample(const PairType useMaster, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff) { + if (useMaster == MASTER) { + masterOutputSample = master.generateNextSample(amp, pitch, cutoff); + } else { + slaveOutputSample = slave.generateNextSample(amp, pitch, cutoff); + } +} + +Bit16s LA32PartialPair::nextOutSample() { + float outputSample; + if (ringModulated) { + float ringModulatedSample = masterOutputSample * slaveOutputSample; + outputSample = mixed ? masterOutputSample + ringModulatedSample : ringModulatedSample; + } else { + outputSample = masterOutputSample + slaveOutputSample; + } + return Bit16s(outputSample * 8192.0f); +} + +void LA32PartialPair::deactivate(const PairType useMaster) { + if (useMaster == MASTER) { + master.deactivate(); + masterOutputSample = 0.0f; + } else { + slave.deactivate(); + slaveOutputSample = 0.0f; + } +} + +bool LA32PartialPair::isActive(const PairType useMaster) const { + return useMaster == MASTER ? master.isActive() : slave.isActive(); +} + +} + +#endif // #if MT32EMU_ACCURATE_WG == 1 diff --git a/audio/softsynth/mt32/LegacyWaveGenerator.h b/audio/softsynth/mt32/LegacyWaveGenerator.h new file mode 100644 index 00000000000..81c1b9c7137 --- /dev/null +++ b/audio/softsynth/mt32/LegacyWaveGenerator.h @@ -0,0 +1,146 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#if MT32EMU_ACCURATE_WG == 1 + +#ifndef MT32EMU_LA32_WAVE_GENERATOR_H +#define MT32EMU_LA32_WAVE_GENERATOR_H + +namespace MT32Emu { + +/** + * LA32WaveGenerator is aimed to represent the exact model of LA32 wave generator. + * The output square wave is created by adding high / low linear segments in-between + * the rising and falling cosine segments. Basically, it’s very similar to the phase distortion synthesis. + * Behaviour of a true resonance filter is emulated by adding decaying sine wave. + * The beginning and the ending of the resonant sine is multiplied by a cosine window. + * To synthesise sawtooth waves, the resulting square wave is multiplied by synchronous cosine wave. + */ +class LA32WaveGenerator { + //*************************************************************************** + // The local copy of partial parameters below + //*************************************************************************** + + bool active; + + // True means the resulting square wave is to be multiplied by the synchronous cosine + bool sawtoothWaveform; + + // Logarithmic amp of the wave generator + Bit32u amp; + + // Logarithmic frequency of the resulting wave + Bit16u pitch; + + // Values in range [1..31] + // Value 1 correspong to the minimum resonance + Bit8u resonance; + + // Processed value in range [0..255] + // Values in range [0..128] have no effect and the resulting wave remains symmetrical + // Value 255 corresponds to the maximum possible asymmetric of the resulting wave + Bit8u pulseWidth; + + // Composed of the base cutoff in range [78..178] left-shifted by 18 bits and the TVF modifier + Bit32u cutoffVal; + + // Logarithmic PCM sample start address + const Bit16s *pcmWaveAddress; + + // Logarithmic PCM sample length + Bit32u pcmWaveLength; + + // true for looped logarithmic PCM samples + bool pcmWaveLooped; + + // false for slave PCM partials in the structures with the ring modulation + bool pcmWaveInterpolated; + + //*************************************************************************** + // Internal variables below + //*************************************************************************** + + float wavePos; + float lastFreq; + float pcmPosition; + + float getPCMSample(unsigned int position); + +public: + // Initialise the WG engine for generation of synth partial samples and set up the invariant parameters + void initSynth(const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance); + + // Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters + void initPCM(const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped, const bool pcmWaveInterpolated); + + // Update parameters with respect to TVP, TVA and TVF, and generate next sample + float generateNextSample(const Bit32u amp, const Bit16u pitch, const Bit32u cutoff); + + // Deactivate the WG engine + void deactivate(); + + // Return active state of the WG engine + bool isActive() const; + + // Return true if the WG engine generates PCM wave samples + bool isPCMWave() const; +}; + +// LA32PartialPair contains a structure of two partials being mixed / ring modulated +class LA32PartialPair { + LA32WaveGenerator master; + LA32WaveGenerator slave; + bool ringModulated; + bool mixed; + float masterOutputSample; + float slaveOutputSample; + +public: + enum PairType { + MASTER, + SLAVE + }; + + // ringModulated should be set to false for the structures with mixing or stereo output + // ringModulated should be set to true for the structures with ring modulation + // mixed is used for the structures with ring modulation and indicates whether the master partial output is mixed to the ring modulator output + void init(const bool ringModulated, const bool mixed); + + // Initialise the WG engine for generation of synth partial samples and set up the invariant parameters + void initSynth(const PairType master, const bool sawtoothWaveform, const Bit8u pulseWidth, const Bit8u resonance); + + // Initialise the WG engine for generation of PCM partial samples and set up the invariant parameters + void initPCM(const PairType master, const Bit16s * const pcmWaveAddress, const Bit32u pcmWaveLength, const bool pcmWaveLooped); + + // Update parameters with respect to TVP, TVA and TVF, and generate next sample + void generateNextSample(const PairType master, const Bit32u amp, const Bit16u pitch, const Bit32u cutoff); + + // Perform mixing / ring modulation and return the result + Bit16s nextOutSample(); + + // Deactivate the WG engine + void deactivate(const PairType master); + + // Return active state of the WG engine + bool isActive(const PairType master) const; +}; + +} // namespace MT32Emu + +#endif // #ifndef MT32EMU_LA32_WAVE_GENERATOR_H + +#endif // #if MT32EMU_ACCURATE_WG == 1 diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp index 88d42dbbd42..88404316eb9 100644 --- a/audio/softsynth/mt32/Part.cpp +++ b/audio/softsynth/mt32/Part.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -68,18 +68,16 @@ Part::Part(Synth *useSynth, unsigned int usePartNum) { activePartialCount = 0; memset(patchCache, 0, sizeof(patchCache)); for (int i = 0; i < MT32EMU_MAX_POLY; i++) { - freePolys.push_front(new Poly(this)); + freePolys.prepend(new Poly(this)); } } Part::~Part() { - while (!activePolys.empty()) { - delete activePolys.front(); - activePolys.pop_front(); + while (!activePolys.isEmpty()) { + delete activePolys.takeFirst(); } - while (!freePolys.empty()) { - delete freePolys.front(); - freePolys.pop_front(); + while (!freePolys.isEmpty()) { + delete freePolys.takeFirst(); } } @@ -177,6 +175,7 @@ void Part::refresh() { patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0; } memcpy(currentInstr, timbreTemp->common.name, 10); + synth->newTimbreSet(partNum, patchTemp->patch.timbreGroup, currentInstr); updatePitchBenderRange(); } @@ -245,8 +244,8 @@ void Part::backupCacheToPartials(PatchCache cache[4]) { // if so then duplicate the cached data from the part to the partial so that // we can change the part's cache without affecting the partial. // We delay this until now to avoid a copy operation with every note played - for (Common::List::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { - (*polyIt)->backupCacheToPartials(cache); + for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { + poly->backupCacheToPartials(cache); } } @@ -445,8 +444,7 @@ void Part::abortPoly(Poly *poly) { } bool Part::abortFirstPoly(unsigned int key) { - for (Common::List::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { - Poly *poly = *polyIt; + for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { if (poly->getKey() == key) { abortPoly(poly); return true; @@ -456,8 +454,7 @@ bool Part::abortFirstPoly(unsigned int key) { } bool Part::abortFirstPoly(PolyState polyState) { - for (Common::List::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { - Poly *poly = *polyIt; + for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { if (poly->getState() == polyState) { abortPoly(poly); return true; @@ -474,10 +471,10 @@ bool Part::abortFirstPolyPreferHeld() { } bool Part::abortFirstPoly() { - if (activePolys.empty()) { + if (activePolys.isEmpty()) { return false; } - abortPoly(activePolys.front()); + abortPoly(activePolys.getFirst()); return true; } @@ -502,17 +499,16 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt return; } - if (freePolys.empty()) { + if (freePolys.isEmpty()) { synth->printDebug("%s (%s): No free poly to play key %d (velocity %d)", name, currentInstr, midiKey, velocity); return; } - Poly *poly = freePolys.front(); - freePolys.pop_front(); + Poly *poly = freePolys.takeFirst(); if (patchTemp->patch.assignMode & 1) { // Priority to data first received - activePolys.push_front(poly); + activePolys.prepend(poly); } else { - activePolys.push_back(poly); + activePolys.append(poly); } Partial *partials[4]; @@ -537,13 +533,13 @@ void Part::playPoly(const PatchCache cache[4], const MemParams::RhythmTemp *rhyt #if MT32EMU_MONITOR_PARTIALS > 1 synth->printPartialUsage(); #endif + synth->polyStateChanged(partNum); } void Part::allNotesOff() { // The MIDI specification states - and Mok confirms - that all notes off (0x7B) // should treat the hold pedal as usual. - for (Common::List::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { - Poly *poly = *polyIt; + for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { // FIXME: This has special handling of key 0 in NoteOff that Mok has not yet confirmed applies to AllNotesOff. // if (poly->canSustain() || poly->getKey() == 0) { // FIXME: The real devices are found to be ignoring non-sustaining polys while processing AllNotesOff. Need to be confirmed. @@ -557,15 +553,13 @@ void Part::allSoundOff() { // MIDI "All sound off" (0x78) should release notes immediately regardless of the hold pedal. // This controller is not actually implemented by the synths, though (according to the docs and Mok) - // we're only using this method internally. - for (Common::List::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { - Poly *poly = *polyIt; + for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { poly->startDecay(); } } void Part::stopPedalHold() { - for (Common::List::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { - Poly *poly = *polyIt; + for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { poly->stopPedalHold(); } } @@ -583,8 +577,7 @@ void Part::stopNote(unsigned int key) { synth->printDebug("%s (%s): stopping key %d", name, currentInstr, key); #endif - for (Common::List::iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { - Poly *poly = *polyIt; + for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { // Generally, non-sustaining instruments ignore note off. They die away eventually anyway. // Key 0 (only used by special cases on rhythm part) reacts to note off even if non-sustaining or pedal held. if (poly->getKey() == key && (poly->canSustain() || key == 0)) { @@ -605,8 +598,7 @@ unsigned int Part::getActivePartialCount() const { unsigned int Part::getActiveNonReleasingPartialCount() const { unsigned int activeNonReleasingPartialCount = 0; - for (Common::List::const_iterator polyIt = activePolys.begin(); polyIt != activePolys.end(); polyIt++) { - Poly *poly = *polyIt; + for (Poly *poly = activePolys.getFirst(); poly != NULL; poly = poly->getNext()) { if (poly->getState() != POLY_Releasing) { activeNonReleasingPartialCount += poly->getActivePartialCount(); } @@ -618,7 +610,100 @@ void Part::partialDeactivated(Poly *poly) { activePartialCount--; if (!poly->isActive()) { activePolys.remove(poly); - freePolys.push_front(poly); + freePolys.prepend(poly); + synth->polyStateChanged(partNum); + } +} + +//#define POLY_LIST_DEBUG + +PolyList::PolyList() : firstPoly(NULL), lastPoly(NULL) {} + +bool PolyList::isEmpty() const { +#ifdef POLY_LIST_DEBUG + if ((firstPoly == NULL || lastPoly == NULL) && firstPoly != lastPoly) { + printf("PolyList: desynchronised firstPoly & lastPoly pointers\n"); + } +#endif + return firstPoly == NULL && lastPoly == NULL; +} + +Poly *PolyList::getFirst() const { + return firstPoly; +} + +Poly *PolyList::getLast() const { + return lastPoly; +} + +void PolyList::prepend(Poly *poly) { +#ifdef POLY_LIST_DEBUG + if (poly->getNext() != NULL) { + printf("PolyList: Non-NULL next field in a Poly being prepended is ignored\n"); + } +#endif + poly->setNext(firstPoly); + firstPoly = poly; + if (lastPoly == NULL) { + lastPoly = poly; + } +} + +void PolyList::append(Poly *poly) { +#ifdef POLY_LIST_DEBUG + if (poly->getNext() != NULL) { + printf("PolyList: Non-NULL next field in a Poly being appended is ignored\n"); + } +#endif + poly->setNext(NULL); + if (lastPoly != NULL) { +#ifdef POLY_LIST_DEBUG + if (lastPoly->getNext() != NULL) { + printf("PolyList: Non-NULL next field in the lastPoly\n"); + } +#endif + lastPoly->setNext(poly); + } + lastPoly = poly; + if (firstPoly == NULL) { + firstPoly = poly; + } +} + +Poly *PolyList::takeFirst() { + Poly *oldFirst = firstPoly; + firstPoly = oldFirst->getNext(); + if (firstPoly == NULL) { +#ifdef POLY_LIST_DEBUG + if (lastPoly != oldFirst) { + printf("PolyList: firstPoly != lastPoly in a list with a single Poly\n"); + } +#endif + lastPoly = NULL; + } + oldFirst->setNext(NULL); + return oldFirst; +} + +void PolyList::remove(Poly * const polyToRemove) { + if (polyToRemove == firstPoly) { + takeFirst(); + return; + } + for (Poly *poly = firstPoly; poly != NULL; poly = poly->getNext()) { + if (poly->getNext() == polyToRemove) { + if (polyToRemove == lastPoly) { +#ifdef POLY_LIST_DEBUG + if (lastPoly->getNext() != NULL) { + printf("PolyList: Non-NULL next field in the lastPoly\n"); + } +#endif + lastPoly = poly; + } + poly->setNext(polyToRemove->getNext()); + polyToRemove->setNext(NULL); + break; + } } } diff --git a/audio/softsynth/mt32/Part.h b/audio/softsynth/mt32/Part.h index 5c59c6d61f4..b6585880fed 100644 --- a/audio/softsynth/mt32/Part.h +++ b/audio/softsynth/mt32/Part.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,13 +18,27 @@ #ifndef MT32EMU_PART_H #define MT32EMU_PART_H -#include - namespace MT32Emu { class PartialManager; class Synth; +class PolyList { +private: + Poly *firstPoly; + Poly *lastPoly; + +public: + PolyList(); + bool isEmpty() const; + Poly *getFirst() const; + Poly *getLast() const; + void prepend(Poly *poly); + void append(Poly *poly); + Poly *takeFirst(); + void remove(Poly * const poly); +}; + class Part { private: // Direct pointer to sysex-addressable memory dedicated to this part (valid for parts 1-8, NULL for rhythm) @@ -37,8 +51,8 @@ private: unsigned int activePartialCount; PatchCache patchCache[4]; - Common::List freePolys; - Common::List activePolys; + PolyList freePolys; + PolyList activePolys; void setPatch(const PatchParam *patch); unsigned int midiKeyToKey(unsigned int midiKey); diff --git a/audio/softsynth/mt32/Partial.cpp b/audio/softsynth/mt32/Partial.cpp index a6d164f2184..b80a0285154 100644 --- a/audio/softsynth/mt32/Partial.cpp +++ b/audio/softsynth/mt32/Partial.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -35,7 +35,12 @@ static const float PAN_NUMERATOR_MASTER[] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, static const float PAN_NUMERATOR_SLAVE[] = {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f}; Partial::Partial(Synth *useSynth, int useDebugPartialNum) : - synth(useSynth), debugPartialNum(useDebugPartialNum), sampleNum(0), tva(new TVA(this, &Ramp)), tvp(new TVP(this)), tvf(new TVF(this, &cutoffModifierRamp)) { + synth(useSynth), debugPartialNum(useDebugPartialNum), sampleNum(0) { + // Initialisation of tva, tvp and tvf uses 'this' pointer + // and thus should not be in the initializer list to avoid a compiler warning + tva = new TVA(this, &Ramp); + tvp = new TVP(this); + tvf = new TVF(this, &cutoffModifierRamp); ownerPart = -1; poly = NULL; pair = NULL; @@ -81,26 +86,22 @@ void Partial::deactivate() { ownerPart = -1; if (poly != NULL) { poly->partialDeactivated(this); - if (pair != NULL) { - pair->pair = NULL; - } } #if MT32EMU_MONITOR_PARTIALS > 2 synth->printDebug("[+%lu] [Partial %d] Deactivated", sampleNum, debugPartialNum); synth->printPartialUsage(sampleNum); #endif -} - -// DEPRECATED: This should probably go away eventually, it's currently only used as a kludge to protect our old assumptions that -// rhythm part notes were always played as key MIDDLEC. -int Partial::getKey() const { - if (poly == NULL) { - return -1; - } else if (ownerPart == 8) { - // FIXME: Hack, should go away after new pitch stuff is committed (and possibly some TVF changes) - return MIDDLEC; + if (isRingModulatingSlave()) { + pair->la32Pair.deactivate(LA32PartialPair::SLAVE); } else { - return poly->getKey(); + la32Pair.deactivate(LA32PartialPair::MASTER); + if (hasRingModulatingSlave()) { + pair->deactivate(); + pair = NULL; + } + } + if (pair != NULL) { + pair->pair = NULL; } } @@ -163,8 +164,6 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us pcmWave = &synth->pcmWaves[pcmNum]; } else { pcmWave = NULL; - wavePos = 0.0f; - lastFreq = 0.0; } // CONFIRMED: pulseWidthVal calculation is based on information from Mok @@ -175,26 +174,65 @@ void Partial::startPartial(const Part *part, Poly *usePoly, const PatchCache *us pulseWidthVal = 255; } - pcmPosition = 0.0f; pair = pairPartial; alreadyOutputed = false; tva->reset(part, patchCache->partialParam, rhythmTemp); tvp->reset(part, patchCache->partialParam); tvf->reset(patchCache->partialParam, tvp->getBasePitch()); -} -float Partial::getPCMSample(unsigned int position) { - if (position >= pcmWave->len) { - if (!pcmWave->loop) { - return 0; - } - position = position % pcmWave->len; + LA32PartialPair::PairType pairType; + LA32PartialPair *useLA32Pair; + if (isRingModulatingSlave()) { + pairType = LA32PartialPair::SLAVE; + useLA32Pair = &pair->la32Pair; + } else { + pairType = LA32PartialPair::MASTER; + la32Pair.init(hasRingModulatingSlave(), mixType == 1); + useLA32Pair = &la32Pair; } - return synth->pcmROMData[pcmWave->addr + position]; + if (isPCM()) { + useLA32Pair->initPCM(pairType, &synth->pcmROMData[pcmWave->addr], pcmWave->len, pcmWave->loop); + } else { + useLA32Pair->initSynth(pairType, (patchCache->waveform & 1) != 0, pulseWidthVal, patchCache->srcPartial.tvf.resonance + 1); + } + if (!hasRingModulatingSlave()) { + la32Pair.deactivate(LA32PartialPair::SLAVE); + } + // Temporary integration hack + stereoVolume.leftVol /= 8192.0f; + stereoVolume.rightVol /= 8192.0f; } -unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) { - const Tables &tables = Tables::getInstance(); +Bit32u Partial::getAmpValue() { + // SEMI-CONFIRMED: From sample analysis: + // (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc. + // This gives results within +/- 2 at the output (before any DAC bitshifting) + // when sustaining at levels 156 - 255 with no modifiers. + // (2) Tested with a special square wave partial (internal capture ID tva5) at TVA envelope levels 155-255. + // This gives deltas between -1 and 0 compared to the real output. Note that this special partial only produces + // positive amps, so negative still needs to be explored, as well as lower levels. + // + // Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing. + // TODO: The tests above were performed using the float model, to be refined + Bit32u ampRampVal = 67117056 - ampRamp.nextValue(); + if (ampRamp.checkInterrupt()) { + tva->handleInterrupt(); + } + return ampRampVal; +} + +Bit32u Partial::getCutoffValue() { + if (isPCM()) { + return 0; + } + Bit32u cutoffModifierRampVal = cutoffModifierRamp.nextValue(); + if (cutoffModifierRamp.checkInterrupt()) { + tvf->handleInterrupt(); + } + return (tvf->getBaseCutoff() << 18) + cutoffModifierRampVal; +} + +unsigned long Partial::generateSamples(Bit16s *partialBuf, unsigned long length) { if (!isActive() || alreadyOutputed) { return 0; } @@ -202,303 +240,31 @@ unsigned long Partial::generateSamples(float *partialBuf, unsigned long length) synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::generateSamples()!", debugPartialNum); return 0; } - alreadyOutputed = true; - // Generate samples - for (sampleNum = 0; sampleNum < length; sampleNum++) { - float sample = 0; - Bit32u ampRampVal = ampRamp.nextValue(); - if (ampRamp.checkInterrupt()) { - tva->handleInterrupt(); - } - if (!tva->isPlaying()) { + if (!tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::MASTER)) { deactivate(); break; } - - Bit16u pitch = tvp->nextPitch(); - - // SEMI-CONFIRMED: From sample analysis: - // (1) Tested with a single partial playing PCM wave 77 with pitchCoarse 36 and no keyfollow, velocity follow, etc. - // This gives results within +/- 2 at the output (before any DAC bitshifting) - // when sustaining at levels 156 - 255 with no modifiers. - // (2) Tested with a special square wave partial (internal capture ID tva5) at TVA envelope levels 155-255. - // This gives deltas between -1 and 0 compared to the real output. Note that this special partial only produces - // positive amps, so negative still needs to be explored, as well as lower levels. - // - // Also still partially unconfirmed is the behaviour when ramping between levels, as well as the timing. - -#if MT32EMU_ACCURATE_WG == 1 - float amp = EXP2F((32772 - ampRampVal / 2048) / -2048.0f); - float freq = EXP2F(pitch / 4096.0f - 16.0f) * 32000.0f; -#else - static const float ampFactor = EXP2F(32772 / -2048.0f); - float amp = EXP2I(ampRampVal >> 10) * ampFactor; - - static const float freqFactor = EXP2F(-16.0f) * 32000.0f; - float freq = EXP2I(pitch) * freqFactor; -#endif - - if (patchCache->PCMPartial) { - // Render PCM waveform - int len = pcmWave->len; - int intPCMPosition = (int)pcmPosition; - if (intPCMPosition >= len && !pcmWave->loop) { - // We're now past the end of a non-looping PCM waveform so it's time to die. - deactivate(); - break; - } - Bit32u pcmAddr = pcmWave->addr; - float positionDelta = freq * 2048.0f / synth->myProp.sampleRate; - - // Linear interpolation - float firstSample = synth->pcmROMData[pcmAddr + intPCMPosition]; - // We observe that for partial structures with ring modulation the interpolation is not applied to the slave PCM partial. - // It's assumed that the multiplication circuitry intended to perform the interpolation on the slave PCM partial - // is borrowed by the ring modulation circuit (or the LA32 chip has a similar lack of resources assigned to each partial pair). - if (pair == NULL || mixType == 0 || structurePosition == 0) { - sample = firstSample + (getPCMSample(intPCMPosition + 1) - firstSample) * (pcmPosition - intPCMPosition); - } else { - sample = firstSample; - } - - float newPCMPosition = pcmPosition + positionDelta; - if (pcmWave->loop) { - newPCMPosition = fmod(newPCMPosition, (float)pcmWave->len); - } - pcmPosition = newPCMPosition; - } else { - // Render synthesised waveform - wavePos *= lastFreq / freq; - lastFreq = freq; - - Bit32u cutoffModifierRampVal = cutoffModifierRamp.nextValue(); - if (cutoffModifierRamp.checkInterrupt()) { - tvf->handleInterrupt(); - } - float cutoffModifier = cutoffModifierRampVal / 262144.0f; - - // res corresponds to a value set in an LA32 register - Bit8u res = patchCache->srcPartial.tvf.resonance + 1; - - // Using tiny exact table for computation of EXP2F(1.0f - (32 - res) / 4.0f) - float resAmp = tables.resAmpMax[res]; - - // The cutoffModifier may not be supposed to be directly added to the cutoff - - // it may for example need to be multiplied in some way. - // The 240 cutoffVal limit was determined via sample analysis (internal Munt capture IDs: glop3, glop4). - // More research is needed to be sure that this is correct, however. - float cutoffVal = tvf->getBaseCutoff() + cutoffModifier; - if (cutoffVal > 240.0f) { - cutoffVal = 240.0f; - } - - // Wave length in samples - float waveLen = synth->myProp.sampleRate / freq; - - // Init cosineLen - float cosineLen = 0.5f * waveLen; - if (cutoffVal > 128.0f) { -#if MT32EMU_ACCURATE_WG == 1 - cosineLen *= EXP2F((cutoffVal - 128.0f) / -16.0f); // found from sample analysis -#else - static const float cosineLenFactor = EXP2F(128.0f / -16.0f); - cosineLen *= EXP2I(Bit32u((256.0f - cutoffVal) * 256.0f)) * cosineLenFactor; -#endif - } - - // Start playing in center of first cosine segment - // relWavePos is shifted by a half of cosineLen - float relWavePos = wavePos + 0.5f * cosineLen; - if (relWavePos > waveLen) { - relWavePos -= waveLen; - } - - float pulseLen = 0.5f; - if (pulseWidthVal > 128) { - pulseLen += tables.pulseLenFactor[pulseWidthVal - 128]; - } - pulseLen *= waveLen; - - float lLen = pulseLen - cosineLen; - - // Ignore pulsewidths too high for given freq - if (lLen < 0.0f) { - lLen = 0.0f; - } - - // Ignore pulsewidths too high for given freq and cutoff - float hLen = waveLen - lLen - 2 * cosineLen; - if (hLen < 0.0f) { - hLen = 0.0f; - } - - // Correct resAmp for cutoff in range 50..66 - if ((cutoffVal >= 128.0f) && (cutoffVal < 144.0f)) { -#if MT32EMU_ACCURATE_WG == 1 - resAmp *= sinf(FLOAT_PI * (cutoffVal - 128.0f) / 32.0f); -#else - resAmp *= tables.sinf10[Bit32u(64 * (cutoffVal - 128.0f))]; -#endif - } - - // Produce filtered square wave with 2 cosine waves on slopes - - // 1st cosine segment - if (relWavePos < cosineLen) { -#if MT32EMU_ACCURATE_WG == 1 - sample = -cosf(FLOAT_PI * relWavePos / cosineLen); -#else - sample = -tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) + 1024]; -#endif - } else - - // high linear segment - if (relWavePos < (cosineLen + hLen)) { - sample = 1.f; - } else - - // 2nd cosine segment - if (relWavePos < (2 * cosineLen + hLen)) { -#if MT32EMU_ACCURATE_WG == 1 - sample = cosf(FLOAT_PI * (relWavePos - (cosineLen + hLen)) / cosineLen); -#else - sample = tables.sinf10[Bit32u(2048.0f * (relWavePos - (cosineLen + hLen)) / cosineLen) + 1024]; -#endif - } else { - - // low linear segment - sample = -1.f; - } - - if (cutoffVal < 128.0f) { - - // Attenuate samples below cutoff 50 - // Found by sample analysis -#if MT32EMU_ACCURATE_WG == 1 - sample *= EXP2F(-0.125f * (128.0f - cutoffVal)); -#else - static const float cutoffAttenuationFactor = EXP2F(-0.125f * 128.0f); - sample *= EXP2I(Bit32u(512.0f * cutoffVal)) * cutoffAttenuationFactor; -#endif - } else { - - // Add resonance sine. Effective for cutoff > 50 only - float resSample = 1.0f; - - // Now relWavePos counts from the middle of first cosine - relWavePos = wavePos; - - // negative segments - if (!(relWavePos < (cosineLen + hLen))) { - resSample = -resSample; - relWavePos -= cosineLen + hLen; + la32Pair.generateNextSample(LA32PartialPair::MASTER, getAmpValue(), tvp->nextPitch(), getCutoffValue()); + if (hasRingModulatingSlave()) { + la32Pair.generateNextSample(LA32PartialPair::SLAVE, pair->getAmpValue(), pair->tvp->nextPitch(), pair->getCutoffValue()); + if (!pair->tva->isPlaying() || !la32Pair.isActive(LA32PartialPair::SLAVE)) { + pair->deactivate(); + if (mixType == 2) { + deactivate(); + break; } - - // Resonance sine WG -#if MT32EMU_ACCURATE_WG == 1 - resSample *= sinf(FLOAT_PI * relWavePos / cosineLen); -#else - resSample *= tables.sinf10[Bit32u(2048.0f * relWavePos / cosineLen) & 4095]; -#endif - - // Resonance sine amp - float resAmpFadeLog2 = -tables.resAmpFadeFactor[res >> 2] * (relWavePos / cosineLen); // seems to be exact -#if MT32EMU_ACCURATE_WG == 1 - float resAmpFade = EXP2F(resAmpFadeLog2); -#else - static const float resAmpFadeFactor = EXP2F(-30.0f); - float resAmpFade = (resAmpFadeLog2 < -30.0f) ? 0.0f : EXP2I(Bit32u((30.0f + resAmpFadeLog2) * 4096.0f)) * resAmpFadeFactor; -#endif - - // Now relWavePos set negative to the left from center of any cosine - relWavePos = wavePos; - - // negative segment - if (!(wavePos < (waveLen - 0.5f * cosineLen))) { - relWavePos -= waveLen; - } else - - // positive segment - if (!(wavePos < (hLen + 0.5f * cosineLen))) { - relWavePos -= cosineLen + hLen; - } - - // Fading to zero while within cosine segments to avoid jumps in the wave - // Sample analysis suggests that this window is very close to cosine - if (relWavePos < 0.5f * cosineLen) { -#if MT32EMU_ACCURATE_WG == 1 - resAmpFade *= 0.5f * (1.0f - cosf(FLOAT_PI * relWavePos / (0.5f * cosineLen))); -#else - resAmpFade *= 0.5f * (1.0f + tables.sinf10[Bit32s(2048.0f * relWavePos / (0.5f * cosineLen)) + 3072]); -#endif - } - - sample += resSample * resAmp * resAmpFade; - } - - // sawtooth waves - if ((patchCache->waveform & 1) != 0) { -#if MT32EMU_ACCURATE_WG == 1 - sample *= cosf(FLOAT_2PI * wavePos / waveLen); -#else - sample *= tables.sinf10[(Bit32u(4096.0f * wavePos / waveLen) & 4095) + 1024]; -#endif - } - - wavePos++; - - // wavePos isn't supposed to be > waveLen - if (wavePos > waveLen) { - wavePos -= waveLen; } } - - // Multiply sample with current TVA value - sample *= amp; - *partialBuf++ = sample; + *partialBuf++ = la32Pair.nextOutSample(); } unsigned long renderedSamples = sampleNum; sampleNum = 0; return renderedSamples; } -float *Partial::mixBuffersRingMix(float *buf1, float *buf2, unsigned long len) { - if (buf1 == NULL) { - return NULL; - } - if (buf2 == NULL) { - return buf1; - } - - while (len--) { - // FIXME: At this point we have no idea whether this is remotely correct... - *buf1 = *buf1 * *buf2 + *buf1; - buf1++; - buf2++; - } - return buf1; -} - -float *Partial::mixBuffersRing(float *buf1, float *buf2, unsigned long len) { - if (buf1 == NULL) { - return NULL; - } - if (buf2 == NULL) { - return NULL; - } - - while (len--) { - // FIXME: At this point we have no idea whether this is remotely correct... - *buf1 = *buf1 * *buf2; - buf1++; - buf2++; - } - return buf1; -} - bool Partial::hasRingModulatingSlave() const { return pair != NULL && structurePosition == 0 && (mixType == 1 || mixType == 2); } @@ -530,53 +296,14 @@ bool Partial::produceOutput(float *leftBuf, float *rightBuf, unsigned long lengt synth->printDebug("[Partial %d] *** ERROR: poly is NULL at Partial::produceOutput()!", debugPartialNum); return false; } - - float *partialBuf = &myBuffer[0]; - unsigned long numGenerated = generateSamples(partialBuf, length); - if (mixType == 1 || mixType == 2) { - float *pairBuf; - unsigned long pairNumGenerated; - if (pair == NULL) { - pairBuf = NULL; - pairNumGenerated = 0; - } else { - pairBuf = &pair->myBuffer[0]; - pairNumGenerated = pair->generateSamples(pairBuf, numGenerated); - // pair will have been set to NULL if it deactivated within generateSamples() - if (pair != NULL) { - if (!isActive()) { - pair->deactivate(); - pair = NULL; - } else if (!pair->isActive()) { - pair = NULL; - } - } - } - if (pairNumGenerated > 0) { - if (mixType == 1) { - mixBuffersRingMix(partialBuf, pairBuf, pairNumGenerated); - } else { - mixBuffersRing(partialBuf, pairBuf, pairNumGenerated); - } - } - if (numGenerated > pairNumGenerated) { - if (mixType == 2) { - numGenerated = pairNumGenerated; - deactivate(); - } - } - } - + unsigned long numGenerated = generateSamples(myBuffer, length); for (unsigned int i = 0; i < numGenerated; i++) { - *leftBuf++ = partialBuf[i] * stereoVolume.leftVol; + *leftBuf++ = myBuffer[i] * stereoVolume.leftVol; + *rightBuf++ = myBuffer[i] * stereoVolume.rightVol; } - for (unsigned int i = 0; i < numGenerated; i++) { - *rightBuf++ = partialBuf[i] * stereoVolume.rightVol; - } - while (numGenerated < length) { + for (; numGenerated < length; numGenerated++) { *leftBuf++ = 0.0f; *rightBuf++ = 0.0f; - numGenerated++; } return true; } diff --git a/audio/softsynth/mt32/Partial.h b/audio/softsynth/mt32/Partial.h index 5e250769ec4..21b1bfe376d 100644 --- a/audio/softsynth/mt32/Partial.h +++ b/audio/softsynth/mt32/Partial.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -44,12 +44,7 @@ private: int structurePosition; // 0 or 1 of a structure pair StereoVolume stereoVolume; - // Distance in (possibly fractional) samples from the start of the current pulse - float wavePos; - - float lastFreq; - - float myBuffer[MAX_SAMPLES_PER_RUN]; + Bit16s myBuffer[MAX_SAMPLES_PER_RUN]; // Only used for PCM partials int pcmNum; @@ -60,17 +55,16 @@ private: // Range: 0-255 int pulseWidthVal; - float pcmPosition; - Poly *poly; LA32Ramp ampRamp; LA32Ramp cutoffModifierRamp; - float *mixBuffersRingMix(float *buf1, float *buf2, unsigned long len); - float *mixBuffersRing(float *buf1, float *buf2, unsigned long len); + // TODO: This should be owned by PartialPair + LA32PartialPair la32Pair; - float getPCMSample(unsigned int position); + Bit32u getAmpValue(); + Bit32u getCutoffValue(); public: const PatchCache *patchCache; @@ -90,7 +84,6 @@ public: unsigned long debugGetSampleNum() const; int getOwnerPart() const; - int getKey() const; const Poly *getPoly() const; bool isActive() const; void activate(int part); @@ -111,7 +104,7 @@ public: bool produceOutput(float *leftBuf, float *rightBuf, unsigned long length); // This function writes mono sample output to the provided buffer, and returns the number of samples written - unsigned long generateSamples(float *partialBuf, unsigned long length); + unsigned long generateSamples(Bit16s *partialBuf, unsigned long length); }; } diff --git a/audio/softsynth/mt32/PartialManager.cpp b/audio/softsynth/mt32/PartialManager.cpp index f8c2dbcd483..436e7a353e7 100644 --- a/audio/softsynth/mt32/PartialManager.cpp +++ b/audio/softsynth/mt32/PartialManager.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/audio/softsynth/mt32/PartialManager.h b/audio/softsynth/mt32/PartialManager.h index bb78672457f..a1c9266ea1a 100644 --- a/audio/softsynth/mt32/PartialManager.h +++ b/audio/softsynth/mt32/PartialManager.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/audio/softsynth/mt32/Poly.cpp b/audio/softsynth/mt32/Poly.cpp index c45391f6720..46574f8967e 100644 --- a/audio/softsynth/mt32/Poly.cpp +++ b/audio/softsynth/mt32/Poly.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -29,6 +29,7 @@ Poly::Poly(Part *usePart) { partials[i] = NULL; } state = POLY_Inactive; + next = NULL; } void Poly::reset(unsigned int newKey, unsigned int newVelocity, bool newSustain, Partial **newPartials) { @@ -174,4 +175,12 @@ void Poly::partialDeactivated(Partial *partial) { part->partialDeactivated(this); } +Poly *Poly::getNext() { + return next; +} + +void Poly::setNext(Poly *poly) { + next = poly; +} + } diff --git a/audio/softsynth/mt32/Poly.h b/audio/softsynth/mt32/Poly.h index cd15a776f53..068cf73d35c 100644 --- a/audio/softsynth/mt32/Poly.h +++ b/audio/softsynth/mt32/Poly.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -41,6 +41,8 @@ private: Partial *partials[4]; + Poly *next; + public: Poly(Part *part); void reset(unsigned int key, unsigned int velocity, bool sustain, Partial **partials); @@ -60,6 +62,9 @@ public: bool isActive() const; void partialDeactivated(Partial *partial); + + Poly *getNext(); + void setNext(Poly *poly); }; } diff --git a/audio/softsynth/mt32/ROMInfo.cpp b/audio/softsynth/mt32/ROMInfo.cpp new file mode 100644 index 00000000000..9d05420874f --- /dev/null +++ b/audio/softsynth/mt32/ROMInfo.cpp @@ -0,0 +1,111 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +//#include +#include "ROMInfo.h" + +namespace MT32Emu { + +// Known ROMs +static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL, NULL}; +static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL, NULL}; +static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL, NULL}; + +static const ROMInfo * const ROM_INFOS[] = { + &CTRL_MT32_V1_04, + &CTRL_MT32_V1_05, + &CTRL_MT32_V1_06, + &CTRL_MT32_V1_07, + &CTRL_MT32_BLUER, + &CTRL_CM32L_V1_00, + &CTRL_CM32L_V1_02, + &PCM_MT32, + &PCM_CM32L, + NULL}; + +const ROMInfo* ROMInfo::getROMInfo(Common::File *file) { + size_t fileSize = file->size(); + // We haven't added the SHA1 checksum code in ScummVM, as the file size + // suffices for our needs for now. + //const char *fileDigest = file->getSHA1(); + for (int i = 0; ROM_INFOS[i] != NULL; i++) { + const ROMInfo *romInfo = ROM_INFOS[i]; + if (fileSize == romInfo->fileSize /*&& !strcmp(fileDigest, romInfo->sha1Digest)*/) { + return romInfo; + } + } + return NULL; +} + +void ROMInfo::freeROMInfo(const ROMInfo *romInfo) { + (void) romInfo; +} + +static int getROMCount() { + int count; + for(count = 0; ROM_INFOS[count] != NULL; count++) { + } + return count; +} + +const ROMInfo** ROMInfo::getROMInfoList(unsigned int types, unsigned int pairTypes) { + const ROMInfo **romInfoList = new const ROMInfo*[getROMCount() + 1]; + const ROMInfo **currentROMInList = romInfoList; + for(int i = 0; ROM_INFOS[i] != NULL; i++) { + const ROMInfo *romInfo = ROM_INFOS[i]; + if ((types & (1 << romInfo->type)) && (pairTypes & (1 << romInfo->pairType))) { + *currentROMInList++ = romInfo; + } + } + *currentROMInList = NULL; + return romInfoList; +} + +void ROMInfo::freeROMInfoList(const ROMInfo **romInfoList) { + delete[] romInfoList; +} + +const ROMImage* ROMImage::makeROMImage(Common::File *file) { + ROMImage *romImage = new ROMImage; + romImage->file = file; + romImage->romInfo = ROMInfo::getROMInfo(romImage->file); + return romImage; +} + +void ROMImage::freeROMImage(const ROMImage *romImage) { + ROMInfo::freeROMInfo(romImage->romInfo); + delete romImage; +} + + +Common::File* ROMImage::getFile() const { + return file; +} + +const ROMInfo* ROMImage::getROMInfo() const { + return romInfo; +} + +} diff --git a/audio/softsynth/mt32/ROMInfo.h b/audio/softsynth/mt32/ROMInfo.h new file mode 100644 index 00000000000..1d837dc5ee4 --- /dev/null +++ b/audio/softsynth/mt32/ROMInfo.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef MT32EMU_ROMINFO_H +#define MT32EMU_ROMINFO_H + +//#include +#include "common/file.h" + +namespace MT32Emu { + +// Defines vital info about ROM file to be used by synth and applications + +struct ROMInfo { +public: + size_t fileSize; + const char *sha1Digest; + enum Type {PCM, Control, Reverb} type; + const char *shortName; + const char *description; + enum PairType {Full, FirstHalf, SecondHalf, Mux0, Mux1} pairType; + ROMInfo *pairROMInfo; + void *controlROMInfo; + + // Returns a ROMInfo struct by inspecting the size and the SHA1 hash + static const ROMInfo* getROMInfo(Common::File *file); + + // Currently no-op + static void freeROMInfo(const ROMInfo *romInfo); + + // Allows retrieving a NULL-terminated list of ROMInfos for a range of types and pairTypes + // (specified by bitmasks) + // Useful for GUI/console app to output information on what ROMs it supports + static const ROMInfo** getROMInfoList(unsigned int types, unsigned int pairTypes); + + // Frees the list of ROMInfos given + static void freeROMInfoList(const ROMInfo **romInfos); +}; + +// Synth::open() is to require a full control ROMImage and a full PCM ROMImage to work + +class ROMImage { +private: + Common::File *file; + const ROMInfo *romInfo; + +public: + + // Creates a ROMImage object given a ROMInfo and a File. Keeps a reference + // to the File and ROMInfo given, which must be freed separately by the user + // after the ROMImage is freed + static const ROMImage* makeROMImage(Common::File *file); + + // Must only be done after all Synths using the ROMImage are deleted + static void freeROMImage(const ROMImage *romImage); + + Common::File *getFile() const; + const ROMInfo *getROMInfo() const; +}; + +} + +#endif diff --git a/audio/softsynth/mt32/Structures.h b/audio/softsynth/mt32/Structures.h index cbce89ae188..43d2d1f2264 100644 --- a/audio/softsynth/mt32/Structures.h +++ b/audio/softsynth/mt32/Structures.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp index f37d76cd854..1e1be06bc96 100644 --- a/audio/softsynth/mt32/Synth.cpp +++ b/audio/softsynth/mt32/Synth.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -27,8 +27,10 @@ #include "mmath.h" #include "PartialManager.h" -#if MT32EMU_USE_AREVERBMODEL == 1 +#if MT32EMU_USE_REVERBMODEL == 1 #include "AReverbModel.h" +#elif MT32EMU_USE_REVERBMODEL == 2 +#include "BReverbModel.h" #else #include "FreeverbModel.h" #endif @@ -140,22 +142,36 @@ Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) { return checksum; } -Synth::Synth() { +Synth::Synth(ReportHandler *useReportHandler) { isOpen = false; reverbEnabled = true; reverbOverridden = false; -#if MT32EMU_USE_AREVERBMODEL == 1 - reverbModels[0] = new AReverbModel(&AReverbModel::REVERB_MODE_0_SETTINGS); - reverbModels[1] = new AReverbModel(&AReverbModel::REVERB_MODE_1_SETTINGS); - reverbModels[2] = new AReverbModel(&AReverbModel::REVERB_MODE_2_SETTINGS); + if (useReportHandler == NULL) { + reportHandler = new ReportHandler; + isDefaultReportHandler = true; + } else { + reportHandler = useReportHandler; + isDefaultReportHandler = false; + } + +#if MT32EMU_USE_REVERBMODEL == 1 + reverbModels[REVERB_MODE_ROOM] = new AReverbModel(REVERB_MODE_ROOM); + reverbModels[REVERB_MODE_HALL] = new AReverbModel(REVERB_MODE_HALL); + reverbModels[REVERB_MODE_PLATE] = new AReverbModel(REVERB_MODE_PLATE); + reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb(); +#elif MT32EMU_USE_REVERBMODEL == 2 + reverbModels[REVERB_MODE_ROOM] = new BReverbModel(REVERB_MODE_ROOM); + reverbModels[REVERB_MODE_HALL] = new BReverbModel(REVERB_MODE_HALL); + reverbModels[REVERB_MODE_PLATE] = new BReverbModel(REVERB_MODE_PLATE); + reverbModels[REVERB_MODE_TAP_DELAY] = new BReverbModel(REVERB_MODE_TAP_DELAY); #else - reverbModels[0] = new FreeverbModel(0.76f, 0.687770909f, 0.63f, 0, 0.5f); - reverbModels[1] = new FreeverbModel(2.0f, 0.712025098f, 0.86f, 1, 0.5f); - reverbModels[2] = new FreeverbModel(0.4f, 0.939522749f, 0.38f, 2, 0.05f); + reverbModels[REVERB_MODE_ROOM] = new FreeverbModel(0.76f, 0.687770909f, 0.63f, 0, 0.5f); + reverbModels[REVERB_MODE_HALL] = new FreeverbModel(2.0f, 0.712025098f, 0.86f, 1, 0.5f); + reverbModels[REVERB_MODE_PLATE] = new FreeverbModel(0.4f, 0.939522749f, 0.38f, 2, 0.05f); + reverbModels[REVERB_MODE_TAP_DELAY] = new DelayReverb(); #endif - reverbModels[3] = new DelayReverb(); reverbModel = NULL; setDACInputMode(DACInputMode_NICE); setOutputGain(1.0f); @@ -170,31 +186,36 @@ Synth::~Synth() { for (int i = 0; i < 4; i++) { delete reverbModels[i]; } -} - -int Synth::report(ReportType type, const void *data) { - if (myProp.report != NULL) { - return myProp.report(myProp.userData, type, data); + if (isDefaultReportHandler) { + delete reportHandler; } - return 0; } -unsigned int Synth::getSampleRate() const { - return myProp.sampleRate; +void ReportHandler::showLCDMessage(const char *data) { + printf("WRITE-LCD: %s", data); + printf("\n"); +} + +void ReportHandler::printDebug(const char *fmt, va_list list) { + vprintf(fmt, list); + printf("\n"); +} + +void Synth::polyStateChanged(int partNum) { + reportHandler->onPolyStateChanged(partNum); +} + +void Synth::newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]) { + reportHandler->onProgramChanged(partNum, timbreGroup, patchName); } void Synth::printDebug(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - if (myProp.printDebug != NULL) { - myProp.printDebug(myProp.userData, fmt, ap); - } else { #if MT32EMU_DEBUG_SAMPLESTAMPS > 0 - printf("[%u] ", renderedSampleCount); + reportHandler->printDebug("[%u] ", renderedSampleCount); #endif - vprintf(fmt, ap); - printf("\n"); - } + reportHandler->printDebug(fmt, ap); va_end(ap); } @@ -244,80 +265,60 @@ void Synth::setReverbOutputGain(float newReverbOutputGain) { reverbOutputGain = newReverbOutputGain; } -Common::File *Synth::openFile(const char *filename) { - if (myProp.openFile != NULL) { - return myProp.openFile(myProp.userData, filename); - } - char pathBuf[2048]; - if (myProp.baseDir != NULL) { - strcpy(&pathBuf[0], myProp.baseDir); - strcat(&pathBuf[0], filename); - filename = pathBuf; - } - Common::File *file = new Common::File(); - if (!file->open(filename)) { - delete file; - return NULL; - } - return file; -} - -void Synth::closeFile(Common::File *file) { - if (myProp.closeFile != NULL) { - myProp.closeFile(myProp.userData, file); - } else { - file->close(); - delete file; - } -} - -LoadResult Synth::loadControlROM(const char *filename) { - Common::File *file = openFile(filename); // ROM File - if (file == NULL) { - return LoadResult_NotFound; - } - size_t fileSize = file->size(); - if (fileSize != CONTROL_ROM_SIZE) { - printDebug("Control ROM file %s size mismatch: %i", filename, fileSize); +bool Synth::loadControlROM(const ROMImage &controlROMImage) { + if (&controlROMImage == NULL) return false; + Common::File *file = controlROMImage.getFile(); + const ROMInfo *controlROMInfo = controlROMImage.getROMInfo(); + if ((controlROMInfo == NULL) + || (controlROMInfo->type != ROMInfo::Control) + || (controlROMInfo->pairType != ROMInfo::Full)) { + return false; } +#if MT32EMU_MONITOR_INIT + printDebug("Found Control ROM: %s, %s", controlROMInfo->shortName, controlROMInfo->description); +#endif file->read(controlROMData, CONTROL_ROM_SIZE); - if (file->err()) { - closeFile(file); - return LoadResult_Unreadable; - } - closeFile(file); // Control ROM successfully loaded, now check whether it's a known type controlROMMap = NULL; for (unsigned int i = 0; i < sizeof(ControlROMMaps) / sizeof(ControlROMMaps[0]); i++) { if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) { controlROMMap = &ControlROMMaps[i]; - return LoadResult_OK; + return true; } } - printDebug("%s does not match a known control ROM type", filename); - return LoadResult_Invalid; +#if MT32EMU_MONITOR_INIT + printDebug("Control ROM failed to load"); +#endif + return false; } -LoadResult Synth::loadPCMROM(const char *filename) { - Common::File *file = openFile(filename); // ROM File - if (file == NULL) { - return LoadResult_NotFound; +bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { + if (&pcmROMImage == NULL) return false; + Common::File *file = pcmROMImage.getFile(); + const ROMInfo *pcmROMInfo = pcmROMImage.getROMInfo(); + if ((pcmROMInfo == NULL) + || (pcmROMInfo->type != ROMInfo::PCM) + || (pcmROMInfo->pairType != ROMInfo::Full)) { + return false; } +#if MT32EMU_MONITOR_INIT + printDebug("Found PCM ROM: %s, %s", pcmROMInfo->shortName, pcmROMInfo->description); +#endif size_t fileSize = file->size(); - if (fileSize < (size_t)(2 * pcmROMSize)) { - printDebug("PCM ROM file is too short (expected %d, got %d)", 2 * pcmROMSize, fileSize); - closeFile(file); - return LoadResult_Invalid; + if (fileSize != (2 * pcmROMSize)) { +#if MT32EMU_MONITOR_INIT + printDebug("PCM ROM file has wrong size (expected %d, got %d)", 2 * pcmROMSize, fileSize); +#endif + return false; } - if (file->err()) { - closeFile(file); - return LoadResult_Unreadable; - } - LoadResult rc = LoadResult_OK; - for (int i = 0; i < pcmROMSize; i++) { - Bit8u s = file->readByte(); - Bit8u c = file->readByte(); + + byte *buffer = new byte[file->size()]; + file->read(buffer, file->size()); + const byte *fileData = buffer; + for (size_t i = 0; i < pcmROMSize; i++) { + Bit8u s = *(fileData++); + Bit8u c = *(fileData++); int order[16] = {0, 9, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8}; @@ -331,28 +332,20 @@ LoadResult Synth::loadPCMROM(const char *filename) { } log = log | (short)(bit << (15 - u)); } - bool negative = log < 0; - log &= 0x7FFF; - - // CONFIRMED from sample analysis to be 99.99%+ accurate with current TVA multiplier - float lin = EXP2F((32787 - log) / -2048.0f); - - if (negative) { - lin = -lin; - } - - pcmROMData[i] = lin; + pcmROMData[i] = log; } - closeFile(file); - return rc; + + delete[] buffer; + + return true; } bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) { ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress]; for (int i = 0; i < count; i++) { - int rAddr = tps[i].pos * 0x800; - int rLenExp = (tps[i].len & 0x70) >> 4; - int rLen = 0x800 << rLenExp; + size_t rAddr = tps[i].pos * 0x800; + size_t rLenExp = (tps[i].len & 0x70) >> 4; + size_t rLen = 0x800 << rLenExp; if (rAddr + rLen > pcmROMSize) { printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen); return false; @@ -414,12 +407,11 @@ bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int count, int startTi return true; } -bool Synth::open(SynthProperties &useProp) { +bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage) { if (isOpen) { return false; } prerenderReadIx = prerenderWriteIx = 0; - myProp = useProp; #if MT32EMU_MONITOR_INIT printDebug("Initialising Constant Tables"); #endif @@ -428,11 +420,6 @@ bool Synth::open(SynthProperties &useProp) { reverbModels[i]->open(useProp.sampleRate); } #endif - if (useProp.baseDir != NULL) { - char *baseDirCopy = new char[strlen(useProp.baseDir) + 1]; - strcpy(baseDirCopy, useProp.baseDir); - myProp.baseDir = baseDirCopy; - } // This is to help detect bugs memset(&mt32ram, '?', sizeof(mt32ram)); @@ -440,12 +427,10 @@ bool Synth::open(SynthProperties &useProp) { #if MT32EMU_MONITOR_INIT printDebug("Loading Control ROM"); #endif - if (loadControlROM("CM32L_CONTROL.ROM") != LoadResult_OK) { - if (loadControlROM("MT32_CONTROL.ROM") != LoadResult_OK) { - printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM"); - //report(ReportType_errorControlROM, &errno); - return false; - } + if (!loadControlROM(controlROMImage)) { + printDebug("Init Error - Missing or invalid Control ROM image"); + reportHandler->onErrorControlROM(); + return false; } initMemoryRegions(); @@ -454,17 +439,15 @@ bool Synth::open(SynthProperties &useProp) { // 1MB PCM ROM for CM-32L, LAPC-I, CM-64, CM-500 // Note that the size below is given in samples (16-bit), not bytes pcmROMSize = controlROMMap->pcmCount == 256 ? 512 * 1024 : 256 * 1024; - pcmROMData = new float[pcmROMSize]; + pcmROMData = new Bit16s[pcmROMSize]; #if MT32EMU_MONITOR_INIT printDebug("Loading PCM ROM"); #endif - if (loadPCMROM("CM32L_PCM.ROM") != LoadResult_OK) { - if (loadPCMROM("MT32_PCM.ROM") != LoadResult_OK) { - printDebug("Init Error - Missing MT32_PCM.ROM"); - //report(ReportType_errorPCMROM, &errno); - return false; - } + if (!loadPCMROM(pcmROMImage)) { + printDebug("Init Error - Missing PCM ROM image"); + reportHandler->onErrorPCMROM(); + return false; } #if MT32EMU_MONITOR_INIT @@ -593,9 +576,6 @@ void Synth::close() { parts[i] = NULL; } - delete[] myProp.baseDir; - myProp.baseDir = NULL; - delete[] pcmWaves; delete[] pcmROMData; @@ -1182,7 +1162,7 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le case MR_System: region->write(0, off, data, len); - report(ReportType_devReconfig, NULL); + reportHandler->onDeviceReconfig(); // FIXME: We haven't properly confirmed any of this behaviour // In particular, we tend to reset things such as reverb even if the write contained // the same parameters as were already set, which may be wrong. @@ -1220,7 +1200,7 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le #if MT32EMU_MONITOR_SYSEX > 0 printDebug("WRITE-LCD: %s", buf); #endif - report(ReportType_lcdMessage, buf); + reportHandler->showLCDMessage(buf); break; case MR_Reset: reset(); @@ -1248,9 +1228,9 @@ void Synth::refreshSystemReverbParameters() { #endif return; } - report(ReportType_newReverbMode, &mt32ram.system.reverbMode); - report(ReportType_newReverbTime, &mt32ram.system.reverbTime); - report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel); + reportHandler->onNewReverbMode(mt32ram.system.reverbMode); + reportHandler->onNewReverbTime(mt32ram.system.reverbTime); + reportHandler->onNewReverbLevel(mt32ram.system.reverbLevel); ReverbModel *newReverbModel = reverbModels[mt32ram.system.reverbMode]; #if MT32EMU_REDUCE_REVERB_MEMORY @@ -1258,7 +1238,7 @@ void Synth::refreshSystemReverbParameters() { if (reverbModel != NULL) { reverbModel->close(); } - newReverbModel->open(myProp.sampleRate); + newReverbModel->open(); } #endif reverbModel = newReverbModel; @@ -1313,7 +1293,7 @@ void Synth::reset() { #if MT32EMU_MONITOR_SYSEX > 0 printDebug("RESET"); #endif - report(ReportType_devReset, NULL); + reportHandler->onDeviceReset(); partialManager->deactivateAll(); mt32ram = mt32default; for (int i = 0; i < 9; i++) { diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h index a30df629114..b85e7ae507e 100644 --- a/audio/softsynth/mt32/Synth.h +++ b/audio/softsynth/mt32/Synth.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -26,6 +26,7 @@ class TableInitialiser; class Partial; class PartialManager; class Part; +class ROMImage; /** * Methods for emulating the connection between the LA32 and the DAC, which involves @@ -57,71 +58,6 @@ enum DACInputMode { DACInputMode_GENERATION2 }; -enum ReportType { - // Errors - ReportType_errorControlROM = 1, - ReportType_errorPCMROM, - ReportType_errorSampleRate, - - // Progress - ReportType_progressInit, - - // HW spec - ReportType_availableSSE, - ReportType_available3DNow, - ReportType_usingSSE, - ReportType_using3DNow, - - // General info - ReportType_lcdMessage, - ReportType_devReset, - ReportType_devReconfig, - ReportType_newReverbMode, - ReportType_newReverbTime, - ReportType_newReverbLevel -}; - -enum LoadResult { - LoadResult_OK, - LoadResult_NotFound, - LoadResult_Unreadable, - LoadResult_Invalid -}; - -struct SynthProperties { - // Sample rate to use in mixing - unsigned int sampleRate; - - // Deprecated - ignored. Use Synth::setReverbEnabled() instead. - bool useReverb; - // Deprecated - ignored. Use Synth::setReverbOverridden() instead. - bool useDefaultReverb; - // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. - unsigned char reverbType; - // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. - unsigned char reverbTime; - // Deprecated - ignored. Use Synth::playSysex*() to configure reverb instead. - unsigned char reverbLevel; - // The name of the directory in which the ROM and data files are stored (with trailing slash/backslash) - // Not used if "openFile" is set. May be NULL in any case. - const char *baseDir; - // This is used as the first argument to all callbacks - void *userData; - // Callback for reporting various errors and information. May be NULL - int (*report)(void *userData, ReportType type, const void *reportData); - // Callback for debug messages, in vprintf() format - void (*printDebug)(void *userData, const char *fmt, va_list list); - // Callback for providing an implementation of File, opened and ready for use - // May be NULL, in which case a default implementation will be used. - Common::File *(*openFile)(void *userData, const char *filename); - // Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted. - void (*closeFile)(void *userData, Common::File *file); -}; - -// This is the specification of the Callback routine used when calling the RecalcWaveforms -// function -typedef void (*recalcStatusCallback)(int percDone); - typedef void (*FloatToBit16sFunc)(Bit16s *target, const float *source, Bit32u len, float outputGain); const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41; @@ -179,6 +115,13 @@ enum MemoryRegionType { MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset }; +enum ReverbMode { + REVERB_MODE_ROOM, + REVERB_MODE_HALL, + REVERB_MODE_PLATE, + REVERB_MODE_TAP_DELAY +}; + class MemoryRegion { private: Synth *synth; @@ -278,7 +221,7 @@ class ReverbModel { public: virtual ~ReverbModel() {} // After construction or a close(), open() will be called at least once before any other call (with the exception of close()). - virtual void open(unsigned int sampleRate) = 0; + virtual void open() = 0; // May be called multiple times without an open() in between. virtual void close() = 0; virtual void setParameters(Bit8u time, Bit8u level) = 0; @@ -286,6 +229,32 @@ public: virtual bool isActive() const = 0; }; +class ReportHandler { +friend class Synth; + +public: + virtual ~ReportHandler() {} + +protected: + + // Callback for debug messages, in vprintf() format + virtual void printDebug(const char *fmt, va_list list); + + // Callbacks for reporting various errors and information + virtual void onErrorControlROM() {} + virtual void onErrorPCMROM() {} + virtual void showLCDMessage(const char *message); + virtual void onDeviceReset() {} + virtual void onDeviceReconfig() {} + virtual void onNewReverbMode(Bit8u /* mode */) {} + virtual void onNewReverbTime(Bit8u /* time */) {} + virtual void onNewReverbLevel(Bit8u /* level */) {} + virtual void onPartStateChanged(int /* partNum */, bool /* hasActiveNonReleasingPolys */) {} + virtual void onPolyStateChanged(int /* partNum */) {} + virtual void onPartialStateChanged(int /* partialNum */, int /* oldPartialPhase */, int /* newPartialPhase */) {} + virtual void onProgramChanged(int /* partNum */, int /* bankNum */, const char * /* patchName */) {} +}; + class Synth { friend class Part; friend class RhythmPart; @@ -314,8 +283,8 @@ private: const ControlROMMap *controlROMMap; Bit8u controlROMData[CONTROL_ROM_SIZE]; - float *pcmROMData; - int pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM + Bit16s *pcmROMData; + size_t pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM Bit8s chantable[32]; @@ -336,6 +305,9 @@ private: bool isOpen; + bool isDefaultReportHandler; + ReportHandler *reportHandler; + PartialManager *partialManager; Part *parts[9]; @@ -368,8 +340,6 @@ private: int prerenderReadIx; int prerenderWriteIx; - SynthProperties myProp; - bool prerender(); void copyPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u pos, Bit32u len); void checkPrerender(Bit16s *nonReverbLeft, Bit16s *nonReverbRight, Bit16s *reverbDryLeft, Bit16s *reverbDryRight, Bit16s *reverbWetLeft, Bit16s *reverbWetRight, Bit32u &pos, Bit32u &len); @@ -383,8 +353,8 @@ private: void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data); void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data); - LoadResult loadControlROM(const char *filename); - LoadResult loadPCMROM(const char *filename); + bool loadControlROM(const ROMImage &controlROMImage); + bool loadPCMROM(const ROMImage &pcmROMImage); bool initPCMList(Bit16u mapAddress, Bit16u count); bool initTimbres(Bit16u mapAddress, Bit16u offset, int timbreCount, int startTimbre, bool compressed); @@ -398,24 +368,23 @@ private: void refreshSystem(); void reset(); - unsigned int getSampleRate() const; - void printPartialUsage(unsigned long sampleOffset = 0); -protected: - int report(ReportType type, const void *reportData); - Common::File *openFile(const char *filename); - void closeFile(Common::File *file); + + void polyStateChanged(int partNum); + void newTimbreSet(int partNum, Bit8u timbreGroup, const char patchName[]); void printDebug(const char *fmt, ...); public: static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum); - Synth(); + // Optionally sets callbacks for reporting various errors, information and debug messages + Synth(ReportHandler *useReportHandler = NULL); ~Synth(); // Used to initialise the MT-32. Must be called before any other function. // Returns true if initialization was sucessful, otherwise returns false. - bool open(SynthProperties &useProp); + // controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth. + bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage); // Closes the MT-32 and deallocates any memory used by the synthesizer void close(void); diff --git a/audio/softsynth/mt32/TVA.cpp b/audio/softsynth/mt32/TVA.cpp index c581259a384..5438471fa4b 100644 --- a/audio/softsynth/mt32/TVA.cpp +++ b/audio/softsynth/mt32/TVA.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -30,7 +30,7 @@ namespace MT32Emu { static Bit8u biasLevelToAmpSubtractionCoeff[13] = {255, 187, 137, 100, 74, 54, 40, 29, 21, 15, 10, 5, 0}; TVA::TVA(const Partial *usePartial, LA32Ramp *useAmpRamp) : - partial(usePartial), ampRamp(useAmpRamp), system_(&usePartial->getSynth()->mt32ram.system) { + partial(usePartial), ampRamp(useAmpRamp), system_(&usePartial->getSynth()->mt32ram.system), phase(TVA_PHASE_DEAD) { } void TVA::startRamp(Bit8u newTarget, Bit8u newIncrement, int newPhase) { @@ -274,7 +274,7 @@ void TVA::nextPhase() { } int newTarget; - int newIncrement = 0; + int newIncrement = 0; // Initialised to please compilers int envPointIndex = phase; if (!allLevelsZeroFromNowOn) { diff --git a/audio/softsynth/mt32/TVA.h b/audio/softsynth/mt32/TVA.h index a104fe4c1fd..e6e5cc4bc71 100644 --- a/audio/softsynth/mt32/TVA.h +++ b/audio/softsynth/mt32/TVA.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/audio/softsynth/mt32/TVF.cpp b/audio/softsynth/mt32/TVF.cpp index 47c4917632d..49a0d85c9ee 100644 --- a/audio/softsynth/mt32/TVF.cpp +++ b/audio/softsynth/mt32/TVF.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/audio/softsynth/mt32/TVF.h b/audio/softsynth/mt32/TVF.h index 490d8de5047..22d81da7b08 100644 --- a/audio/softsynth/mt32/TVF.h +++ b/audio/softsynth/mt32/TVF.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/audio/softsynth/mt32/TVP.cpp b/audio/softsynth/mt32/TVP.cpp index 32a60678413..c3e64c18d0d 100644 --- a/audio/softsynth/mt32/TVP.cpp +++ b/audio/softsynth/mt32/TVP.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -47,12 +47,11 @@ static Bit16u keyToPitchTable[] = { TVP::TVP(const Partial *usePartial) : partial(usePartial), system_(&usePartial->getSynth()->mt32ram.system) { - unsigned int sampleRate = usePartial->getSynth()->myProp.sampleRate; // We want to do processing 4000 times per second. FIXME: This is pretty arbitrary. - maxCounter = sampleRate / 4000; + maxCounter = SAMPLE_RATE / 4000; // The timer runs at 500kHz. We only need to bother updating it every maxCounter samples, before we do processing. // This is how much to increment it by every maxCounter samples. - processTimerIncrement = 500000 * maxCounter / sampleRate; + processTimerIncrement = 500000 * maxCounter / SAMPLE_RATE; } static Bit16s keyToPitch(unsigned int key) { diff --git a/audio/softsynth/mt32/TVP.h b/audio/softsynth/mt32/TVP.h index f6f62f8d39b..cd5fb4cdc7c 100644 --- a/audio/softsynth/mt32/TVP.h +++ b/audio/softsynth/mt32/TVP.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/audio/softsynth/mt32/Tables.cpp b/audio/softsynth/mt32/Tables.cpp index b6e63840bc3..743820b1f86 100644 --- a/audio/softsynth/mt32/Tables.cpp +++ b/audio/softsynth/mt32/Tables.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -72,36 +72,25 @@ Tables::Tables() { //synth->printDebug("%d: %d", i, pulseWidth100To255[i]); } - // Ratio of negative segment to wave length - for (int i = 0; i < 128; i++) { - // Formula determined from sample analysis. - float pt = 0.5f / 127.0f * i; - pulseLenFactor[i] = (1.241857812f - pt) * pt; // seems to be 2 ^ (5 / 16) = 1.241857812f + // The LA32 chip contains an exponent table inside. The table contains 12-bit integer values. + // The actual table size is 512 rows. The 9 higher bits of the fractional part of the argument are used as a lookup address. + // To improve the precision of computations, the lower bits are supposed to be used for interpolation as the LA32 chip also + // contains another 512-row table with inverted differences between the main table values. + for (int i = 0; i < 512; i++) { + exp9[i] = Bit16u(8191.5f - EXP2F(13.0f + ~i / 512.0f)); } - // The LA32 chip presumably has such a table inside as the internal computaions seem to be performed using fixed point math with 12-bit fractions - for (int i = 0; i < 4096; i++) { - exp2[i] = EXP2F(i / 4096.0f); + // There is a logarithmic sine table inside the LA32 chip. The table contains 13-bit integer values. + for (int i = 1; i < 512; i++) { + logsin9[i] = Bit16u(0.5f - LOG2F(sin((i + 0.5f) / 1024.0f * FLOAT_PI)) * 1024.0f); } + // The very first value is clamped to the maximum possible 13-bit integer + logsin9[0] = 8191; + // found from sample analysis - for (int i = 0; i < 32; i++) { - resAmpMax[i] = EXP2F(1.0f - (32 - i) / 4.0f); - } - - // found from sample analysis - resAmpFadeFactor[7] = 1.0f / 8.0f; - resAmpFadeFactor[6] = 2.0f / 8.0f; - resAmpFadeFactor[5] = 3.0f / 8.0f; - resAmpFadeFactor[4] = 5.0f / 8.0f; - resAmpFadeFactor[3] = 8.0f / 8.0f; - resAmpFadeFactor[2] = 12.0f / 8.0f; - resAmpFadeFactor[1] = 16.0f / 8.0f; - resAmpFadeFactor[0] = 31.0f / 8.0f; - - for (int i = 0; i < 5120; i++) { - sinf10[i] = sin(FLOAT_PI * i / 2048.0f); - } + static const Bit8u resAmpDecayFactorTable[] = {31, 16, 12, 8, 5, 3, 2, 1}; + resAmpDecayFactor = resAmpDecayFactorTable; } } diff --git a/audio/softsynth/mt32/Tables.h b/audio/softsynth/mt32/Tables.h index a63eaf6d269..8b4580df0ed 100644 --- a/audio/softsynth/mt32/Tables.h +++ b/audio/softsynth/mt32/Tables.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -20,7 +20,9 @@ namespace MT32Emu { -// Sample rate to use in mixing +// Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent. +// In order to achieve further advance in emulation accuracy, sample rate made fixed throughout the emulator. +// The output from the synth is supposed to be resampled to convert the sample rate. const unsigned int SAMPLE_RATE = 32000; const int MIDDLEC = 60; @@ -54,11 +56,10 @@ public: // CONFIRMED: Bit8u pulseWidth100To255[101]; - float exp2[4096]; - float pulseLenFactor[128]; - float resAmpMax[32]; - float resAmpFadeFactor[8]; - float sinf10[5120]; + Bit16u exp9[512]; + Bit16u logsin9[512]; + + const Bit8u *resAmpDecayFactor; }; } diff --git a/audio/softsynth/mt32/mmath.h b/audio/softsynth/mt32/mmath.h index 25c79d57a91..ee6a652c36e 100644 --- a/audio/softsynth/mt32/mmath.h +++ b/audio/softsynth/mt32/mmath.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -52,10 +52,6 @@ static inline float EXP2F(float x) { #endif } -static inline float EXP2I(unsigned int i) { - return float(1 << (i >> 12)) * Tables::getInstance().exp2[i & 0x0FFF]; -} - static inline float EXP10F(float x) { return exp(FLOAT_LN_10 * x); } diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk index 995e4500769..e7afdfd2b4f 100644 --- a/audio/softsynth/mt32/module.mk +++ b/audio/softsynth/mt32/module.mk @@ -2,13 +2,17 @@ MODULE := audio/softsynth/mt32 MODULE_OBJS := \ AReverbModel.o \ + BReverbModel.o \ DelayReverb.o \ FreeverbModel.o \ LA32Ramp.o \ + LA32WaveGenerator.o \ + LegacyWaveGenerator.o \ Part.o \ Partial.o \ PartialManager.o \ Poly.o \ + ROMInfo.o \ Synth.o \ TVA.o \ TVF.o \ diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h index f10bc1faab2..971a0886d5e 100644 --- a/audio/softsynth/mt32/mt32emu.h +++ b/audio/softsynth/mt32/mt32emu.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011, 2012, 2013 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -59,20 +59,6 @@ #define MT32EMU_MONITOR_TVA 0 #define MT32EMU_MONITOR_TVF 0 -// The WG algorithm involves dozens of transcendent maths, e.g. exponents and trigonometry. -// Unfortunately, the majority of systems perform such computations inefficiently, -// standard math libs and FPUs make no optimisations for single precision floats, -// and use no LUTs to speedup computing internal taylor series. Though, there're rare exceptions, -// and there's a hope it will become common soon. -// So, this is the crucial point of speed optimisations. We have now eliminated all the transcendent maths -// within the critical path and use LUTs instead. -// Besides, since the LA32 chip is assumed to use similar LUTs inside, the overall emulation accuracy should be better. -// 0: Use LUTs to speedup WG. Most common setting. You can expect about 50% performance boost. -// 1: Use precise float math. Use this setting to achieve more accurate wave generator. If your system performs better with this setting, it is really notable. :) -#define MT32EMU_ACCURATE_WG 0 - -#define MT32EMU_USE_EXTINT 0 - // Configuration // The maximum number of partials playing simultaneously #define MT32EMU_MAX_PARTIALS 32 @@ -84,9 +70,14 @@ // If zero, keeps reverb buffers for all modes around all the time to avoid allocating/freeing in the critical path. #define MT32EMU_REDUCE_REVERB_MEMORY 1 -// 0: Use standard Freeverb -// 1: Use AReverb (currently not properly tuned) -#define MT32EMU_USE_AREVERBMODEL 0 +// 0: Use legacy Freeverb +// 1: Use Accurate Reverb model aka AReverb +// 2: Use Bit-perfect Boss Reverb model aka BReverb (for developers, not much practical use) +#define MT32EMU_USE_REVERBMODEL 1 + +// 0: Use refined wave generator based on logarithmic fixed-point computations and LUTs +// 1: Use legacy accurate wave generator based on float computations +#define MT32EMU_ACCURATE_WG 0 namespace MT32Emu { @@ -111,11 +102,14 @@ const unsigned int MAX_PRERENDER_SAMPLES = 1024; #include "Tables.h" #include "Poly.h" #include "LA32Ramp.h" +#include "LA32WaveGenerator.h" +#include "LegacyWaveGenerator.h" #include "TVA.h" #include "TVP.h" #include "TVF.h" #include "Partial.h" #include "Part.h" +#include "ROMInfo.h" #include "Synth.h" #endif diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp index e039845b8f1..a1a736f9de6 100644 --- a/audio/softsynth/opl/dosbox.cpp +++ b/audio/softsynth/opl/dosbox.cpp @@ -247,7 +247,7 @@ byte OPL::read(int port) { } void OPL::writeReg(int r, int v) { - byte tempReg = 0; + int tempReg = 0; switch (_type) { case Config::kOpl2: case Config::kDualOpl2: @@ -257,12 +257,27 @@ void OPL::writeReg(int r, int v) { // Backup old setup register tempReg = _reg.normal; - // We need to set the register we want to write to via port 0x388 - write(0x388, r); - // Do the real writing to the register - write(0x389, v); + // We directly allow writing to secondary OPL3 registers by using + // register values >= 0x100. + if (_type == Config::kOpl3 && r >= 0x100) { + // We need to set the register we want to write to via port 0x222, + // since we want to write to the secondary register set. + write(0x222, r); + // Do the real writing to the register + write(0x223, v); + } else { + // We need to set the register we want to write to via port 0x388 + write(0x388, r); + // Do the real writing to the register + write(0x389, v); + } + // Restore the old register - write(0x388, tempReg); + if (_type == Config::kOpl3 && tempReg >= 0x100) { + write(0x222, tempReg & ~0x100); + } else { + write(0x388, tempReg); + } break; }; } diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp index c54f620a108..2db7d421b62 100644 --- a/audio/softsynth/opl/mame.cpp +++ b/audio/softsynth/opl/mame.cpp @@ -223,7 +223,7 @@ static int *ENV_CURVE; /* multiple table */ -#define ML(a) (int)(a * 2) +#define ML(a) (uint)(a * 2) static const uint MUL_TABLE[16]= { /* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */ ML(0.50), ML(1.00), ML(2.00), ML(3.00), ML(4.00), ML(5.00), ML(6.00), ML(7.00), diff --git a/backends/events/default/default-events.cpp b/backends/events/default/default-events.cpp index 99d12c73dc2..bf76bbc1cba 100644 --- a/backends/events/default/default-events.cpp +++ b/backends/events/default/default-events.cpp @@ -51,6 +51,8 @@ DefaultEventManager::DefaultEventManager(Common::EventSource *boss) : // Reset key repeat _currentKeyDown.keycode = 0; + _currentKeyDown.ascii = 0; + _currentKeyDown.flags = 0; #ifdef ENABLE_VKEYBD _vk = new Common::VirtualKeyboard(); @@ -82,7 +84,8 @@ void DefaultEventManager::init() { } bool DefaultEventManager::pollEvent(Common::Event &event) { - uint32 time = g_system->getMillis(); + // Skip recording of these events + uint32 time = g_system->getMillis(true); bool result = false; _dispatcher.dispatch(); diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp index 1a60c4dc12c..90c538788fb 100644 --- a/backends/events/sdl/sdl-events.cpp +++ b/backends/events/sdl/sdl-events.cpp @@ -101,6 +101,7 @@ int SdlEventSource::mapKey(SDLKey key, SDLMod mod, Uint16 unicode) { return key; } +// ResidualVM specific relMouse x,y void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y, int relx, int rely) { event.mouse.x = x; event.mouse.y = y; @@ -118,7 +119,9 @@ void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y, int r } void SdlEventSource::handleKbdMouse() { - uint32 curTime = g_system->getMillis(); + // Skip recording of these events + uint32 curTime = g_system->getMillis(true); + if (curTime >= _km.last_time + _km.delay_time) { _km.last_time = curTime; if (_km.x_down_count == 1) { @@ -530,7 +533,7 @@ bool SdlEventSource::handleKeyUp(SDL_Event &ev, Common::Event &event) { bool SdlEventSource::handleMouseMotion(SDL_Event &ev, Common::Event &event) { event.type = Common::EVENT_MOUSEMOVE; - processMouseEvent(event, ev.motion.x, ev.motion.y, ev.motion.xrel, ev.motion.yrel); + processMouseEvent(event, ev.motion.x, ev.motion.y, ev.motion.xrel, ev.motion.yrel); // ResidualVM xrel,yrel return true; } diff --git a/backends/fs/abstract-fs.h b/backends/fs/abstract-fs.h index 54e3958972a..2b66a6e6e12 100644 --- a/backends/fs/abstract-fs.h +++ b/backends/fs/abstract-fs.h @@ -100,7 +100,7 @@ public: * @param mode Mode to use while listing the directory. * @param hidden Whether to include hidden files or not in the results. * - * @return true if succesful, false otherwise (e.g. when the directory does not exist). + * @return true if successful, false otherwise (e.g. when the directory does not exist). */ virtual bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const = 0; diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp index c5fe6e6dd8f..a401baa6427 100644 --- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp @@ -40,6 +40,8 @@ #include "graphics/scaler.h" #include "graphics/surface.h" #include "graphics/pixelbuffer.h" +#include "gui/EventRecorder.h" + static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { {0, 0, 0} }; diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h index 9057500e8b1..cd2464730fd 100644 --- a/backends/graphics/surfacesdl/surfacesdl-graphics.h +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h @@ -69,8 +69,8 @@ public: virtual Common::List getSupportedFormats() const; #endif virtual void initSize(uint w, uint h, const Graphics::PixelFormat *format = NULL); - virtual void launcherInitSize(uint w, uint h); - Graphics::PixelBuffer setupScreen(int screenW, int screenH, bool fullscreen, bool accel3d); + virtual void launcherInitSize(uint w, uint h); // ResidualVM specific method + Graphics::PixelBuffer setupScreen(int screenW, int screenH, bool fullscreen, bool accel3d); // ResidualVM specific method virtual int getScreenChangeID() const { return _screenChangeCount; } virtual void beginGFXTransaction(); @@ -100,17 +100,17 @@ public: virtual void clearOverlay(); virtual void grabOverlay(void *buf, int pitch); virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h); - //ResidualVM specific implemention: + + //ResidualVM specific implementions: virtual int16 getOverlayHeight() { return _overlayHeight; } virtual int16 getOverlayWidth() { return _overlayWidth; } - void closeOverlay(); + void closeOverlay(); // ResidualVM specific method virtual bool showMouse(bool visible); virtual void warpMouse(int x, int y); virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL); virtual void setCursorPalette(const byte *colors, uint start, uint num); - // ResidualVM specific method - virtual bool lockMouse(bool lock); + virtual bool lockMouse(bool lock); // ResidualVM specific method #ifdef USE_OSD virtual void displayMessageOnOSD(const char *msg); diff --git a/backends/keymapper/keymapper.h b/backends/keymapper/keymapper.h index 1e8d1c08c32..a54a2acbdc6 100644 --- a/backends/keymapper/keymapper.h +++ b/backends/keymapper/keymapper.h @@ -128,7 +128,7 @@ public: * @param name name of the keymap to push * @param transparent if true keymapper will iterate down the * stack if it cannot find a key in the new map - * @return true if succesful + * @return true if successful */ bool pushKeymap(const String& name, bool transparent = false); diff --git a/backends/midi/coreaudio.cpp b/backends/midi/coreaudio.cpp index 94262d0d92c..e42b8ca3139 100644 --- a/backends/midi/coreaudio.cpp +++ b/backends/midi/coreaudio.cpp @@ -102,6 +102,7 @@ public: void sysEx(const byte *msg, uint16 length); private: + void loadSoundFont(const char *soundfont); AUGraph _auGraph; AudioUnit _synth; }; @@ -171,52 +172,8 @@ int MidiDriver_CORE::open() { #endif // Load custom soundfont, if specified - if (ConfMan.hasKey("soundfont")) { - const char *soundfont = ConfMan.get("soundfont").c_str(); - - // TODO: We should really check whether the file contains an - // actual soundfont... - -#if USE_DEPRECATED_COREAUDIO_API - // Before 10.5, we need to use kMusicDeviceProperty_SoundBankFSSpec - FSRef fsref; - FSSpec fsSpec; - err = FSPathMakeRef ((const byte *)soundfont, &fsref, NULL); - - if (err == noErr) { - err = FSGetCatalogInfo (&fsref, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL); - } - - if (err == noErr) { - err = AudioUnitSetProperty ( - _synth, - kMusicDeviceProperty_SoundBankFSSpec, kAudioUnitScope_Global, - 0, - &fsSpec, sizeof(fsSpec) - ); - } -#else - // kMusicDeviceProperty_SoundBankFSSpec is present on 10.6+, but broken - // kMusicDeviceProperty_SoundBankURL was added in 10.5 as a replacement - CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)soundfont, strlen(soundfont), false); - - if (url) { - err = AudioUnitSetProperty ( - _synth, - kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global, - 0, - &url, sizeof(url) - ); - - CFRelease(url); - } else { - warning("Failed to allocate CFURLRef from '%s'", soundfont); - } -#endif - - if (err != noErr) - error("Failed loading custom sound font '%s' (error %ld)", soundfont, (long)err); - } + if (ConfMan.hasKey("soundfont")) + loadSoundFont(ConfMan.get("soundfont").c_str()); #ifdef COREAUDIO_DISABLE_REVERB // Disable reverb mode, as that sucks up a lot of CPU power, which can @@ -242,6 +199,74 @@ bail: return MERR_CANNOT_CONNECT; } +void MidiDriver_CORE::loadSoundFont(const char *soundfont) { + // TODO: We should really check whether the file contains an + // actual soundfont... + + OSStatus err = 0; + +#if USE_DEPRECATED_COREAUDIO_API + FSRef fsref; + err = FSPathMakeRef((const byte *)soundfont, &fsref, NULL); + + SInt32 version; + err = Gestalt(gestaltSystemVersion, &version); + + if (err == noErr) { + if (version >= 0x1030) { + // Use kMusicDeviceProperty_SoundBankFSRef in >= 10.3 + + // HACK HACK HACK HACK SUPER HACK: Using the value of 1012 instead of + // kMusicDeviceProperty_SoundBankFSRef so this compiles with the 10.2 + // SDK (which does not have that symbol). + if (err == noErr) { + err = AudioUnitSetProperty( + _synth, + /*kMusicDeviceProperty_SoundBankFSRef*/ 1012, kAudioUnitScope_Global, + 0, + &fsref, sizeof(fsref) + ); + } + } else { + // In 10.2, only kMusicDeviceProperty_SoundBankFSSpec is available + FSSpec fsSpec; + + if (err == noErr) + err = FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL); + + if (err == noErr) { + err = AudioUnitSetProperty( + _synth, + kMusicDeviceProperty_SoundBankFSSpec, kAudioUnitScope_Global, + 0, + &fsSpec, sizeof(fsSpec) + ); + } + } + } +#else + // kMusicDeviceProperty_SoundBankURL was added in 10.5 as a replacement + // In addition, the File Manager API became deprecated starting in 10.8 + CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)soundfont, strlen(soundfont), false); + + if (url) { + err = AudioUnitSetProperty( + _synth, + kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global, + 0, + &url, sizeof(url) + ); + + CFRelease(url); + } else { + warning("Failed to allocate CFURLRef from '%s'", soundfont); + } +#endif // USE_DEPRECATED_COREAUDIO_API + + if (err != noErr) + error("Failed loading custom sound font '%s' (error %ld)", soundfont, (long)err); +} + void MidiDriver_CORE::close() { MidiDriver_MPU401::close(); if (_auGraph) { diff --git a/backends/midi/seq.cpp b/backends/midi/seq.cpp index c0383fc12bd..d179cc4ae4e 100644 --- a/backends/midi/seq.cpp +++ b/backends/midi/seq.cpp @@ -88,12 +88,9 @@ int MidiDriver_SEQ::open() { device = ::open((device_name), O_RDWR, 0); - if ((device_name == NULL) || (device < 0)) { - if (device_name == NULL) - warning("Opening /dev/null (no music will be heard) "); - else - warning("Cannot open rawmidi device %s - using /dev/null (no music will be heard) ", - device_name); + if (device < 0) { + warning("Cannot open rawmidi device %s - using /dev/null (no music will be heard)", + device_name); device = (::open(("/dev/null"), O_RDWR, 0)); if (device < 0) error("Cannot open /dev/null to dump midi output"); @@ -145,7 +142,7 @@ void MidiDriver_SEQ::send(uint32 b) { buf[position++] = 0; break; default: - warning("MidiDriver_SEQ::send: unknown : %08x", (int)b); + warning("MidiDriver_SEQ::send: unknown: %08x", (int)b); break; } if (write(device, buf, position) == -1) diff --git a/backends/mixer/sdl/sdl-mixer.cpp b/backends/mixer/sdl/sdl-mixer.cpp index 001309a7770..3c79290b50b 100644 --- a/backends/mixer/sdl/sdl-mixer.cpp +++ b/backends/mixer/sdl/sdl-mixer.cpp @@ -153,7 +153,7 @@ void SdlMixerManager::suspendAudio() { int SdlMixerManager::resumeAudio() { if (!_audioSuspended) return -2; - if (SDL_OpenAudio(&_obtained, NULL) < 0){ + if (SDL_OpenAudio(&_obtained, NULL) < 0) { return -1; } SDL_PauseAudio(0); diff --git a/backends/modular-backend.cpp b/backends/modular-backend.cpp index 45e6bb86c56..5b8e0e01dc8 100644 --- a/backends/modular-backend.cpp +++ b/backends/modular-backend.cpp @@ -26,11 +26,11 @@ #include "backends/graphics/graphics.h" #include "backends/mutex/mutex.h" +#include "gui/EventRecorder.h" #include "audio/mixer.h" #include "graphics/pixelformat.h" -// ResidualVM specific: -#include "graphics/pixelbuffer.h" +#include "graphics/pixelbuffer.h" // ResidualVM specific: ModularBackend::ModularBackend() : @@ -54,7 +54,7 @@ bool ModularBackend::hasFeature(Feature f) { } void ModularBackend::setFeatureState(Feature f, bool enable) { - return _graphicsManager->setFeatureState(f, enable); + _graphicsManager->setFeatureState(f, enable); } bool ModularBackend::getFeatureState(Feature f) { @@ -153,7 +153,9 @@ void ModularBackend::fillScreen(uint32 col) { } void ModularBackend::updateScreen() { + g_eventRec.preDrawOverlayGui(); _graphicsManager->updateScreen(); + g_eventRec.postDrawOverlayGui(); } void ModularBackend::setShakePos(int shakeOffset) { diff --git a/backends/modular-backend.h b/backends/modular-backend.h index 3209261ca47..9cc836d4e54 100644 --- a/backends/modular-backend.h +++ b/backends/modular-backend.h @@ -72,10 +72,8 @@ public: virtual Common::List getSupportedFormats() const; #endif virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL); -// ResidualVM specific method - virtual void launcherInitSize(uint w, uint h); -// ResidualVM specific method - virtual Graphics::PixelBuffer setupScreen(int screenW, int screenH, bool fullscreen, bool accel3d); + virtual void launcherInitSize(uint w, uint h); // ResidualVM specific method + virtual Graphics::PixelBuffer setupScreen(int screenW, int screenH, bool fullscreen, bool accel3d); // ResidualVM specific method virtual int getScreenChangeID() const; virtual void beginGFXTransaction(); @@ -106,8 +104,7 @@ public: virtual void warpMouse(int x, int y); virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL); virtual void setCursorPalette(const byte *colors, uint start, uint num); - // ResidualVM specific method - virtual bool lockMouse(bool lock); + virtual bool lockMouse(bool lock); // ResidualVM specific method //@} diff --git a/backends/module.mk b/backends/module.mk index 772f11fc6f3..682ca78e22d 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -62,7 +62,7 @@ MODULE_OBJS += \ mutex/sdl/sdl-mutex.o \ plugins/sdl/sdl-provider.o \ timer/sdl/sdl-timer.o - + # SDL 1.3 removed audio CD support ifndef USE_SDL13 MODULE_OBJS += \ @@ -112,9 +112,9 @@ MODULE_OBJS += \ mixer/sdl13/sdl13-mixer.o endif -ifeq ($(BACKEND),bada) +ifeq ($(BACKEND),tizen) MODULE_OBJS += \ - timer/bada/timer.o + timer/tizen/timer.o endif ifeq ($(BACKEND),ds) @@ -206,5 +206,11 @@ MODULE_OBJS += \ plugins/wii/wii-provider.o endif +ifdef ENABLE_EVENTRECORDER +MODULE_OBJS += \ + mixer/nullmixer/nullsdl-mixer.o \ + saves/recorder/recorder-saves.o +endif + # Include common rules include $(srcdir)/rules.mk diff --git a/backends/mutex/sdl/sdl-mutex.cpp b/backends/mutex/sdl/sdl-mutex.cpp index 8491ae468ce..a51e6f0e388 100644 --- a/backends/mutex/sdl/sdl-mutex.cpp +++ b/backends/mutex/sdl/sdl-mutex.cpp @@ -33,15 +33,15 @@ OSystem::MutexRef SdlMutexManager::createMutex() { } void SdlMutexManager::lockMutex(OSystem::MutexRef mutex) { - SDL_mutexP((SDL_mutex *) mutex); + SDL_mutexP((SDL_mutex *)mutex); } void SdlMutexManager::unlockMutex(OSystem::MutexRef mutex) { - SDL_mutexV((SDL_mutex *) mutex); + SDL_mutexV((SDL_mutex *)mutex); } void SdlMutexManager::deleteMutex(OSystem::MutexRef mutex) { - SDL_DestroyMutex((SDL_mutex *) mutex); + SDL_DestroyMutex((SDL_mutex *)mutex); } #endif diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp index 4f57b7a3cac..1dca773f60a 100644 --- a/backends/platform/android/android.cpp +++ b/backends/platform/android/android.cpp @@ -116,7 +116,7 @@ OSystem_Android::OSystem_Android(int audio_sample_rate, int audio_buffer_size) : _screen_changeid(0), _egl_surface_width(0), _egl_surface_height(0), - _htc_fail(false), + _htc_fail(true), _force_redraw(false), _game_texture(0), _game_pbuf(), @@ -149,8 +149,7 @@ OSystem_Android::OSystem_Android(int audio_sample_rate, int audio_buffer_size) : _touchpad_scale(66), _dpad_scale(4), _fingersDown(0), - _trackball_scale(2) - { + _trackball_scale(2) { _fsFactory = new POSIXFilesystemFactory(); @@ -166,10 +165,10 @@ OSystem_Android::OSystem_Android(int audio_sample_rate, int audio_buffer_size) : getSystemProperty("ro.product.cpu.abi").c_str()); mf.toLowercase(); - _htc_fail = mf.contains("htc"); + /*_htc_fail = mf.contains("htc"); if (_htc_fail) - LOGI("Enabling HTC workaround"); + LOGI("Enabling HTC workaround");*/ } OSystem_Android::~OSystem_Android() { @@ -463,7 +462,7 @@ bool OSystem_Android::getFeatureState(Feature f) { } } -uint32 OSystem_Android::getMillis() { +uint32 OSystem_Android::getMillis(bool skipRecord) { timeval curTime; gettimeofday(&curTime, 0); diff --git a/backends/platform/android/android.h b/backends/platform/android/android.h index 7db77eeff46..8fecfce18e0 100644 --- a/backends/platform/android/android.h +++ b/backends/platform/android/android.h @@ -288,7 +288,7 @@ public: virtual void setCursorPalette(const byte *colors, uint start, uint num); virtual bool pollEvent(Common::Event &event); - virtual uint32 getMillis(); + virtual uint32 getMillis(bool skipRecord = false); virtual void delayMillis(uint msecs); virtual MutexRef createMutex(void); diff --git a/backends/platform/android/android.mk b/backends/platform/android/android.mk index ddf057422c6..51d23c635e8 100644 --- a/backends/platform/android/android.mk +++ b/backends/platform/android/android.mk @@ -50,7 +50,7 @@ JAVACFLAGS = -source 1.5 -target 1.5 ANDROID_JAR = $(ANDROID_SDK)/platforms/android-8/android.jar -PATH_BUILD = build.tmp +PATH_BUILD = ./build.tmp PATH_BUILD_ASSETS = $(PATH_BUILD)/assets PATH_BUILD_CLASSES_MAIN_TOP = $(PATH_BUILD)/classes.main PATH_BUILD_CLASSES_PLUGIN_TOP = $(PATH_BUILD)/classes.plugin @@ -128,7 +128,7 @@ $(FILE_RESOURCES_MAIN): $(FILE_MANIFEST) $(RESOURCES) $(ANDROID_JAR) $(DIST_FILE work_dir=`pwd`; \ for i in $(PATH_BUILD_ASSETS)/*.zip; do \ echo "recompress $$i"; \ - cd $$work_dir; \ + cd "$$work_dir"; \ $(RM) -rf $(PATH_BUILD_ASSETS)/tmp; \ $(MKDIR) $(PATH_BUILD_ASSETS)/tmp; \ unzip -q $$i -d $(PATH_BUILD_ASSETS)/tmp; \ diff --git a/backends/platform/sdl/ps3/ps3.cpp b/backends/platform/sdl/ps3/ps3.cpp index 4242a275e39..ce109f636bf 100644 --- a/backends/platform/sdl/ps3/ps3.cpp +++ b/backends/platform/sdl/ps3/ps3.cpp @@ -62,8 +62,7 @@ void OSystem_PS3::initBackend() { ConfMan.set("joystick_num", 0); ConfMan.set("vkeybdpath", PREFIX "/data"); ConfMan.registerDefault("fullscreen", true); - //ResidualVM: not used - //ConfMan.registerDefault("aspect_ratio", true); + //ConfMan.registerDefault("aspect_ratio", true); //ResidualVM: not used // Create the savefile manager if (_savefileManager == 0) diff --git a/backends/platform/sdl/sdl-sys.h b/backends/platform/sdl/sdl-sys.h index ca3c586e037..eccf73815dc 100644 --- a/backends/platform/sdl/sdl-sys.h +++ b/backends/platform/sdl/sdl-sys.h @@ -35,8 +35,11 @@ // it with an alternate slightly less unfriendly override. #if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_FILE) #undef FILE +// Solaris has typedef __FILE FILE in several places already +#if !defined(__sun) typedef struct { int FAKE; } FAKE_FILE; #define FILE FAKE_FILE +#endif // (__sun) #endif #if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_strcasecmp) diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index 33e7692d6a1..6be65a5337d 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -30,7 +30,7 @@ #include "backends/platform/sdl/sdl.h" #include "common/config-manager.h" -#include "common/EventRecorder.h" +#include "gui/EventRecorder.h" #include "common/taskbar.h" #include "common/textconsole.h" @@ -87,7 +87,9 @@ OSystem_SDL::~OSystem_SDL() { _audiocdManager = 0; delete _mixerManager; _mixerManager = 0; - delete _timerManager; + + delete g_eventRec.getTimerManager(); + _timerManager = 0; delete _mutexManager; _mutexManager = 0; @@ -117,9 +119,6 @@ void OSystem_SDL::init() { if (_mutexManager == 0) _mutexManager = new SdlMutexManager(); - if (_timerManager == 0) - _timerManager = new SdlTimerManager(); - #if defined(USE_TASKBAR) if (_taskbarManager == 0) _taskbarManager = new Common::TaskbarManager(); @@ -149,10 +148,12 @@ void OSystem_SDL::initBackend() { if (_mixerManager == 0) { _mixerManager = new SdlMixerManager(); - // Setup and start mixer _mixerManager->init(); } + g_eventRec.registerMixerManager(_mixerManager); + + g_eventRec.registerTimerManager(new SdlTimerManager()); if (_audiocdManager == 0) { // Audio CD support was removed with SDL 1.3 @@ -419,14 +420,15 @@ void OSystem_SDL::setupIcon() { free(icon); } -uint32 OSystem_SDL::getMillis() { + +uint32 OSystem_SDL::getMillis(bool skipRecord) { uint32 millis = SDL_GetTicks(); - g_eventRec.processMillis(millis); + g_eventRec.processMillis(millis, skipRecord); return millis; } void OSystem_SDL::delayMillis(uint msecs) { - if (!g_eventRec.processDelayMillis(msecs)) + if (!g_eventRec.processDelayMillis()) SDL_Delay(msecs); } @@ -444,11 +446,15 @@ void OSystem_SDL::getTimeAndDate(TimeDate &td) const { Audio::Mixer *OSystem_SDL::getMixer() { assert(_mixerManager); - return _mixerManager->getMixer(); + return getMixerManager()->getMixer(); } SdlMixerManager *OSystem_SDL::getMixerManager() { assert(_mixerManager); - return _mixerManager; + return g_eventRec.getMixerManager(); +} + +Common::TimerManager *OSystem_SDL::getTimerManager() { + return g_eventRec.getTimerManager(); } diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h index ada7234d530..04c6b9f5f94 100644 --- a/backends/platform/sdl/sdl.h +++ b/backends/platform/sdl/sdl.h @@ -24,6 +24,7 @@ #define PLATFORM_SDL_H #include "backends/platform/sdl/sdl-sys.h" +// ResidualVM specific: #ifdef USE_OPENGL #include #endif @@ -72,10 +73,11 @@ public: virtual void setWindowCaption(const char *caption); virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0); - virtual uint32 getMillis(); + virtual uint32 getMillis(bool skipRecord = false); virtual void delayMillis(uint msecs); virtual void getTimeAndDate(TimeDate &td) const; virtual Audio::Mixer *getMixer(); + virtual Common::TimerManager *getTimerManager(); protected: bool _inited; diff --git a/backends/saves/recorder/recorder-saves.cpp b/backends/saves/recorder/recorder-saves.cpp new file mode 100644 index 00000000000..49b46729138 --- /dev/null +++ b/backends/saves/recorder/recorder-saves.cpp @@ -0,0 +1,35 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "backends/saves/recorder/recorder-saves.h" +#include "gui/EventRecorder.h" +#include "common/savefile.h" + +Common::InSaveFile *RecorderSaveFileManager::openForLoading(const Common::String &filename) { + Common::InSaveFile *result = g_eventRec.processSaveStream(filename); + return result; +} + +Common::StringArray RecorderSaveFileManager::listSaveFiles(const Common::String &pattern) { + return g_eventRec.listSaveFiles(pattern); +} + diff --git a/backends/saves/recorder/recorder-saves.h b/backends/saves/recorder/recorder-saves.h new file mode 100644 index 00000000000..692aeca329d --- /dev/null +++ b/backends/saves/recorder/recorder-saves.h @@ -0,0 +1,36 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef BACKEND_SAVES_RECORDER_H +#define BACKEND_SAVES_RECORDER_H + +#include "backends/saves/default/default-saves.h" + +/** + * Provides a savefile manager implementation for event recorder. + */ +class RecorderSaveFileManager : public DefaultSaveFileManager { + virtual Common::StringArray listSaveFiles(const Common::String &pattern); + virtual Common::InSaveFile *openForLoading(const Common::String &filename); +}; + +#endif diff --git a/backends/timer/default/default-timer.cpp b/backends/timer/default/default-timer.cpp index 9f56d58b12e..ce93320f3d5 100644 --- a/backends/timer/default/default-timer.cpp +++ b/backends/timer/default/default-timer.cpp @@ -80,7 +80,7 @@ DefaultTimerManager::~DefaultTimerManager() { void DefaultTimerManager::handler() { Common::StackLock lock(_mutex); - const uint32 curTime = g_system->getMillis(); + uint32 curTime = g_system->getMillis(true); // Repeat as long as there is a TimerSlot that is scheduled to fire. TimerSlot *slot = _head->next; diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 81fe7d38961..e6a3d605650 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -117,6 +117,13 @@ static const char HELP_STRING[] = " --no-show-fps Set the turn off display FPS info\n" " --soft-renderer Switch to 3D software renderer\n" " --no-soft-renderer Switch to 3D hardware renderer\n" +#ifdef ENABLE_EVENTRECORDER + " --record-mode=MODE Specify record mode for event recorder (record, playback,\n" + " passthrough [default])\n" + " --record-file-name=FILE Specify record file name\n" + " --disable-display Disable any gfx output. Used for headless events\n" + " playback by Event Recorder\n" +#endif "\n" #ifdef ENABLE_GRIM " --dimuse-tempo=NUM Set internal Digital iMuse tempo (10 - 100) per second\n" @@ -127,7 +134,7 @@ static const char HELP_STRING[] = static const char *s_appName = "residualvm"; -static void usage(const char *s, ...) GCC_PRINTF(1, 2); +static void NORETURN_PRE usage(const char *s, ...) GCC_PRINTF(1, 2) NORETURN_POST; static void usage(const char *s, ...) { char buf[STRINGBUFLEN]; @@ -178,7 +185,7 @@ void registerDefaults() { // Game specific ConfMan.registerDefault("path", ""); - ConfMan.registerDefault("platform", Common::kPlatformPC); + ConfMan.registerDefault("platform", Common::kPlatformDOS); ConfMan.registerDefault("language", "en"); ConfMan.registerDefault("subtitles", false); ConfMan.registerDefault("boot_param", 0); @@ -197,11 +204,34 @@ void registerDefaults() { ConfMan.registerDefault("confirm_exit", false); ConfMan.registerDefault("disable_sdl_parachute", false); + ConfMan.registerDefault("disable_display", false); ConfMan.registerDefault("record_mode", "none"); ConfMan.registerDefault("record_file_name", "record.bin"); - ConfMan.registerDefault("record_temp_file_name", "record.tmp"); - ConfMan.registerDefault("record_time_file_name", "record.time"); + ConfMan.registerDefault("gui_saveload_chooser", "grid"); + ConfMan.registerDefault("gui_saveload_last_pos", "0"); + + ConfMan.registerDefault("gui_browser_show_hidden", false); + +#ifdef USE_FLUIDSYNTH + // The settings are deliberately stored the same way as in Qsynth. The + // FluidSynth music driver is responsible for transforming them into + // their appropriate values. + ConfMan.registerDefault("fluidsynth_chorus_activate", true); + ConfMan.registerDefault("fluidsynth_chorus_nr", 3); + ConfMan.registerDefault("fluidsynth_chorus_level", 100); + ConfMan.registerDefault("fluidsynth_chorus_speed", 30); + ConfMan.registerDefault("fluidsynth_chorus_depth", 80); + ConfMan.registerDefault("fluidsynth_chorus_waveform", "sine"); + + ConfMan.registerDefault("fluidsynth_reverb_activate", true); + ConfMan.registerDefault("fluidsynth_reverb_roomsize", 20); + ConfMan.registerDefault("fluidsynth_reverb_damping", 0); + ConfMan.registerDefault("fluidsynth_reverb_width", 1); + ConfMan.registerDefault("fluidsynth_reverb_level", 90); + + ConfMan.registerDefault("fluidsynth_misc_interpolation", "4th"); +#endif } // @@ -273,12 +303,19 @@ void registerDefaults() { continue; \ } +// End an option handler +#define END_COMMAND \ + } + Common::String parseCommandLine(Common::StringMap &settings, int argc, const char * const *argv) { const char *s, *s2; + if (!argv) + return Common::String(); + // argv[0] contains the name of the executable. - if (argv && argv[0]) { + if (argv[0]) { s = strrchr(argv[0], '/'); s_appName = s ? (s+1) : argv[0]; } @@ -304,27 +341,27 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha bool isLongCmd = (s[0] == '-' && s[1] == '-'); DO_COMMAND('h', "help") - END_OPTION + END_COMMAND DO_COMMAND('v', "version") - END_OPTION + END_COMMAND DO_COMMAND('t', "list-targets") - END_OPTION + END_COMMAND DO_COMMAND('z', "list-games") - END_OPTION + END_COMMAND #ifdef DETECTOR_TESTING_HACK // HACK FIXME TODO: This command is intentionally *not* documented! DO_LONG_COMMAND("test-detector") - END_OPTION + END_COMMAND #endif #ifdef UPGRADE_ALL_TARGETS_HACK // HACK FIXME TODO: This command is intentionally *not* documented! DO_LONG_COMMAND("upgrade-targets") - END_OPTION + END_COMMAND #endif DO_LONG_OPTION("list-saves") @@ -350,7 +387,7 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha END_OPTION DO_LONG_COMMAND("list-audio-devices") - END_OPTION + END_COMMAND DO_LONG_OPTION_INT("output-rate") END_OPTION @@ -358,6 +395,17 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha DO_OPTION_BOOL('f', "fullscreen") END_OPTION +#ifdef ENABLE_EVENTRECORDER + DO_LONG_OPTION_INT("disable-display") + END_OPTION + + DO_LONG_OPTION("record-mode") + END_OPTION + + DO_LONG_OPTION("record-file-name") + END_OPTION +#endif + DO_LONG_OPTION("opl-driver") END_OPTION @@ -480,7 +528,7 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha END_OPTION DO_LONG_COMMAND("list-themes") - END_OPTION + END_COMMAND DO_LONG_OPTION("target-md5") END_OPTION @@ -494,18 +542,6 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha DO_LONG_OPTION("speech-mode") END_OPTION - DO_LONG_OPTION("record-mode") - END_OPTION - - DO_LONG_OPTION("record-file-name") - END_OPTION - - DO_LONG_OPTION("record-temp-file-name") - END_OPTION - - DO_LONG_OPTION("record-time-file-name") - END_OPTION - #ifdef IPHONE // This is automatically set when launched from the Springboard. DO_LONG_OPTION_OPT("launchedFromSB", 0) @@ -533,8 +569,7 @@ static void listGames() { "-------------------- ------------------------------------------------------\n"); const EnginePlugin::List &plugins = EngineMan.getPlugins(); - EnginePlugin::List::const_iterator iter = plugins.begin(); - for (iter = plugins.begin(); iter != plugins.end(); ++iter) { + for (EnginePlugin::List::const_iterator iter = plugins.begin(); iter != plugins.end(); ++iter) { GameList list = (**iter)->getSupportedGames(); for (GameList::iterator v = list.begin(); v != list.end(); ++v) { printf("%-20s %s\n", v->gameid().c_str(), v->description().c_str()); diff --git a/base/main.cpp b/base/main.cpp index 639410e701b..d67c57b11e3 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -42,8 +42,11 @@ #include "common/debug.h" #include "common/debug-channels.h" /* for debug manager */ #include "common/events.h" -#include "common/EventRecorder.h" +#include "gui/EventRecorder.h" #include "common/fs.h" +#ifdef ENABLE_EVENTRECORDER +#include "common/recorderfile.h" +#endif #include "common/system.h" #include "common/textconsole.h" #include "common/tokenizer.h" @@ -251,7 +254,7 @@ static void setupGraphics(OSystem &system) { system.setGraphicsMode(ConfMan.get("gfx_mode").c_str()); system.initSize(320, 200); - system.launcherInitSize(640, 400);//ResidualVM specific + system.launcherInitSize(640, 400); //ResidualVM specific if (ConfMan.hasKey("aspect_ratio")) system.setFeatureState(OSystem::kFeatureAspectRatioCorrection, ConfMan.getBool("aspect_ratio")); @@ -410,7 +413,9 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { settings["gfx-mode"] = "default"; } } - + if (settings.contains("disable-display")) { + ConfMan.setInt("disable-display", 1, Common::ConfigManager::kTransientDomain); + } setupGraphics(system); // Init the different managers that are used by the engines. @@ -429,7 +434,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { // TODO: This is just to match the current behavior, when we further extend // our event recorder, we might do this at another place. Or even change // the whole API for that ;-). - g_eventRec.init(); + g_eventRec.RegisterEventSource(); // Now as the event manager is created, setup the keymapper setupKeymapper(system); @@ -449,6 +454,21 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { // to save memory PluginManager::instance().unloadPluginsExcept(PLUGIN_TYPE_ENGINE, plugin); +#ifdef ENABLE_EVENTRECORDER + Common::String recordMode = ConfMan.get("record_mode"); + Common::String recordFileName = ConfMan.get("record_file_name"); + + if (recordMode == "record") { + g_eventRec.init(g_eventRec.generateRecordFileName(ConfMan.getActiveDomainName()), GUI::EventRecorder::kRecorderRecord); + } else if (recordMode == "playback") { + g_eventRec.init(recordFileName, GUI::EventRecorder::kRecorderPlayback); + } else if ((recordMode == "info") && (!recordFileName.empty())) { + Common::PlaybackFile record; + record.openRead(recordFileName); + debug("info:author=%s name=%s description=%s", record.getHeader().author.c_str(), record.getHeader().name.c_str(), record.getHeader().description.c_str()); + break; + } +#endif // Try to run the game Common::Error result = runGame(plugin, system, specialDebug); @@ -479,6 +499,11 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { #ifdef FORCE_RTL g_system->getEventManager()->resetQuit(); #endif + #ifdef ENABLE_EVENTRECORDER + if (g_eventRec.checkForContinueGame()) { + continue; + } + #endif // Discard any command line options. It's unlikely that the user // wanted to apply them to *all* games ever launched. @@ -502,7 +527,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { GUI::GuiManager::destroy(); Common::ConfigManager::destroy(); Common::DebugManager::destroy(); - Common::EventRecorder::destroy(); + GUI::EventRecorder::destroy(); Common::SearchManager::destroy(); #ifdef USE_TRANSLATION Common::TranslationManager::destroy(); diff --git a/base/plugins.cpp b/base/plugins.cpp index 8099d12ea9d..3035b5a95bc 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -124,7 +124,7 @@ public: LINK_PLUGIN(MT32) #endif LINK_PLUGIN(ADLIB) -//ResidualVM: disabled belows +//ResidualVM: disabled belows: // LINK_PLUGIN(PCSPK) // LINK_PLUGIN(PCJR) // LINK_PLUGIN(CMS) @@ -133,8 +133,8 @@ public: #endif #ifndef DISABLE_SID // LINK_PLUGIN(C64) -// LINK_PLUGIN(AMIGA) #endif +// LINK_PLUGIN(AMIGA) // LINK_PLUGIN(APPLEIIGS) // LINK_PLUGIN(TOWNS) // LINK_PLUGIN(PC98) diff --git a/base/plugins.h b/base/plugins.h index 19a3566b57b..cb93653d2c2 100644 --- a/base/plugins.h +++ b/base/plugins.h @@ -26,7 +26,7 @@ #include "common/array.h" #include "common/fs.h" #include "common/str.h" -//#include "backends/plugins/elf/version.h" +//#include "backends/plugins/elf/version.h" // ResidualVM specific /** diff --git a/common/EventDispatcher.cpp b/common/EventDispatcher.cpp index 012a2dfce5b..e60c1aa7ffe 100644 --- a/common/EventDispatcher.cpp +++ b/common/EventDispatcher.cpp @@ -24,7 +24,7 @@ namespace Common { -EventDispatcher::EventDispatcher() : _mapper(0) { +EventDispatcher::EventDispatcher() : _autoFreeMapper(false), _mapper(0) { } EventDispatcher::~EventDispatcher() { @@ -38,7 +38,9 @@ EventDispatcher::~EventDispatcher() { delete i->observer; } - delete _mapper; + if (_autoFreeMapper) { + delete _mapper; + } _mapper = 0; } @@ -68,11 +70,15 @@ void EventDispatcher::dispatch() { } } -void EventDispatcher::registerMapper(EventMapper *mapper) { - delete _mapper; +void EventDispatcher::registerMapper(EventMapper *mapper, bool autoFree) { + if (_autoFreeMapper) { + delete _mapper; + } _mapper = mapper; + _autoFreeMapper = autoFree; } + void EventDispatcher::registerSource(EventSource *source, bool autoFree) { SourceEntry newEntry; diff --git a/common/EventRecorder.cpp b/common/EventRecorder.cpp deleted file mode 100644 index 5e24f128c3c..00000000000 --- a/common/EventRecorder.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "common/EventRecorder.h" - -#include "common/bufferedstream.h" -#include "common/config-manager.h" -#include "common/random.h" -#include "common/savefile.h" -#include "common/textconsole.h" - -namespace Common { - -DECLARE_SINGLETON(EventRecorder); - -#define RECORD_SIGNATURE 0x54455354 -#define RECORD_VERSION 1 - -uint32 readTime(ReadStream *inFile) { - uint32 d = inFile->readByte(); - if (d == 0xff) { - d = inFile->readUint32LE(); - } - - return d; -} - -void writeTime(WriteStream *outFile, uint32 d) { - //Simple RLE compression - if (d >= 0xff) { - outFile->writeByte(0xff); - outFile->writeUint32LE(d); - } else { - outFile->writeByte(d); - } -} - -void readRecord(SeekableReadStream *inFile, uint32 &diff, Event &event, uint32 &millis) { - millis = readTime(inFile); - - diff = inFile->readUint32LE(); - - event.type = (EventType)inFile->readUint32LE(); - - switch (event.type) { - case EVENT_KEYDOWN: - case EVENT_KEYUP: - event.kbd.keycode = (KeyCode)inFile->readSint32LE(); - event.kbd.ascii = inFile->readUint16LE(); - event.kbd.flags = inFile->readByte(); - break; - case EVENT_MOUSEMOVE: - case EVENT_LBUTTONDOWN: - case EVENT_LBUTTONUP: - case EVENT_RBUTTONDOWN: - case EVENT_RBUTTONUP: - case EVENT_WHEELUP: - case EVENT_WHEELDOWN: - case EVENT_MBUTTONDOWN: - case EVENT_MBUTTONUP: - event.mouse.x = inFile->readSint16LE(); - event.mouse.y = inFile->readSint16LE(); - break; - default: - break; - } -} - -void writeRecord(WriteStream *outFile, uint32 diff, const Event &event, uint32 millis) { - writeTime(outFile, millis); - - outFile->writeUint32LE(diff); - - outFile->writeUint32LE((uint32)event.type); - - switch (event.type) { - case EVENT_KEYDOWN: - case EVENT_KEYUP: - outFile->writeSint32LE(event.kbd.keycode); - outFile->writeUint16LE(event.kbd.ascii); - outFile->writeByte(event.kbd.flags); - break; - case EVENT_MOUSEMOVE: - case EVENT_LBUTTONDOWN: - case EVENT_LBUTTONUP: - case EVENT_RBUTTONDOWN: - case EVENT_RBUTTONUP: - case EVENT_WHEELUP: - case EVENT_WHEELDOWN: - case EVENT_MBUTTONDOWN: - case EVENT_MBUTTONUP: - outFile->writeSint16LE(event.mouse.x); - outFile->writeSint16LE(event.mouse.y); - break; - default: - break; - } -} - -EventRecorder::EventRecorder() { - _recordFile = NULL; - _recordTimeFile = NULL; - _playbackFile = NULL; - _playbackTimeFile = NULL; - _timeMutex = g_system->createMutex(); - _recorderMutex = g_system->createMutex(); - - _eventCount = 0; - _lastEventCount = 0; - _lastMillis = 0; - _lastEventMillis = 0; - - _recordMode = kPassthrough; -} - -EventRecorder::~EventRecorder() { - deinit(); - - g_system->deleteMutex(_timeMutex); - g_system->deleteMutex(_recorderMutex); -} - -void EventRecorder::init() { - String recordModeString = ConfMan.get("record_mode"); - if (recordModeString.compareToIgnoreCase("record") == 0) { - _recordMode = kRecorderRecord; - - debug(3, "EventRecorder: record"); - } else { - if (recordModeString.compareToIgnoreCase("playback") == 0) { - _recordMode = kRecorderPlayback; - debug(3, "EventRecorder: playback"); - } else { - _recordMode = kPassthrough; - debug(3, "EventRecorder: passthrough"); - } - } - - _recordFileName = ConfMan.get("record_file_name"); - if (_recordFileName.empty()) { - _recordFileName = "record.bin"; - } - _recordTempFileName = ConfMan.get("record_temp_file_name"); - if (_recordTempFileName.empty()) { - _recordTempFileName = "record.tmp"; - } - _recordTimeFileName = ConfMan.get("record_time_file_name"); - if (_recordTimeFileName.empty()) { - _recordTimeFileName = "record.time"; - } - - // recorder stuff - if (_recordMode == kRecorderRecord) { - _recordCount = 0; - _recordTimeCount = 0; - _recordFile = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving(_recordTempFileName), 128 * 1024); - _recordTimeFile = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving(_recordTimeFileName), 128 * 1024); - _recordSubtitles = ConfMan.getBool("subtitles"); - } - - uint32 sign; - uint32 randomSourceCount; - if (_recordMode == kRecorderPlayback) { - _playbackCount = 0; - _playbackTimeCount = 0; - _playbackFile = wrapBufferedSeekableReadStream(g_system->getSavefileManager()->openForLoading(_recordFileName), 128 * 1024, DisposeAfterUse::YES); - _playbackTimeFile = wrapBufferedSeekableReadStream(g_system->getSavefileManager()->openForLoading(_recordTimeFileName), 128 * 1024, DisposeAfterUse::YES); - - if (!_playbackFile) { - warning("Cannot open playback file %s. Playback was switched off", _recordFileName.c_str()); - _recordMode = kPassthrough; - } - - if (!_playbackTimeFile) { - warning("Cannot open playback time file %s. Playback was switched off", _recordTimeFileName.c_str()); - _recordMode = kPassthrough; - } - } - - if (_recordMode == kRecorderPlayback) { - sign = _playbackFile->readUint32LE(); - if (sign != RECORD_SIGNATURE) { - error("Unknown record file signature"); - } - - _playbackFile->readUint32LE(); // version - - // conf vars - ConfMan.setBool("subtitles", _playbackFile->readByte() != 0); - - _recordCount = _playbackFile->readUint32LE(); - _recordTimeCount = _playbackFile->readUint32LE(); - - randomSourceCount = _playbackFile->readUint32LE(); - for (uint i = 0; i < randomSourceCount; ++i) { - RandomSourceRecord rec; - rec.name = ""; - uint32 sLen = _playbackFile->readUint32LE(); - for (uint j = 0; j < sLen; ++j) { - char c = _playbackFile->readSByte(); - rec.name += c; - } - rec.seed = _playbackFile->readUint32LE(); - _randomSourceRecords.push_back(rec); - } - - _hasPlaybackEvent = false; - } - - g_system->getEventManager()->getEventDispatcher()->registerSource(this, false); - g_system->getEventManager()->getEventDispatcher()->registerObserver(this, EventManager::kEventRecorderPriority, false, true); -} - -void EventRecorder::deinit() { - debug(3, "EventRecorder: deinit"); - - g_system->getEventManager()->getEventDispatcher()->unregisterSource(this); - g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this); - - g_system->lockMutex(_timeMutex); - g_system->lockMutex(_recorderMutex); - _recordMode = kPassthrough; - g_system->unlockMutex(_timeMutex); - g_system->unlockMutex(_recorderMutex); - - delete _playbackFile; - delete _playbackTimeFile; - - if (_recordFile != NULL) { - _recordFile->finalize(); - delete _recordFile; - _recordTimeFile->finalize(); - delete _recordTimeFile; - - _playbackFile = g_system->getSavefileManager()->openForLoading(_recordTempFileName); - - assert(_playbackFile); - - _recordFile = g_system->getSavefileManager()->openForSaving(_recordFileName); - _recordFile->writeUint32LE(RECORD_SIGNATURE); - _recordFile->writeUint32LE(RECORD_VERSION); - - // conf vars - _recordFile->writeByte(_recordSubtitles ? 1 : 0); - - _recordFile->writeUint32LE(_recordCount); - _recordFile->writeUint32LE(_recordTimeCount); - - _recordFile->writeUint32LE(_randomSourceRecords.size()); - for (uint i = 0; i < _randomSourceRecords.size(); ++i) { - _recordFile->writeUint32LE(_randomSourceRecords[i].name.size()); - _recordFile->writeString(_randomSourceRecords[i].name); - _recordFile->writeUint32LE(_randomSourceRecords[i].seed); - } - - for (uint i = 0; i < _recordCount; ++i) { - uint32 tempDiff; - Event tempEvent; - uint32 millis; - readRecord(_playbackFile, tempDiff, tempEvent, millis); - writeRecord(_recordFile, tempDiff, tempEvent, millis); - } - - _recordFile->finalize(); - delete _recordFile; - delete _playbackFile; - - //TODO: remove recordTempFileName'ed file - } -} - -void EventRecorder::registerRandomSource(RandomSource &rnd, const String &name) { - if (_recordMode == kRecorderRecord) { - RandomSourceRecord rec; - rec.name = name; - rec.seed = rnd.getSeed(); - _randomSourceRecords.push_back(rec); - } - - if (_recordMode == kRecorderPlayback) { - for (uint i = 0; i < _randomSourceRecords.size(); ++i) { - if (_randomSourceRecords[i].name == name) { - rnd.setSeed(_randomSourceRecords[i].seed); - _randomSourceRecords.remove_at(i); - break; - } - } - } -} - -void EventRecorder::processMillis(uint32 &millis) { - uint32 d; - if (_recordMode == kPassthrough) { - return; - } - - g_system->lockMutex(_timeMutex); - if (_recordMode == kRecorderRecord) { - d = millis - _lastMillis; - writeTime(_recordTimeFile, d); - - _recordTimeCount++; - } - - if (_recordMode == kRecorderPlayback) { - if (_recordTimeCount > _playbackTimeCount) { - d = readTime(_playbackTimeFile); - - while ((_lastMillis + d > millis) && (_lastMillis + d - millis > 50)) { - _recordMode = kPassthrough; - g_system->delayMillis(50); - millis = g_system->getMillis(); - _recordMode = kRecorderPlayback; - } - - millis = _lastMillis + d; - _playbackTimeCount++; - } - } - - _lastMillis = millis; - g_system->unlockMutex(_timeMutex); -} - -bool EventRecorder::processDelayMillis(uint &msecs) { - if (_recordMode == kRecorderPlayback) { - _recordMode = kPassthrough; - - uint32 millis = g_system->getMillis(); - - _recordMode = kRecorderPlayback; - - if (_lastMillis > millis) { - // Skip delay if we're getting late - return true; - } - } - - return false; -} - -bool EventRecorder::notifyEvent(const Event &ev) { - if (_recordMode != kRecorderRecord) - return false; - - StackLock lock(_recorderMutex); - ++_eventCount; - - writeRecord(_recordFile, _eventCount - _lastEventCount, ev, _lastMillis - _lastEventMillis); - - _recordCount++; - _lastEventCount = _eventCount; - _lastEventMillis = _lastMillis; - - return false; -} - -bool EventRecorder::notifyPoll() { - if (_recordMode != kRecorderRecord) - return false; - - ++_eventCount; - - return false; -} - -bool EventRecorder::pollEvent(Event &ev) { - uint32 millis; - - if (_recordMode != kRecorderPlayback) - return false; - - StackLock lock(_recorderMutex); - ++_eventCount; - - if (!_hasPlaybackEvent) { - if (_recordCount > _playbackCount) { - readRecord(_playbackFile, const_cast(_playbackDiff), _playbackEvent, millis); - _playbackCount++; - _hasPlaybackEvent = true; - } - } - - if (_hasPlaybackEvent) { - if (_playbackDiff <= (_eventCount - _lastEventCount)) { - switch (_playbackEvent.type) { - case EVENT_MOUSEMOVE: - case EVENT_LBUTTONDOWN: - case EVENT_LBUTTONUP: - case EVENT_RBUTTONDOWN: - case EVENT_RBUTTONUP: - case EVENT_WHEELUP: - case EVENT_WHEELDOWN: - g_system->warpMouse(_playbackEvent.mouse.x, _playbackEvent.mouse.y); - break; - default: - break; - } - ev = _playbackEvent; - _hasPlaybackEvent = false; - _lastEventCount = _eventCount; - return true; - } - } - - return false; -} - -} // End of namespace Common diff --git a/common/EventRecorder.h b/common/EventRecorder.h deleted file mode 100644 index 43a08b08cd7..00000000000 --- a/common/EventRecorder.h +++ /dev/null @@ -1,110 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef COMMON_EVENTRECORDER_H -#define COMMON_EVENTRECORDER_H - -#include "common/scummsys.h" -#include "common/events.h" -#include "common/singleton.h" -#include "common/mutex.h" -#include "common/array.h" - -#define g_eventRec (Common::EventRecorder::instance()) - -namespace Common { - -class RandomSource; -class SeekableReadStream; -class WriteStream; - -/** - * Our generic event recorder. - * - * TODO: Add more documentation. - */ -class EventRecorder : private EventSource, private EventObserver, public Singleton { - friend class Singleton; - EventRecorder(); - ~EventRecorder(); -public: - void init(); - void deinit(); - - /** Register random source so it can be serialized in game test purposes */ - void registerRandomSource(RandomSource &rnd, const String &name); - - /** TODO: Add documentation, this is only used by the backend */ - void processMillis(uint32 &millis); - - /** TODO: Add documentation, this is only used by the backend */ - bool processDelayMillis(uint &msecs); - -private: - bool notifyEvent(const Event &ev); - bool notifyPoll(); - bool pollEvent(Event &ev); - bool allowMapping() const { return false; } - - class RandomSourceRecord { - public: - String name; - uint32 seed; - }; - Array _randomSourceRecords; - - bool _recordSubtitles; - volatile uint32 _recordCount; - volatile uint32 _lastRecordEvent; - volatile uint32 _recordTimeCount; - volatile uint32 _lastEventMillis; - WriteStream *_recordFile; - WriteStream *_recordTimeFile; - MutexRef _timeMutex; - MutexRef _recorderMutex; - volatile uint32 _lastMillis; - - volatile uint32 _playbackCount; - volatile uint32 _playbackDiff; - volatile bool _hasPlaybackEvent; - volatile uint32 _playbackTimeCount; - Event _playbackEvent; - SeekableReadStream *_playbackFile; - SeekableReadStream *_playbackTimeFile; - - volatile uint32 _eventCount; - volatile uint32 _lastEventCount; - - enum RecordMode { - kPassthrough = 0, - kRecorderRecord = 1, - kRecorderPlayback = 2 - }; - volatile RecordMode _recordMode; - String _recordFileName; - String _recordTempFileName; - String _recordTimeFileName; -}; - -} // End of namespace Common - -#endif diff --git a/common/archive.cpp b/common/archive.cpp index 1323f148059..57ebeb2ca6a 100644 --- a/common/archive.cpp +++ b/common/archive.cpp @@ -118,7 +118,7 @@ void SearchSet::addDirectory(const String &name, const FSNode &dir, int priority add(name, new FSDirectory(dir, depth, flat), priority); } -void SearchSet::addSubDirectoriesMatching(const FSNode &directory, String origPattern, bool ignoreCase, int priority) { +void SearchSet::addSubDirectoriesMatching(const FSNode &directory, String origPattern, bool ignoreCase, int priority, int depth, bool flat) { FSList subDirs; if (!directory.getChildren(subDirs)) return; @@ -161,9 +161,9 @@ void SearchSet::addSubDirectoriesMatching(const FSNode &directory, String origPa } if (nextPattern.empty()) - addDirectory(name, *i, priority); + addDirectory(name, *i, priority, depth, flat); else - addSubDirectoriesMatching(*i, nextPattern, ignoreCase, priority); + addSubDirectoriesMatching(*i, nextPattern, ignoreCase, priority, depth, flat); } } } diff --git a/common/archive.h b/common/archive.h index ffd86d786da..2f9736f0329 100644 --- a/common/archive.h +++ b/common/archive.h @@ -184,8 +184,8 @@ public: * to assume that this method is using anything other than a simple case insensitive compare. * Thus do not use any tokens like '*' or '?' in the "caselessName" parameter of this function! */ - void addSubDirectoryMatching(const FSNode &directory, const String &caselessName, int priority = 0) { - addSubDirectoriesMatching(directory, caselessName, true, priority); + void addSubDirectoryMatching(const FSNode &directory, const String &caselessName, int priority = 0, int depth = 1, bool flat = false) { + addSubDirectoriesMatching(directory, caselessName, true, priority, depth, flat); } /** @@ -208,7 +208,7 @@ public: * * @see Common::matchString */ - void addSubDirectoriesMatching(const FSNode &directory, String origPattern, bool ignoreCase, int priority = 0); + void addSubDirectoriesMatching(const FSNode &directory, String origPattern, bool ignoreCase, int priority = 0, int depth = 1, bool flat = false); /** * Remove an archive from the searchable set. diff --git a/common/bufferedstream.h b/common/bufferedstream.h index 55ea8dc13b6..6c859c98fe2 100644 --- a/common/bufferedstream.h +++ b/common/bufferedstream.h @@ -61,6 +61,6 @@ SeekableReadStream *wrapBufferedSeekableReadStream(SeekableReadStream *parentStr */ WriteStream *wrapBufferedWriteStream(WriteStream *parentStream, uint32 bufSize); -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/c++11-compat.h b/common/c++11-compat.h new file mode 100644 index 00000000000..50d79bd79e8 --- /dev/null +++ b/common/c++11-compat.h @@ -0,0 +1,42 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef COMMON_CPP11_COMPAT_H +#define COMMON_CPP11_COMPAT_H + +#if __cplusplus >= 201103L +#error "c++11-compat.h included when C++11 is available" +#endif + +// +// Custom nullptr replacement. This is not type safe as the real C++11 nullptr +// though. +// +#define nullptr 0 + +// +// Replacement for the override keyword. This allows compilation of code +// which uses it, but does not feature any semantic. +// +#define override + +#endif diff --git a/common/config-file.cpp b/common/config-file.cpp index 4224d7491de..0ce6dcf0c88 100644 --- a/common/config-file.cpp +++ b/common/config-file.cpp @@ -226,6 +226,15 @@ bool ConfigFile::saveToStream(WriteStream &stream) { return !stream.err(); } +void ConfigFile::addSection(const String §ion) { + Section *s = getSection(section); + if (s) + return; + + Section newSection; + newSection.name = section; + _sections.push_back(newSection); +} void ConfigFile::removeSection(const String §ion) { assert(isValidName(section)); @@ -380,4 +389,4 @@ void ConfigFile::Section::removeKey(const String &key) { } } -} // End of namespace Common +} // End of namespace Common diff --git a/common/config-file.h b/common/config-file.h index 7bc5604b4ca..8bba851110b 100644 --- a/common/config-file.h +++ b/common/config-file.h @@ -106,6 +106,7 @@ public: bool saveToStream(WriteStream &stream); bool hasSection(const String §ion) const; + void addSection(const String §ion); void removeSection(const String §ion); void renameSection(const String &oldName, const String &newName); @@ -134,6 +135,6 @@ private: */ -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/config-manager.h b/common/config-manager.h index 02d4ec34384..d43a7bec517 100644 --- a/common/config-manager.h +++ b/common/config-manager.h @@ -175,7 +175,7 @@ private: String _filename; }; -} // End of namespace Common +} // End of namespace Common /** Shortcut for accessing the configuration manager. */ #define ConfMan Common::ConfigManager::instance() diff --git a/common/cosinetables.cpp b/common/cosinetables.cpp index fe8f454e144..3b245750fa1 100644 --- a/common/cosinetables.cpp +++ b/common/cosinetables.cpp @@ -34,10 +34,10 @@ CosineTable::CosineTable(int bitPrecision) { int m = 1 << _bitPrecision; double freq = 2 * M_PI / m; - _table = new float[m]; + _table = new float[m / 2]; - // Table contains cos(2*pi*x/n) for 0<=x<=n/4, - // followed by its reverse + // Table contains cos(2*pi*i/m) for 0<=iflush(); } -} // End of namespace Common +} // End of namespace Common diff --git a/common/forbidden.h b/common/forbidden.h index eec80bba590..9050114442b 100644 --- a/common/forbidden.h +++ b/common/forbidden.h @@ -333,11 +333,21 @@ #define isalpha(a) FORBIDDEN_SYMBOL_REPLACEMENT #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_iscntrl + #undef iscntrl + #define iscntrl(a) FORBIDDEN_SYMBOL_REPLACEMENT + #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isdigit #undef isdigit #define isdigit(a) FORBIDDEN_SYMBOL_REPLACEMENT #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isgraph + #undef isgraph + #define isgraph(a) FORBIDDEN_SYMBOL_REPLACEMENT + #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isnumber #undef isnumber #define isnumber(a) FORBIDDEN_SYMBOL_REPLACEMENT @@ -348,6 +358,16 @@ #define islower(a) FORBIDDEN_SYMBOL_REPLACEMENT #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isprint + #undef isprint + #define isprint(a) FORBIDDEN_SYMBOL_REPLACEMENT + #endif + + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_ispunct + #undef ispunct + #define ispunct(a) FORBIDDEN_SYMBOL_REPLACEMENT + #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isspace #undef isspace #define isspace(a) FORBIDDEN_SYMBOL_REPLACEMENT @@ -358,6 +378,11 @@ #define isupper(a) FORBIDDEN_SYMBOL_REPLACEMENT #endif + #ifndef FORBIDDEN_SYMBOL_EXCEPTION_isxdigit + #undef isxdigit + #define isxdigit(a) FORBIDDEN_SYMBOL_REPLACEMENT + #endif + #endif // FORBIDDEN_SYMBOL_EXCEPTION_ctype_h #ifndef FORBIDDEN_SYMBOL_EXCEPTION_mkdir diff --git a/common/fs.cpp b/common/fs.cpp index 0143c936d46..19a01074ead 100644 --- a/common/fs.cpp +++ b/common/fs.cpp @@ -335,4 +335,4 @@ int FSDirectory::listMembers(ArchiveMemberList &list) const { } -} // End of namespace Common +} // End of namespace Common diff --git a/common/func.h b/common/func.h index db57d736684..a4a80e5406e 100644 --- a/common/func.h +++ b/common/func.h @@ -535,6 +535,6 @@ GENERATE_TRIVIAL_HASH_FUNCTOR(unsigned long); #undef GENERATE_TRIVIAL_HASH_FUNCTOR -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/hash-str.h b/common/hash-str.h index 08f0558bfd7..190e6922eb2 100644 --- a/common/hash-str.h +++ b/common/hash-str.h @@ -79,7 +79,7 @@ typedef HashMap StringMap; -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/hashmap.cpp b/common/hashmap.cpp index ff8f9db9ceb..e505d1dd257 100644 --- a/common/hashmap.cpp +++ b/common/hashmap.cpp @@ -106,4 +106,4 @@ void updateHashCollisionStats(int collisions, int dummyHits, int lookups, int ar } #endif -} // End of namespace Common +} // End of namespace Common diff --git a/common/hashmap.h b/common/hashmap.h index 7cf54997e86..42509d67e55 100644 --- a/common/hashmap.h +++ b/common/hashmap.h @@ -632,6 +632,6 @@ void HashMap::erase(const Key &key) { #undef HASHMAP_DUMMY_NODE -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/language.h b/common/language.h index db552fc9c45..03b9ebaf8e7 100644 --- a/common/language.h +++ b/common/language.h @@ -81,6 +81,6 @@ const String getGameGUIOptionsDescriptionLanguage(Common::Language lang); // TODO: Document this GUIO related function bool checkGameGUIOptionLanguage(Common::Language lang, const String &str); -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/macresman.cpp b/common/macresman.cpp index f2f020c6de5..00562f746a7 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -360,8 +360,8 @@ bool MacResManager::load(SeekableReadStream &stream) { _mapLength = stream.readUint32BE(); // do sanity check - if (_dataOffset >= (uint32)stream.size() || _mapOffset >= (uint32)stream.size() || - _dataLength + _mapLength > (uint32)stream.size()) { + if (stream.eos() || _dataOffset >= (uint32)stream.size() || _mapOffset >= (uint32)stream.size() || + _dataLength + _mapLength > (uint32)stream.size()) { _resForkOffset = -1; _mode = kResForkNone; return false; diff --git a/common/memorypool.h b/common/memorypool.h index 9a4e523d53e..1cd725b99d4 100644 --- a/common/memorypool.h +++ b/common/memorypool.h @@ -141,7 +141,7 @@ public: } }; -} // End of namespace Common +} // End of namespace Common /** * A custom placement new operator, using an arbitrary MemoryPool. diff --git a/common/memstream.h b/common/memstream.h index 497a178ab90..7fa6500753c 100644 --- a/common/memstream.h +++ b/common/memstream.h @@ -89,8 +89,9 @@ public: */ class MemoryWriteStream : public WriteStream { private: - byte *_ptr; const uint32 _bufSize; +protected: + byte *_ptr; uint32 _pos; bool _err; public: @@ -116,6 +117,40 @@ public: virtual void clearErr() { _err = false; } }; +/** + * MemoryWriteStream subclass with ability to set stream position indicator. + */ +class SeekableMemoryWriteStream : public MemoryWriteStream { +private: + byte *_ptrOrig; +public: + SeekableMemoryWriteStream(byte *buf, uint32 len) : MemoryWriteStream(buf, len), _ptrOrig(buf) {} + uint32 seek(uint32 offset, int whence = SEEK_SET) { + switch (whence) { + case SEEK_END: + // SEEK_END works just like SEEK_SET, only 'reversed', + // i.e. from the end. + offset = size() + offset; + // Fall through + case SEEK_SET: + _ptr = _ptrOrig + offset; + _pos = offset; + break; + case SEEK_CUR: + _ptr += offset; + _pos += offset; + break; + } + // Post-Condition + if (_pos > size()) { + _pos = size(); + _ptr = _ptrOrig + _pos; + } + return _pos; + } +}; + + /** * A sort of hybrid between MemoryWriteStream and Array classes. A stream * that grows as it's written to. @@ -173,6 +208,6 @@ public: bool seek(int32 offset, int whence = SEEK_SET); }; -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/module.mk b/common/module.mk index bfffaeda526..41db8b98140 100644 --- a/common/module.mk +++ b/common/module.mk @@ -9,7 +9,6 @@ MODULE_OBJS := \ error.o \ EventDispatcher.o \ EventMapper.o \ - EventRecorder.o \ file.o \ fs.o \ gui_options.o \ @@ -46,5 +45,10 @@ MODULE_OBJS += \ sinetables.o endif +ifdef ENABLE_EVENTRECORDER +MODULE_OBJS += \ + recorderfile.o +endif + # Include common rules include $(srcdir)/rules.mk diff --git a/common/mutex.cpp b/common/mutex.cpp index 4e6316528c6..f912e79591e 100644 --- a/common/mutex.cpp +++ b/common/mutex.cpp @@ -75,4 +75,4 @@ void StackLock::unlock() { g_system->unlockMutex(_mutex); } -} // End of namespace Common +} // End of namespace Common diff --git a/common/platform.cpp b/common/platform.cpp index a3a02c60ec7..3a16a3237a3 100644 --- a/common/platform.cpp +++ b/common/platform.cpp @@ -31,7 +31,7 @@ const PlatformDescription g_platforms[] = { { "amiga", "ami", "amiga", "Amiga", kPlatformAmiga }, { "atari", "atari-st", "st", "Atari ST", kPlatformAtariST }, { "c64", "c64", "c64", "Commodore 64", kPlatformC64 }, - { "pc", "dos", "ibm", "DOS", kPlatformPC }, + { "pc", "dos", "ibm", "DOS", kPlatformDOS }, { "pc98", "pc98", "pc98", "PC-98", kPlatformPC98 }, { "wii", "wii", "wii", "Nintendo Wii", kPlatformWii }, { "coco3", "coco3", "coco3", "CoCo3", kPlatformCoCo3 }, @@ -52,6 +52,8 @@ const PlatformDescription g_platforms[] = { { "playstation2", "ps2", "ps2", "Sony PlayStation 2", kPlatformPS2 }, { "cdi", "cdi", "cdi", "Philips CD-i", kPlatformCDi }, { "ios", "ios", "ios", "Apple iOS", kPlatformIOS }, + { "os2", "os2", "os2", "OS/2", kPlatformOS2 }, + { "beos", "beos", "beos", "BeOS", kPlatformBeOS }, { 0, 0, 0, "Default", kPlatformUnknown } }; diff --git a/common/platform.h b/common/platform.h index b972850029a..1243c59a49f 100644 --- a/common/platform.h +++ b/common/platform.h @@ -35,7 +35,7 @@ class String; * game in question. */ enum Platform { - kPlatformPC, + kPlatformDOS, kPlatformAmiga, kPlatformAtariST, kPlatformMacintosh, @@ -57,6 +57,8 @@ enum Platform { kPlatformPS2, kPlatformCDi, kPlatformIOS, + kPlatformOS2, + kPlatformBeOS, kPlatformUnknown = -1 }; @@ -77,6 +79,6 @@ extern const char *getPlatformCode(Platform id); extern const char *getPlatformAbbrev(Platform id); extern const char *getPlatformDescription(Platform id); -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/random.cpp b/common/random.cpp index 55fa3cbd308..a74ab831ea7 100644 --- a/common/random.cpp +++ b/common/random.cpp @@ -21,7 +21,7 @@ #include "common/random.h" #include "common/system.h" -#include "common/EventRecorder.h" +#include "gui/EventRecorder.h" namespace Common { @@ -30,13 +30,8 @@ RandomSource::RandomSource(const String &name) { // Use system time as RNG seed. Normally not a good idea, if you are using // a RNG for security purposes, but good enough for our purposes. assert(g_system); - uint32 seed = g_system->getMillis(); + uint32 seed = g_eventRec.getRandomSeed(name); setSeed(seed); - - // Register this random source with the event recorder. This may end - // up querying or resetting the current seed, so we must call it - // *after* the initial seed has been set. - g_eventRec.registerRandomSource(*this, name); } void RandomSource::setSeed(uint32 seed) { @@ -59,4 +54,4 @@ uint RandomSource::getRandomNumberRng(uint min, uint max) { return getRandomNumber(max - min) + min; } -} // End of namespace Common +} // End of namespace Common diff --git a/common/random.h b/common/random.h index 90f2ed5cb03..c8aec589467 100644 --- a/common/random.h +++ b/common/random.h @@ -74,6 +74,6 @@ public: uint getRandomNumberRng(uint min, uint max); }; -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/rdft.h b/common/rdft.h index 33869404041..76e95c363a5 100644 --- a/common/rdft.h +++ b/common/rdft.h @@ -20,7 +20,7 @@ * */ -// Based on eos' (I)RDFT code which is in turn +// Based on xoreos' (I)RDFT code which is in turn // Based upon the (I)RDFT code in FFmpeg // Copyright (c) 2009 Alex Converse @@ -44,6 +44,40 @@ namespace Common { * * Used in engines: * - scumm + * + * + * It has four modes: + * + * Below, n = 1 << bits + * + * (I)DFT_R2C: + * input: + * n real floats + * output: + * n/2 complex floats (stored as real part followed by imag part). + * + * The output represents the first half of the (I)DFT of the input. + * If F is the complex (I)DFT of the input, then + * output[0] = F[0] + i * F[n/2] and + * output[k] = F[k] for k = 1 .. n/2-1. + * Note that F[0] and F[k] are real since the input is real, and + * the remaining values of F can be reconstructed from symmetry if desired. + * + * (I)DFT_C2R: + * input: + * n/2 complex floats + * output: + * n real floats + * + * The input encodes a complex vector x of length n that has the + * required symmetry to have a real (I)DFT: + * x[0] = Re(input[0]) + * x[k] = input[k] for k = 1 .. n/2-1 + * x[n/2] = Im(input[0]) + * x[k] = conj(input[n-k]) for k = n/2+1 .. n-1 + * The output is then the real (I)DFT of x, divided by 2. + * + * TODO: Is this division by 2 intentional? */ class RDFT { diff --git a/common/recorderfile.cpp b/common/recorderfile.cpp new file mode 100644 index 00000000000..60c47e11ce2 --- /dev/null +++ b/common/recorderfile.cpp @@ -0,0 +1,708 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" +#include "gui/EventRecorder.h" +#include "common/md5.h" +#include "common/recorderfile.h" +#include "common/savefile.h" +#include "common/bufferedstream.h" +#include "graphics/thumbnail.h" +#include "graphics/surface.h" +#include "graphics/scaler.h" + +#define RECORD_VERSION 1 + +namespace Common { + +PlaybackFile::PlaybackFile() : _tmpRecordFile(_tmpBuffer, kRecordBuffSize), _tmpPlaybackFile(_tmpBuffer, kRecordBuffSize) { + _readStream = NULL; + _writeStream = NULL; + _screenshotsFile = NULL; + _mode = kClosed; +} + +PlaybackFile::~PlaybackFile() { + close(); +} + +bool PlaybackFile::openWrite(const String &fileName) { + close(); + _header.fileName = fileName; + _writeStream = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving(fileName), 128 * 1024); + _headerDumped = false; + _recordCount = 0; + if (_writeStream == NULL) { + return false; + } + _mode = kWrite; + return true; +} + +bool PlaybackFile::openRead(const String &fileName) { + close(); + _header.fileName = fileName; + _eventsSize = 0; + _tmpPlaybackFile.seek(0); + _readStream = wrapBufferedSeekableReadStream(g_system->getSavefileManager()->openForLoading(fileName), 128 * 1024, DisposeAfterUse::YES); + if (_readStream == NULL) { + debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=fail reason=\"file %s not found\"", fileName.c_str()); + return false; + } + if (!parseHeader()) { + debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=fail reason=\"header parsing failed\""); + return false; + } + _screenshotsFile = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving("screenshots.bin"), 128 * 1024); + debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=success"); + _mode = kRead; + return true; +} + +void PlaybackFile::close() { + delete _readStream; + _readStream = NULL; + if (_writeStream != NULL) { + dumpRecordsToFile(); + _writeStream->finalize(); + delete _writeStream; + _writeStream = NULL; + updateHeader(); + } + if (_screenshotsFile != NULL) { + _screenshotsFile->finalize(); + delete _screenshotsFile; + _screenshotsFile = NULL; + } + for (HashMap::iterator i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) { + free(i->_value.buffer); + } + _header.saveFiles.clear(); + _mode = kClosed; +} + +bool PlaybackFile::parseHeader() { + PlaybackFileHeader result; + ChunkHeader nextChunk; + _playbackParseState = kFileStateCheckFormat; + if (!readChunkHeader(nextChunk)) { + _playbackParseState = kFileStateError; + return false; + } + while ((_playbackParseState != kFileStateDone) && (_playbackParseState != kFileStateError)) { + if (processChunk(nextChunk)) { + if (!readChunkHeader(nextChunk)) { + warning("Error in header parsing"); + _playbackParseState = kFileStateError; + } + } + } + return _playbackParseState == kFileStateDone; +} + +bool PlaybackFile::checkPlaybackFileVersion() { + uint32 version; + version = _readStream->readUint32LE(); + if (version != RECORD_VERSION) { + warning("Incorrect playback file version. Expected version %d, but got %d.", RECORD_VERSION, version); + return false; + } + return true; +} + + +String PlaybackFile::readString(int len) { + String result; + char buf[50]; + int readSize = 49; + while (len > 0) { + if (len <= 49) { + readSize = len; + } + _readStream->read(buf, readSize); + buf[readSize] = 0; + result += buf; + len -= readSize; + } + return result; +} + +bool PlaybackFile::readChunkHeader(PlaybackFile::ChunkHeader &nextChunk) { + nextChunk.id = (FileTag)_readStream->readUint32LE(); + nextChunk.len = _readStream->readUint32LE(); + return !_readStream->err() && !_readStream->eos(); +} + +bool PlaybackFile::processChunk(ChunkHeader &nextChunk) { + switch (_playbackParseState) { + case kFileStateCheckFormat: + if (nextChunk.id == kFormatIdTag) { + _playbackParseState = kFileStateCheckVersion; + } else { + warning("Unknown playback file signature"); + _playbackParseState = kFileStateError; + } + break; + case kFileStateCheckVersion: + if ((nextChunk.id == kVersionTag) && checkPlaybackFileVersion()) { + _playbackParseState = kFileStateSelectSection; + } else { + _playbackParseState = kFileStateError; + } + break; + case kFileStateSelectSection: + switch (nextChunk.id) { + case kHeaderSectionTag: + _playbackParseState = kFileStateProcessHeader; + break; + case kHashSectionTag: + _playbackParseState = kFileStateProcessHash; + break; + case kRandomSectionTag: + _playbackParseState = kFileStateProcessRandom; + break; + case kEventTag: + case kScreenShotTag: + _readStream->seek(-8, SEEK_CUR); + _playbackParseState = kFileStateDone; + return false; + case kSaveTag: + _playbackParseState = kFileStateProcessSave; + break; + case kSettingsSectionTag: + _playbackParseState = kFileStateProcessSettings; + warning("Loading record header"); + break; + default: + _readStream->skip(nextChunk.len); + break; + } + break; + case kFileStateProcessSave: + if (nextChunk.id == kSaveRecordTag) { + readSaveRecord(); + } else { + _playbackParseState = kFileStateSelectSection; + return false; + } + break; + case kFileStateProcessHeader: + switch (nextChunk.id) { + case kAuthorTag: + _header.author = readString(nextChunk.len); + break; + case kCommentsTag: + _header.notes = readString(nextChunk.len); + break; + case kNameTag: + _header.name = readString(nextChunk.len); + break; + default: + _playbackParseState = kFileStateSelectSection; + return false; + } + break; + case kFileStateProcessHash: + if (nextChunk.id == kHashRecordTag) { + readHashMap(nextChunk); + } else { + _playbackParseState = kFileStateSelectSection; + return false; + } + break; + case kFileStateProcessRandom: + if (nextChunk.id == kRandomRecordTag) { + processRndSeedRecord(nextChunk); + } else { + _playbackParseState = kFileStateSelectSection; + return false; + } + break; + case kFileStateProcessSettings: + if (nextChunk.id == kSettingsRecordTag) { + if (!processSettingsRecord()) { + _playbackParseState = kFileStateError; + return false; + } + } else { + _playbackParseState = kFileStateSelectSection; + return false; + } + break; + default: + return false; + } + return true; +} + +void PlaybackFile::returnToChunkHeader() { + _readStream->seek(-8, SEEK_CUR); +} + +void PlaybackFile::readHashMap(ChunkHeader chunk) { + String hashName = readString(chunk.len - 32); + String hashMd5 = readString(32); + _header.hashRecords[hashName] = hashMd5; +} + +void PlaybackFile::processRndSeedRecord(ChunkHeader chunk) { + String randomSourceName = readString(chunk.len - 4); + uint32 randomSourceSeed = _readStream->readUint32LE(); + _header.randomSourceRecords[randomSourceName] = randomSourceSeed; +} + +bool PlaybackFile::processSettingsRecord() { + ChunkHeader keyChunk; + if (!readChunkHeader(keyChunk) || (keyChunk.id != kSettingsRecordKeyTag)) { + warning("Invalid format of settings section"); + return false; + } + String key = readString(keyChunk.len); + ChunkHeader valueChunk; + if (!readChunkHeader(valueChunk) || (valueChunk.id != kSettingsRecordValueTag)) { + warning("Invalid format of settings section"); + return false; + } + String value = readString(valueChunk.len); + _header.settingsRecords[key] = value; + return true; +} + + +bool PlaybackFile::readSaveRecord() { + ChunkHeader fileNameChunk; + if (!readChunkHeader(fileNameChunk) || (fileNameChunk.id != kSaveRecordNameTag)) { + warning("Invalid format of save section"); + return false; + } + String fileName = readString(fileNameChunk.len); + ChunkHeader saveBufferChunk; + if (!readChunkHeader(saveBufferChunk) || (saveBufferChunk.id != kSaveRecordBufferTag)) { + warning("Invalid format of save section"); + return false; + } + SaveFileBuffer buf; + buf.size = saveBufferChunk.len; + buf.buffer = (byte *)malloc(saveBufferChunk.len); + _readStream->read(buf.buffer, buf.size); + _header.saveFiles[fileName] = buf; + debugC(1, kDebugLevelEventRec, "playback:action=\"Load save file\" filename=%s len=%d", fileName.c_str(), buf.size); + return true; +} + + + +RecorderEvent PlaybackFile::getNextEvent() { + assert(_mode == kRead); + if (isEventsBufferEmpty()) { + PlaybackFile::ChunkHeader header; + header.id = kFormatIdTag; + while (header.id != kEventTag) { + if (!readChunkHeader(header) || _readStream->eos()) { + break; + } + switch (header.id) { + case kEventTag: + readEventsToBuffer(header.len); + break; + case kScreenShotTag: + _readStream->seek(-4, SEEK_CUR); + header.len = _readStream->readUint32BE(); + _readStream->skip(header.len-8); + break; + case kMD5Tag: + checkRecordedMD5(); + break; + default: + _readStream->skip(header.len); + break; + } + } + } + RecorderEvent result; + readEvent(result); + return result; +} + +bool PlaybackFile::isEventsBufferEmpty() { + return (uint32)_tmpPlaybackFile.pos() == _eventsSize; +} + +void PlaybackFile::readEvent(RecorderEvent& event) { + event.recordedtype = (RecorderEventType)_tmpPlaybackFile.readByte(); + switch (event.recordedtype) { + case kRecorderEventTypeTimer: + event.time = _tmpPlaybackFile.readUint32LE(); + break; + case kRecorderEventTypeNormal: + event.type = (EventType)_tmpPlaybackFile.readUint32LE(); + switch (event.type) { + case EVENT_KEYDOWN: + case EVENT_KEYUP: + event.time = _tmpPlaybackFile.readUint32LE(); + event.kbd.keycode = (KeyCode)_tmpPlaybackFile.readSint32LE(); + event.kbd.ascii = _tmpPlaybackFile.readUint16LE(); + event.kbd.flags = _tmpPlaybackFile.readByte(); + break; + case EVENT_MOUSEMOVE: + case EVENT_LBUTTONDOWN: + case EVENT_LBUTTONUP: + case EVENT_RBUTTONDOWN: + case EVENT_RBUTTONUP: + case EVENT_WHEELUP: + case EVENT_WHEELDOWN: + case EVENT_MBUTTONDOWN: + case EVENT_MBUTTONUP: + event.time = _tmpPlaybackFile.readUint32LE(); + event.mouse.x = _tmpPlaybackFile.readSint16LE(); + event.mouse.y = _tmpPlaybackFile.readSint16LE(); + break; + default: + event.time = _tmpPlaybackFile.readUint32LE(); + break; + } + break; + } + event.synthetic = true; +} + +void PlaybackFile::readEventsToBuffer(uint32 size) { + _readStream->read(_tmpBuffer, size); + _tmpPlaybackFile.seek(0); + _eventsSize = size; +} + +void PlaybackFile::saveScreenShot(Graphics::Surface &screen, byte md5[16]) { + dumpRecordsToFile(); + _writeStream->writeUint32LE(kMD5Tag); + _writeStream->writeUint32LE(16); + _writeStream->write(md5, 16); + Graphics::saveThumbnail(*_writeStream, screen); +} + +void PlaybackFile::dumpRecordsToFile() { + if (!_headerDumped) { + dumpHeaderToFile(); + _headerDumped = true; + } + if (_recordCount == 0) { + return; + } + _writeStream->writeUint32LE(kEventTag); + _writeStream->writeUint32LE(_tmpRecordFile.pos()); + _writeStream->write(_tmpBuffer, _tmpRecordFile.pos()); + _tmpRecordFile.seek(0); + _recordCount = 0; +} + +void PlaybackFile::dumpHeaderToFile() { + _writeStream->writeUint32LE(kFormatIdTag); + // Specify size for first tag as NULL since we cannot calculate + // size of the file at time of the header dumping + _writeStream->writeUint32LE(0); + _writeStream->writeUint32LE(kVersionTag); + _writeStream->writeUint32LE(4); + _writeStream->writeUint32LE(RECORD_VERSION); + writeHeaderSection(); + writeGameHash(); + writeRandomRecords(); + writeGameSettings(); + writeSaveFilesSection(); +} + +void PlaybackFile::writeHeaderSection() { + uint32 headerSize = 0; + if (!_header.author.empty()) { + headerSize = _header.author.size() + 8; + } + if (!_header.notes.empty()) { + headerSize += _header.notes.size() + 8; + } + if (!_header.name.empty()) { + headerSize += _header.name.size() + 8; + } + if (headerSize == 0) { + return; + } + _writeStream->writeUint32LE(kHeaderSectionTag); + _writeStream->writeUint32LE(headerSize); + if (!_header.author.empty()) { + _writeStream->writeUint32LE(kAuthorTag); + _writeStream->writeUint32LE(_header.author.size()); + _writeStream->writeString(_header.author); + } + if (!_header.notes.empty()) { + _writeStream->writeUint32LE(kCommentsTag); + _writeStream->writeUint32LE(_header.notes.size()); + _writeStream->writeString(_header.notes); + } + if (!_header.name.empty()) { + _writeStream->writeUint32LE(kNameTag); + _writeStream->writeUint32LE(_header.name.size()); + _writeStream->writeString(_header.name); + } +} + +void PlaybackFile::writeGameHash() { + uint32 hashSectionSize = 0; + for (StringMap::iterator i = _header.hashRecords.begin(); i != _header.hashRecords.end(); ++i) { + hashSectionSize = hashSectionSize + i->_key.size() + i->_value.size() + 8; + } + if (_header.hashRecords.size() == 0) { + return; + } + _writeStream->writeUint32LE(kHashSectionTag); + _writeStream->writeUint32LE(hashSectionSize); + for (StringMap::iterator i = _header.hashRecords.begin(); i != _header.hashRecords.end(); ++i) { + _writeStream->writeUint32LE(kHashRecordTag); + _writeStream->writeUint32LE(i->_key.size() + i->_value.size()); + _writeStream->writeString(i->_key); + _writeStream->writeString(i->_value); + } +} + +void PlaybackFile::writeRandomRecords() { + uint32 randomSectionSize = 0; + for (RandomSeedsDictionary::iterator i = _header.randomSourceRecords.begin(); i != _header.randomSourceRecords.end(); ++i) { + randomSectionSize = randomSectionSize + i->_key.size() + 12; + } + if (_header.randomSourceRecords.size() == 0) { + return; + } + _writeStream->writeUint32LE(kRandomSectionTag); + _writeStream->writeUint32LE(randomSectionSize); + for (RandomSeedsDictionary::iterator i = _header.randomSourceRecords.begin(); i != _header.randomSourceRecords.end(); ++i) { + _writeStream->writeUint32LE(kRandomRecordTag); + _writeStream->writeUint32LE(i->_key.size() + 4); + _writeStream->writeString(i->_key); + _writeStream->writeUint32LE(i->_value); + } +} + +void PlaybackFile::writeEvent(const RecorderEvent &event) { + assert(_mode == kWrite); + _recordCount++; + _tmpRecordFile.writeByte(event.recordedtype); + switch (event.recordedtype) { + case kRecorderEventTypeTimer: + _tmpRecordFile.writeUint32LE(event.time); + break; + case kRecorderEventTypeNormal: + _tmpRecordFile.writeUint32LE((uint32)event.type); + switch(event.type) { + case EVENT_KEYDOWN: + case EVENT_KEYUP: + _tmpRecordFile.writeUint32LE(event.time); + _tmpRecordFile.writeSint32LE(event.kbd.keycode); + _tmpRecordFile.writeUint16LE(event.kbd.ascii); + _tmpRecordFile.writeByte(event.kbd.flags); + break; + case EVENT_MOUSEMOVE: + case EVENT_LBUTTONDOWN: + case EVENT_LBUTTONUP: + case EVENT_RBUTTONDOWN: + case EVENT_RBUTTONUP: + case EVENT_WHEELUP: + case EVENT_WHEELDOWN: + case EVENT_MBUTTONDOWN: + case EVENT_MBUTTONUP: + _tmpRecordFile.writeUint32LE(event.time); + _tmpRecordFile.writeSint16LE(event.mouse.x); + _tmpRecordFile.writeSint16LE(event.mouse.y); + break; + default: + _tmpRecordFile.writeUint32LE(event.time); + break; + } + break; + } + if (_recordCount == kMaxBufferedRecords) { + dumpRecordsToFile(); + } +} + +void PlaybackFile::writeGameSettings() { + _writeStream->writeUint32LE(kSettingsSectionTag); + uint32 settingsSectionSize = 0; + for (StringMap::iterator i = _header.settingsRecords.begin(); i != _header.settingsRecords.end(); ++i) { + settingsSectionSize += i->_key.size() + i->_value.size() + 24; + } + _writeStream->writeUint32LE(settingsSectionSize); + for (StringMap::iterator i = _header.settingsRecords.begin(); i != _header.settingsRecords.end(); ++i) { + _writeStream->writeUint32LE(kSettingsRecordTag); + _writeStream->writeUint32LE(i->_key.size() + i->_value.size() + 16); + _writeStream->writeUint32LE(kSettingsRecordKeyTag); + _writeStream->writeUint32LE(i->_key.size()); + _writeStream->writeString(i->_key); + _writeStream->writeUint32LE(kSettingsRecordValueTag); + _writeStream->writeUint32LE(i->_value.size()); + _writeStream->writeString(i->_value); + } +} + +int PlaybackFile::getScreensCount() { + if (_mode != kRead) { + return 0; + } + _readStream->seek(0); + int result = 0; + while (skipToNextScreenshot()) { + uint32 size = _readStream->readUint32BE(); + _readStream->skip(size-8); + ++result; + } + return result; +} + +bool PlaybackFile::skipToNextScreenshot() { + while (true) { + FileTag id = (FileTag)_readStream->readUint32LE(); + if (_readStream->eos()) { + break; + } + if (id == kScreenShotTag) { + return true; + } + else { + uint32 size = _readStream->readUint32LE(); + _readStream->skip(size); + } + } + return false; +} + +Graphics::Surface *PlaybackFile::getScreenShot(int number) { + if (_mode != kRead) { + return NULL; + } + _readStream->seek(0); + int screenCount = 1; + while (skipToNextScreenshot()) { + if (screenCount == number) { + screenCount++; + _readStream->seek(-4, SEEK_CUR); + return Graphics::loadThumbnail(*_readStream); + } else { + uint32 size = _readStream->readUint32BE(); + _readStream->skip(size-8); + screenCount++; + } + } + return NULL; +} + +void PlaybackFile::updateHeader() { + if (_mode == kWrite) { + _readStream = g_system->getSavefileManager()->openForLoading(_header.fileName); + } + _readStream->seek(0); + skipHeader(); + String tmpFilename = "_" + _header.fileName; + _writeStream = g_system->getSavefileManager()->openForSaving(tmpFilename); + dumpHeaderToFile(); + uint32 readedSize = 0; + do { + readedSize = _readStream->read(_tmpBuffer, kRecordBuffSize); + _writeStream->write(_tmpBuffer, readedSize); + } while (readedSize != 0); + delete _writeStream; + _writeStream = NULL; + delete _readStream; + _readStream = NULL; + g_system->getSavefileManager()->removeSavefile(_header.fileName); + g_system->getSavefileManager()->renameSavefile(tmpFilename, _header.fileName); + if (_mode == kRead) { + openRead(_header.fileName); + } +} + +void PlaybackFile::skipHeader() { + while (true) { + uint32 id = _readStream->readUint32LE(); + if (_readStream->eos()) { + break; + } + if ((id == kScreenShotTag) || (id == kEventTag) || (id == kMD5Tag)) { + _readStream->seek(-4, SEEK_CUR); + return; + } + else { + uint32 size = _readStream->readUint32LE(); + _readStream->skip(size); + } + } +} + +void PlaybackFile::addSaveFile(const String &fileName, InSaveFile *saveStream) { + uint oldPos = saveStream->pos(); + saveStream->seek(0); + _header.saveFiles[fileName].buffer = (byte *)malloc(saveStream->size()); + _header.saveFiles[fileName].size = saveStream->size(); + saveStream->read(_header.saveFiles[fileName].buffer, saveStream->size()); + saveStream->seek(oldPos); +} + +void PlaybackFile::writeSaveFilesSection() { + uint size = 0; + for (HashMap::iterator i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) { + size += i->_value.size + i->_key.size() + 24; + } + if (size == 0) { + return; + } + _writeStream->writeSint32LE(kSaveTag); + _writeStream->writeSint32LE(size); + for (HashMap::iterator i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) { + _writeStream->writeSint32LE(kSaveRecordTag); + _writeStream->writeSint32LE(i->_key.size() + i->_value.size + 16); + _writeStream->writeSint32LE(kSaveRecordNameTag); + _writeStream->writeSint32LE(i->_key.size()); + _writeStream->writeString(i->_key); + _writeStream->writeSint32LE(kSaveRecordBufferTag); + _writeStream->writeSint32LE(i->_value.size); + _writeStream->write(i->_value.buffer, i->_value.size); + } +} + + +void PlaybackFile::checkRecordedMD5() { + uint8 currentMD5[16]; + uint8 savedMD5[16]; + Graphics::Surface screen; + _readStream->read(savedMD5, 16); + if (!g_eventRec.grabScreenAndComputeMD5(screen, currentMD5)) { + return; + } + uint32 seconds = g_system->getMillis(true) / 1000; + String screenTime = String::format("%.2d:%.2d:%.2d", seconds / 3600 % 24, seconds / 60 % 60, seconds % 60); + if (memcmp(savedMD5, currentMD5, 16) != 0) { + debugC(1, kDebugLevelEventRec, "playback:action=\"Check screenshot\" time=%s result = fail", screenTime.c_str()); + warning("Recorded and current screenshots are different"); + } else { + debugC(1, kDebugLevelEventRec, "playback:action=\"Check screenshot\" time=%s result = success", screenTime.c_str()); + } + Graphics::saveThumbnail(*_screenshotsFile, screen); + screen.free(); +} + + +} diff --git a/common/recorderfile.h b/common/recorderfile.h new file mode 100644 index 00000000000..1c95e5a9158 --- /dev/null +++ b/common/recorderfile.h @@ -0,0 +1,180 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef COMMON_RECORDERFILE_H +#define COMMON_RECORDERFILE_H + +#include "common/scummsys.h" +#include "common/events.h" +#include "common/mutex.h" +#include "common/memstream.h" +#include "common/config-manager.h" +#include "common/savefile.h" + +//capacity of records buffer +#define kMaxBufferedRecords 10000 +#define kRecordBuffSize sizeof(RecorderEvent) * kMaxBufferedRecords + +namespace Common { + +enum RecorderEventType { + kRecorderEventTypeNormal = 0, + kRecorderEventTypeTimer = 1 +}; + +struct RecorderEvent : Event { + RecorderEventType recordedtype; + uint32 time; +}; + + + +class PlaybackFile { + typedef HashMap RandomSeedsDictionary; + enum fileMode { + kRead = 0, + kWrite = 1, + kClosed = 2 + }; + enum PlaybackFileState { + kFileStateCheckFormat, + kFileStateCheckVersion, + kFileStateProcessHash, + kFileStateProcessHeader, + kFileStateProcessRandom, + kFileStateSelectSection, + kFileStateProcessSettings, + kFileStateProcessSave, + kFileStateDone, + kFileStateError + }; + enum FileTag { + kFormatIdTag = MKTAG('P','B','C','K'), + kVersionTag = MKTAG('V','E','R','S'), + kHeaderSectionTag = MKTAG('H','E','A','D'), + kHashSectionTag = MKTAG('H','A','S','H'), + kRandomSectionTag = MKTAG('R','A','N','D'), + kEventTag = MKTAG('E','V','N','T'), + kScreenShotTag = MKTAG('B','M','H','T'), + kSettingsSectionTag = MKTAG('S','E','T','T'), + kAuthorTag = MKTAG('H','A','U','T'), + kCommentsTag = MKTAG('H','C','M','T'), + kNameTag = MKTAG('H','N','A','M'), + kHashRecordTag = MKTAG('H','R','C','D'), + kRandomRecordTag = MKTAG('R','R','C','D'), + kSettingsRecordTag = MKTAG('S','R','E','C'), + kSettingsRecordKeyTag = MKTAG('S','K','E','Y'), + kSettingsRecordValueTag = MKTAG('S','V','A','L'), + kSaveTag = MKTAG('S','A','V','E'), + kSaveRecordTag = MKTAG('R','S','A','V'), + kSaveRecordNameTag = MKTAG('S','N','A','M'), + kSaveRecordBufferTag = MKTAG('S','B','U','F'), + kMD5Tag = MKTAG('M','D','5',' ') + }; + struct ChunkHeader { + FileTag id; + uint32 len; + }; +public: + struct SaveFileBuffer { + byte *buffer; + uint32 size; + }; + struct PlaybackFileHeader { + String fileName; + String author; + String name; + String notes; + String description; + StringMap hashRecords; + StringMap settingsRecords; + HashMap saveFiles; + RandomSeedsDictionary randomSourceRecords; + }; + PlaybackFile(); + ~PlaybackFile(); + + bool openWrite(const String &fileName); + bool openRead(const String &fileName); + void close(); + + RecorderEvent getNextEvent(); + void writeEvent(const RecorderEvent &event); + + void saveScreenShot(Graphics::Surface &screen, byte md5[16]); + Graphics::Surface *getScreenShot(int number); + int getScreensCount(); + + bool isEventsBufferEmpty(); + PlaybackFileHeader &getHeader() {return _header;} + void updateHeader(); + void addSaveFile(const String &fileName, InSaveFile *saveStream); +private: + WriteStream *_recordFile; + WriteStream *_writeStream; + WriteStream *_screenshotsFile; + MemoryReadStream _tmpPlaybackFile; + SeekableReadStream *_readStream; + SeekableMemoryWriteStream _tmpRecordFile; + + fileMode _mode; + bool _headerDumped; + int _recordCount; + uint32 _eventsSize; + byte _tmpBuffer[kRecordBuffSize]; + PlaybackFileHeader _header; + PlaybackFileState _playbackParseState; + + void skipHeader(); + bool parseHeader(); + bool processChunk(ChunkHeader &nextChunk); + void returnToChunkHeader(); + + bool readSaveRecord(); + void checkRecordedMD5(); + bool readChunkHeader(ChunkHeader &nextChunk); + void processRndSeedRecord(ChunkHeader chunk); + bool processSettingsRecord(); + + bool checkPlaybackFileVersion(); + + void dumpHeaderToFile(); + void writeSaveFilesSection(); + void writeGameSettings(); + void writeHeaderSection(); + void writeGameHash(); + void writeRandomRecords(); + + void dumpRecordsToFile(); + + String readString(int len); + void readHashMap(ChunkHeader chunk); + + bool skipToNextScreenshot(); + void readEvent(RecorderEvent& event); + void readEventsToBuffer(uint32 size); + bool grabScreenAndComputeMD5(Graphics::Surface &screen, uint8 md5[16]); +}; + +} // End of namespace Common + +#endif diff --git a/common/rect.h b/common/rect.h index 8d1243f7e40..5790cf7c0f4 100644 --- a/common/rect.h +++ b/common/rect.h @@ -266,6 +266,6 @@ struct Rect { } }; -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/scummsys.h b/common/scummsys.h index 368c1ec1cf3..3fdabe324dd 100644 --- a/common/scummsys.h +++ b/common/scummsys.h @@ -51,7 +51,7 @@ #include #include - // MSVC's vsnprintf is either non-existant (2003) or bugged since it + // MSVC's vsnprintf is either non-existent (2003) or bugged since it // does not always include a terminating NULL (2005+). To work around // that we fix up the _vsnprintf included. Note that the return value // will still not match C99's specs! @@ -144,7 +144,10 @@ #endif #endif - +// Include our C++11 compatability header for pre-C++11 compilers. +#if __cplusplus < 201103L +#include "common/c++11-compat.h" +#endif // Use config.h, generated by configure #if defined(HAVE_CONFIG_H) @@ -310,6 +313,7 @@ #endif #endif +// ResidualVM specific: #ifndef LOCAL_PI #define LOCAL_PI 3.14159265358979323846 #endif diff --git a/common/sinetables.cpp b/common/sinetables.cpp index a6ec99469d4..7338166d39d 100644 --- a/common/sinetables.cpp +++ b/common/sinetables.cpp @@ -34,15 +34,15 @@ SineTable::SineTable(int bitPrecision) { int m = 1 << _bitPrecision; double freq = 2 * M_PI / m; - _table = new float[m]; + _table = new float[m / 2]; - // Table contains sin(2*pi*x/n) for 0<=x<=n/4, - // followed by its reverse - for (int i = 0; i <= m / 4; i++) + // Table contains sin(2*pi*i/m) for 0<=i T *Singleton::_singleton = 0 -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/str.cpp b/common/str.cpp index 84805082acc..4a107923737 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -361,6 +361,25 @@ void String::deleteChar(uint32 p) { _size--; } +void String::erase(uint32 p, uint32 len) { + assert(p < _size); + + makeUnique(); + // If len == npos or p + len is over the end, remove all the way to the end + if (len == npos || p + len >= _size) { + // Delete char at p as well. So _size = (p - 1) + 1 + _size = p; + // Null terminate + _str[_size] = 0; + return; + } + + for ( ; p + len <= _size; p++) { + _str[p] = _str[p + len]; + } + _size -= len; +} + void String::clear() { decRefCount(_extern._refCount); @@ -370,7 +389,7 @@ void String::clear() { } void String::setChar(char c, uint32 p) { - assert(p <= _size); + assert(p < _size); makeUnique(); _str[p] = c; @@ -764,7 +783,7 @@ String tag2string(uint32 tag) { str[4] = '\0'; // Replace non-printable chars by dot for (int i = 0; i < 4; ++i) { - if (!isprint((unsigned char)str[i])) + if (!Common::isPrint(str[i])) str[i] = '.'; } return String(str); @@ -850,7 +869,7 @@ size_t strlcat(char *dst, const char *src, size_t size) { return dstLength + (src - srcStart); } -} // End of namespace Common +} // End of namespace Common // Portable implementation of stricmp / strcasecmp / strcmpi. // TODO: Rename this to Common::strcasecmp diff --git a/common/str.h b/common/str.h index 50391307077..6b4475e1c4f 100644 --- a/common/str.h +++ b/common/str.h @@ -43,6 +43,8 @@ namespace Common { * behavior in some operations. */ class String { +public: + static const uint32 npos = 0xFFFFFFFF; protected: /** * The size of the internal storage. Increasing this means less heap @@ -191,6 +193,9 @@ public: /** Remove the character at position p from the string. */ void deleteChar(uint32 p); + /** Remove all characters from position p to the p + len. If len = String::npos, removes all characters to the end */ + void erase(uint32 p, uint32 len = npos); + /** Set character c at position p, replacing the previous character there. */ void setChar(char c, uint32 p); diff --git a/common/stream.cpp b/common/stream.cpp index 55ffa23649b..f49603c8821 100644 --- a/common/stream.cpp +++ b/common/stream.cpp @@ -342,7 +342,7 @@ uint32 BufferedReadStream::read(void *dataPtr, uint32 dataSize) { return alreadyRead + dataSize; } -} // End of nameless namespace +} // End of anonymous namespace ReadStream *wrapBufferedReadStream(ReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream) { @@ -420,7 +420,7 @@ bool BufferedSeekableReadStream::seek(int32 offset, int whence) { return true; } -} // End of nameless namespace +} // End of anonymous namespace SeekableReadStream *wrapBufferedSeekableReadStream(SeekableReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream) { if (parentStream) @@ -502,7 +502,7 @@ public: }; -} // End of nameless namespace +} // End of anonymous namespace WriteStream *wrapBufferedWriteStream(WriteStream *parentStream, uint32 bufSize) { if (parentStream) @@ -510,4 +510,4 @@ WriteStream *wrapBufferedWriteStream(WriteStream *parentStream, uint32 bufSize) return 0; } -} // End of namespace Common +} // End of namespace Common diff --git a/common/stream.h b/common/stream.h index 26c04e5bf6c..33ebc95a86f 100644 --- a/common/stream.h +++ b/common/stream.h @@ -454,6 +454,6 @@ public: }; -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/system.cpp b/common/system.cpp index 7e08f8af179..36b845093ed 100644 --- a/common/system.cpp +++ b/common/system.cpp @@ -30,6 +30,9 @@ #include "common/taskbar.h" #include "common/updates.h" #include "common/textconsole.h" +#ifdef ENABLE_EVENTRECORDER +#include "gui/EventRecorder.h" +#endif #include "backends/audiocd/default/default-audiocd.h" #include "backends/fs/fs-factory.h" @@ -84,7 +87,7 @@ void OSystem::initBackend() { error("Backend failed to instantiate audio CD manager"); if (!_eventManager) error("Backend failed to instantiate event manager"); - if (!_timerManager) + if (!getTimerManager()) error("Backend failed to instantiate timer manager"); // TODO: We currently don't check _savefileManager, because at least @@ -153,3 +156,15 @@ Common::String OSystem::getDefaultConfigFileName() { Common::String OSystem::getSystemLanguage() const { return "en_US"; } + +Common::TimerManager *OSystem::getTimerManager() { + return _timerManager; +} + +Common::SaveFileManager *OSystem::getSavefileManager() { +#ifdef ENABLE_EVENTRECORDER + return g_eventRec.getSaveManager(_savefileManager); +#else + return _savefileManager; +#endif +} diff --git a/common/system.h b/common/system.h index 22b955d840b..53074d36bc6 100644 --- a/common/system.h +++ b/common/system.h @@ -966,8 +966,14 @@ public: /** @name Events and Time */ //@{ - /** Get the number of milliseconds since the program was started. */ - virtual uint32 getMillis() = 0; + /** Get the number of milliseconds since the program was started. + + @param skipRecord Skip recording of this value by event recorder. + This could be needed particularly when we are in + an on-screen GUI loop where player can pause + the recording. + */ + virtual uint32 getMillis(bool skipRecord = false) = 0; /** Delay/sleep for the specified amount of milliseconds. */ virtual void delayMillis(uint msecs) = 0; @@ -983,9 +989,7 @@ public: * Return the timer manager singleton. For more information, refer * to the TimerManager documentation. */ - inline Common::TimerManager *getTimerManager() { - return _timerManager; - } + virtual Common::TimerManager *getTimerManager(); /** * Return the event manager singleton. For more information, refer @@ -1164,9 +1168,7 @@ public: * and other modifiable persistent game data. For more information, * refer to the SaveFileManager documentation. */ - inline Common::SaveFileManager *getSavefileManager() { - return _savefileManager; - } + Common::SaveFileManager *getSavefileManager(); #if defined(USE_TASKBAR) /** diff --git a/common/taskbar.h b/common/taskbar.h index 6f28028e746..b4ec6737399 100644 --- a/common/taskbar.h +++ b/common/taskbar.h @@ -136,7 +136,7 @@ public: virtual void clearError() {} }; -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/textconsole.h b/common/textconsole.h index 364c49b2e9e..12f15e5e4b5 100644 --- a/common/textconsole.h +++ b/common/textconsole.h @@ -56,7 +56,7 @@ typedef void (*ErrorHandler)(const char *msg); */ void setErrorHandler(ErrorHandler handler); -} // End of namespace Common +} // End of namespace Common void NORETURN_PRE error(const char *s, ...) GCC_PRINTF(1, 2) NORETURN_POST; diff --git a/common/unzip.cpp b/common/unzip.cpp index ab659343a20..69b9ff67cbc 100644 --- a/common/unzip.cpp +++ b/common/unzip.cpp @@ -1534,4 +1534,4 @@ Archive *makeZipArchive(SeekableReadStream *stream) { return new ZipArchive(zipFile); } -} // End of namespace Common +} // End of namespace Common diff --git a/common/unzip.h b/common/unzip.h index 06480b0054e..2e0dae831a0 100644 --- a/common/unzip.h +++ b/common/unzip.h @@ -56,6 +56,6 @@ Archive *makeZipArchive(const FSNode &node); */ Archive *makeZipArchive(SeekableReadStream *stream); -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/updates.h b/common/updates.h index 4d58a216fb5..0012808a170 100644 --- a/common/updates.h +++ b/common/updates.h @@ -95,7 +95,7 @@ public: virtual UpdateInterval getUpdateCheckInterval() { return kUpdateIntervalNotSupported; } }; -} // End of namespace Common +} // End of namespace Common #endif diff --git a/common/util.cpp b/common/util.cpp index 4d9ff11c5c1..3d40fffff59 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -26,6 +26,7 @@ #define FORBIDDEN_SYMBOL_EXCEPTION_islower #define FORBIDDEN_SYMBOL_EXCEPTION_isspace #define FORBIDDEN_SYMBOL_EXCEPTION_isupper +#define FORBIDDEN_SYMBOL_EXCEPTION_isprint #include "common/util.h" @@ -144,4 +145,8 @@ bool isUpper(int c) { return isupper((byte)c); } +bool isPrint(int c) { + ENSURE_ASCII_CHAR(c); + return isprint((byte)c); +} } // End of namespace Common diff --git a/common/util.h b/common/util.h index 78340980d5c..4ca1c429299 100644 --- a/common/util.h +++ b/common/util.h @@ -165,6 +165,17 @@ bool isSpace(int c); */ bool isUpper(int c); -} // End of namespace Common +/** + * Test whether the given character is printable. This includes the space + * character (' '). + * + * If the parameter is outside the range of a signed or unsigned char, then + * false is returned. + * + * @param c the character to test + * @return true if the character is printable, false otherwise. + */ +bool isPrint(int c); +} // End of namespace Common #endif diff --git a/common/winexe.cpp b/common/winexe.cpp index 7cfc1404523..877ab6baa17 100644 --- a/common/winexe.cpp +++ b/common/winexe.cpp @@ -73,7 +73,7 @@ String WinResourceID::toString() const { if (_idType == kIDTypeString) return _name; else if (_idType == kIDTypeNumerical) - return String::format("%08x", _id); + return String::format("0x%08x", _id); return ""; } diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp index f0b7f1cc813..c80d5e15beb 100644 --- a/common/xmlparser.cpp +++ b/common/xmlparser.cpp @@ -298,7 +298,7 @@ bool XMLParser::closeKey() { bool XMLParser::parse() { if (_stream == 0) - return parserError("XML stream not ready for reading."); + return false; // Make sure we are at the start of the stream. _stream->seek(0, SEEK_SET); diff --git a/common/zlib.cpp b/common/zlib.cpp index fc8f351054f..920338e57e6 100644 --- a/common/zlib.cpp +++ b/common/zlib.cpp @@ -392,17 +392,21 @@ public: #endif // USE_ZLIB SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, uint32 knownSize) { -#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) + if (isCompressed) { +#if defined(USE_ZLIB) return new GZipReadStream(toBeWrapped, knownSize); - } +#else + delete toBeWrapped; + return NULL; #endif + } + } return toBeWrapped; } @@ -415,4 +419,4 @@ WriteStream *wrapCompressedWriteStream(WriteStream *toBeWrapped) { } -} // End of namespace Common +} // End of namespace Common diff --git a/common/zlib.h b/common/zlib.h index 6a840f5fdcc..d940f3f3a15 100644 --- a/common/zlib.h +++ b/common/zlib.h @@ -103,7 +103,9 @@ bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcL * 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). + * (and in particular, not wrapped). In the latter case the stream is + * returned wrapped, unless there is no ZLIB support, then NULL is returned + * and the old stream is destroyed. * * Certain GZip-formats don't supply an easily readable length, if you * still need the length carried along with the stream, and you know @@ -129,6 +131,6 @@ SeekableReadStream *wrapCompressedReadStream(SeekableReadStream *toBeWrapped, ui */ WriteStream *wrapCompressedWriteStream(WriteStream *toBeWrapped); -} // End of namespace Common +} // End of namespace Common #endif diff --git a/configure b/configure index fea8afa5ade..9c2e80aa995 100755 --- a/configure +++ b/configure @@ -104,7 +104,7 @@ _srcdir=`dirname $0` # #ResidualVM defaults: mpeg2=auto, faad=no, opengles=no, vorbis=no, tremor=no # mt32emu=no, translation=no, flac=no, seq_midi=no, snd_io=no, timidity=no, png=no -# theoradec=no, fluidsynth=no, opengles2=no +# theoradec=no, fluidsynth=no, opengles2=no, eventrec=no # # Default lib behaviour yes/no/auto _vorbis=no @@ -120,6 +120,8 @@ _zlib=auto _sparkle=auto _png=no _mpeg2=auto +_sparkle=auto +_png=no _theoradec=no _faad=no _fluidsynth=no @@ -135,6 +137,7 @@ _libunity=auto _debug_build=auto _release_build=auto _optimizations=auto +_use_cxx11=no _verbose_build=no _text_console=no _mt32emu=no @@ -144,9 +147,10 @@ _enable_prof=no _global_constructors=no _bink=yes _safedisc=no -# Default vkeybd/keymapper options +# Default vkeybd/keymapper/eventrec options _vkeybd=no _keymapper=no +_eventrec=no # GUI translation options _translation=no # Default platform settings @@ -179,7 +183,7 @@ _nasmpath="$PATH" NASMFLAGS="" NASM="" _tainted_build=no -# residualvm specific +# ResidualVM specific: _android_sysroot_path="" # The following variables are automatically detected, and should not # be modified otherwise. Consider them read-only. @@ -238,7 +242,11 @@ cc_check_no_clean() { echo >> "$TMPLOG" echo "$CXX $LDFLAGS $CXXFLAGS $TMPC -o $TMPO$HOSTEXEEXT $@" >> "$TMPLOG" rm -f "$TMPO$HOSTEXEEXT" - ( $CXX $LDFLAGS $CXXFLAGS "$TMPC" -o "$TMPO$HOSTEXEEXT" "$@" ) >> "$TMPLOG" 2>&1 + if test "-c" = "$*" ; then + ( $CXX $CXXFLAGS "$TMPC" -o "$TMPO$HOSTEXEEXT" "$@" ) >> "$TMPLOG" 2>&1 + else + ( $CXX $LDFLAGS $CXXFLAGS "$TMPC" -o "$TMPO$HOSTEXEEXT" "$@" ) >> "$TMPLOG" 2>&1 + fi TMPR="$?" echo "return code: $TMPR" >> "$TMPLOG" echo >> "$TMPLOG" @@ -820,7 +828,7 @@ Usage: $0 [OPTIONS]... Configuration: -h, --help display this help and exit - --backend=BACKEND backend to build (android, samsungtv, sdl, null) [sdl] + --backend=BACKEND backend to build (android, samsungtv, sdl) [sdl] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX @@ -863,6 +871,7 @@ Game engines: The values of for these options are as follows: $engines_help Optional Features: + --enable-c++11 build as C++11 if the compiler allows that --disable-debug disable building with debugging symbols --enable-Werror treat warnings as errors --enable-release enable building in release mode (this activates @@ -876,6 +885,8 @@ Optional Features: --disable-savegame-timestamp don't use timestamps for blank savegame descriptions --disable-translation don't build support for translated messages --disable-taskbar don't build support for taskbar and launcher integration + --enable-vkeybd build virtual keyboard support + --enable-keymapper build key mapper support --enable-updates build support for updates --enable-text-console use text console instead of graphical console --enable-verbose-build enable regular echoing of commands during build @@ -904,7 +915,7 @@ Optional Libraries: --disable-zlib disable zlib (compression) support [autodetect] --with-mpeg2-prefix=DIR Prefix where libmpeg2 is installed (optional) - --disable-mpeg2 enable mpeg2 codec for cutscenes [autodetect] + --enable-mpeg2 enable mpeg2 codec for cutscenes [autodetect] --disable-opengl disable OpenGL (ES) support [autodetect] @@ -959,7 +970,10 @@ done # for parm in ... for ac_option in $@; do case "$ac_option" in +# --disable-16bit) _16bit=no ;; #ResidualVM: not supported --disable-savegame-timestamp) _savegame_timestamp=no ;; +# --disable-scalers) _build_scalers=no ;; #ResidualVM: not supported +# --disable-hq-scalers) _build_hq_scalers=no ;; #ResidualVM: not supported --enable-alsa) _alsa=yes ;; --disable-alsa) _alsa=no ;; --enable-seq-midi) _seq_midi=yes ;; @@ -982,7 +996,6 @@ for ac_option in $@; do --disable-sparkle) _sparkle=no ;; --enable-nasm) _nasm=yes ;; --disable-nasm) _nasm=no ;; -#ResidualVM specific option: --enable-mpeg2) _mpeg2=yes ;; --disable-mpeg2) _mpeg2=no ;; --disable-png) _png=no ;; @@ -992,8 +1005,7 @@ for ac_option in $@; do --disable-faad) _faad=no ;; --enable-faad) _faad=yes ;; --disable-fluidsynth) _fluidsynth=no ;; -#ResidualVM specific option: - --enable-fluidsynth) _fluidsynth=yes ;; + --enable-fluidsynth) _fluidsynth=yes ;; #ResidualVM specific option --enable-readline) _readline=yes ;; --disable-readline) _readline=no ;; --enable-freetype2) _freetype2=yes ;; @@ -1008,8 +1020,8 @@ for ac_option in $@; do --disable-opengl) _opengl=no ;; --enable-bink) _bink=yes ;; --disable-bink) _bink=no ;; - --enable-safedisc) _safedisc=yes ;; - --disable-safedisc) _safedisc=no ;; + --enable-safedisc) _safedisc=yes ;; #ResidualVM specific option + --disable-safedisc) _safedisc=no ;; #ResidualVM specific option --enable-verbose-build) _verbose_build=yes ;; --enable-plugins) _dynamic_modules=yes ;; --default-dynamic) _plugins_default=dynamic ;; @@ -1021,6 +1033,8 @@ for ac_option in $@; do --disable-vkeybd) _vkeybd=no ;; --enable-keymapper) _keymapper=yes ;; --disable-keymapper) _keymapper=no ;; +# --enable-eventrecorder) _eventrec=yes ;; #ResidualVM: not supported +# --disable-eventrecorder) _eventrec=no ;; #ResidualVM: not supported --enable-text-console) _text_console=yes ;; --disable-text-console) _text_console=no ;; --with-fluidsynth-prefix=*) @@ -1028,7 +1042,6 @@ for ac_option in $@; do FLUIDSYNTH_CFLAGS="-I$arg/include" FLUIDSYNTH_LIBS="-L$arg/lib" ;; -#ResidualVM specific option: --with-mpeg2-prefix=*) arg=`echo $ac_option | cut -d '=' -f 2` MPEG2_CFLAGS="-I$arg/include" @@ -1113,6 +1126,12 @@ for ac_option in $@; do --backend=*) _backend=`echo $ac_option | cut -d '=' -f 2` ;; + --enable-c++11) + _use_cxx11=yes + ;; + --disable-c++11) + _use_cxx11=no + ;; --enable-debug) _debug_build=yes ;; @@ -1233,16 +1252,6 @@ arm-riscos) _host_os=riscos _host_cpu=arm ;; -bada) - _host_os=bada - if test "$_debug_build" = yes; then - _host_cpu=i686 - _host_alias=i686-mingw32 - else - _host_cpu=arm - _host_alias=arm-samsung-nucleuseabi - fi - ;; caanoo) _host_os=gph-linux _host_cpu=arm @@ -1368,6 +1377,11 @@ samsungtv) _host_cpu=arm _host_alias=arm-linux-gnueabi ;; +tizen) + _host_os=tizen + _host_cpu=arm + _host_alias=arm-linux-gnueabi + ;; webos) _host_os=webos _host_cpu=arm @@ -1421,15 +1435,10 @@ fi case $_host in caanoo | gp2x | gp2xwiz | openpandora | ps2) if test "$_debug_build" = auto; then - # If you want to debug one of these platforms, use '--disable-release --enable-debug' + # If you want to debug one of these platforms, use '--disable-optimizations --enable-debug' _debug_build=no fi - if test "$_release_build" = auto; then - # Enable release build by default. - _release_build=yes - fi - if test "$_optimizations" = auto; then # Enable optimizations by default. _optimizations=yes @@ -1481,7 +1490,7 @@ android) echo "Please set ANDROID_NDK in your environment. export ANDROID_NDK=" exit 1 fi - # residualvm specific + # ResidualVM specific start -> if test ! -d android-toolchain; then echo "Installing Android toolchain to build directory..." "$ANDROID_NDK/build/tools/make-standalone-toolchain.sh" --platform=android-8 --install-dir=android-toolchain @@ -1490,12 +1499,7 @@ android) fi PATH="android-toolchain/bin:$PATH" _android_sysroot_path="android-toolchain/bin:" - ;; -bada) - if test -z "$BADA_SDK"; then - echo "Please set BADA_SDK in your environment. export BADA_SDK=" - exit 1 - fi + # ResidualVM specific end <- ;; ds | gamecube | wii) if test -z "$DEVKITPRO"; then @@ -1538,6 +1542,12 @@ psp) exit 1 fi ;; +tizen) + if test -z "$TIZEN_ROOTSTRAP"; then + echo "Please set TIZEN_ROOTSTRAP in your environment. export TIZEN_ROOTSTRAP=" + exit 1 + fi + ;; webos) if test -z "$WEBOS_SDK"; then echo "Please set WEBOS_SDK in your environment. export WEBOS_SDK=" @@ -1570,7 +1580,7 @@ EOF if test -n "$_host"; then # In cross-compiling mode, we cannot run the result - eval "$1 $CXXFLAGS $LDFLAGS -o $TMPO.o -c tmp_cxx_compiler.cpp" 2> /dev/null && cc_check_clean tmp_cxx_compiler.cpp + eval "$1 $CXXFLAGS -o $TMPO.o -c tmp_cxx_compiler.cpp" 2> /dev/null && cc_check_clean tmp_cxx_compiler.cpp else eval "$1 $CXXFLAGS $LDFLAGS -o $TMPO$HOSTEXEEXT tmp_cxx_compiler.cpp" 2> /dev/null && eval "$TMPO$HOSTEXEEXT 2> /dev/null" && cc_check_clean tmp_cxx_compiler.cpp fi @@ -1707,6 +1717,18 @@ if test "$cxx_verc_fail" = yes ; then exit 1 fi +# +# Check whether the compiler supports C++11 +# +have_cxx11=no +cat > $TMPC << EOF +int main(int argc, char *argv[]) { if (argv == nullptr) return -1; else return 0; } +EOF +cc_check -std=c++11 && have_cxx11=yes +if test "$_use_cxx11" = "yes" ; then + _use_cxx11=$have_cxx11 +fi + # # Setup compiler specific CXXFLAGS now that we know the compiler version. # Foremost, this means enabling various warnings. @@ -1714,15 +1736,18 @@ fi # if test "$have_gcc" = yes ; then if test "$_cxx_major" -ge "3" ; then - case $_host_os in - # newlib-based system include files suppress non-C89 function - # declarations under __STRICT_ANSI__ - amigaos* | android | bada | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | wii | wince ) - ;; - *) + # Try to use ANSI mode when C++11 is disabled. + if test "$_use_cxx11" = "no" ; then + case $_host_os in + # newlib-based system include files suppress non-C89 function + # declarations under __STRICT_ANSI__ + amigaos* | android | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | tizen | wii | wince ) + ;; + *) CXXFLAGS="$CXXFLAGS -ansi" ;; - esac + esac + fi CXXFLAGS="$CXXFLAGS -W -Wno-unused-parameter" add_line_to_config_mk 'HAVE_GCC3 = 1' add_line_to_config_mk 'CXX_UPDATE_DEP_FLAG = -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" -MQ "$@" -MP' @@ -1738,12 +1763,21 @@ elif test "$have_icc" = yes ; then add_line_to_config_mk 'CXX_UPDATE_DEP_FLAG = -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" -MQ "$@" -MP' fi; +# +# Update status about C++11 mode +# +echo_n "Building as C++11... " +if test "$_use_cxx11" = "yes" ; then + CXXFLAGS="$CXXFLAGS -std=c++11" +fi +echo $_use_cxx11 + # By default, we add -pedantic to the CXXFLAGS to catch some potentially # non-portable constructs, like use of GNU extensions. # However, some platforms use GNU extensions in system header files, so # for these we must not use -pedantic. case $_host_os in -android | gamecube | psp | wii | webos) +android | gamecube | psp | tizen | wii | webos) ;; *) # ICC does not support pedantic, while GCC and clang do. @@ -1917,7 +1951,7 @@ cc_check_clean tmp_find_type_with_size.cpp # for the smaller sizes. echo_n "Alignment required... " case $_host_cpu in - i[3-6]86 | x86_64 | ppc*) + i[3-6]86 | amd64 | x86_64 | ppc*) # Unaligned access should work _need_memalign=no ;; @@ -1964,7 +1998,7 @@ case $_host_cpu in echo "PowerPC" DEFINES="$DEFINES -DPPC_TARGET" ;; - x86_64) + amd64 | x86_64) echo "x86_64" ;; *) @@ -2029,16 +2063,6 @@ case $_host_os in add_line_to_config_mk "ANDROID_SDK = $ANDROID_SDK" _seq_midi=no ;; - bada) - BADA_SDK_ROOT="`cygpath -m ${BADA_SDK}`" - add_line_to_config_mk "BADA_SDK = $BADA_SDK" - add_line_to_config_mk "BADA_SDK_ROOT = $BADA_SDK_ROOT" - - # assume dependencies have been installed in cygwin's /usr/local - CYGWIN_USR_LOCAL="`cygpath -m /usr/local`" - LDFLAGS="$LDFLAGS -L${CYGWIN_USR_LOCAL}/lib" - CXXFLAGS="$CXXFLAGS -I${CYGWIN_USR_LOCAL}/include" - ;; beos*) DEFINES="$DEFINES -DSYSTEM_NOT_SUPPORTING_D_TYPE" # Needs -lbind -lsocket for the timidity MIDI driver @@ -2154,7 +2178,7 @@ case $_host_os in # When not cross-compiling, enable large file support, but don't # care if getconf doesn't exist or doesn't recognize LFS_CFLAGS. if test -z "$_host"; then - CXXFLAGS="$CXXFLAGS $(getconf LFS_CFLAGS 2>/dev/null)" + CXXFLAGS="$CXXFLAGS `getconf LFS_CFLAGS 2>/dev/null`" fi ;; maemo) @@ -2216,6 +2240,12 @@ case $_host_os in # Needs -lbind -lsocket for the timidity MIDI driver LIBS="$LIBS -lnsl -lsocket" ;; + tizen) + add_line_to_config_mk "TIZEN_ROOTSTRAP = $TIZEN_ROOTSTRAP" + LDFLAGS="$LDFLAGS --sysroot=${TIZEN_ROOTSTRAP}" + LDFLAGS="$LDFLAGS -L${TIZEN_LIBS}/lib" + CXXFLAGS="$CXXFLAGS -I${TIZEN_LIBS}/include" + ;; webos) CXXFLAGS="$CXXFLAGS --sysroot=$WEBOS_PDK/arm-gcc/sysroot" CXXFLAGS="$CXXFLAGS -I$WEBOS_PDK/include" @@ -2294,22 +2324,6 @@ if test -n "$_host"; then arm-riscos|linupy) DEFINES="$DEFINES -DLINUPY" ;; - bada) - _unix=yes - _backend="bada" - _port_mk="backends/platform/bada/bada.mk" - if test "$_debug_build" = yes; then - _arm_asm=no - else - _arm_asm=yes - fi - _taskbar=no - _build_scalers=no - _seq_midi=no - _mt32emu=no - _timidity=no - _vkeybd=yes - ;; bfin*) ;; caanoo) @@ -2536,8 +2550,10 @@ if test -n "$_host"; then DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" if test "$_release_build" = no; then DEFINES="$DEFINES -DOP_DEBUG" - else - # Use -O3 on the OpenPandora for non-debug builds. + fi + + # Use -O3 on the OpenPandora for optimized builds. + if test "$_optimizations" = yes; then _optimization_level=-O3 fi define_in_config_if_yes yes 'USE_ARM_NEON_ASPECT_CORRECTOR' @@ -2619,6 +2635,18 @@ if test -n "$_host"; then _mt32emu=no _vkeybd=yes ;; + tizen) + _unix=yes + _backend="tizen" + _port_mk="backends/platform/tizen/tizen.mk" + _arm_asm=yes + _taskbar=no + _build_scalers=no + _seq_midi=no + _mt32emu=no + _timidity=no + _vkeybd=yes + ;; webos) _backend="webos" _port_mk="backends/platform/webos/webos.mk" @@ -2668,37 +2696,10 @@ case $_backend in CXXFLAGS="$CXXFLAGS -Wa,--noexecstack" LDFLAGS="$LDFLAGS -Wl,-z,noexecstack" INCLUDES="$INCLUDES -I$ANDROID_NDK/sources/cxx-stl/system/include" + # ResidualVM specific: below two lines: DEFINES="$DEFINES -DANDROID_BACKEND" add_line_to_config_mk "ANDROID_BACKEND = 1" ;; - bada) - # dirent.h not available. NONSTANDARD_PORT==ensure portdefs.h is included - DEFINES="$DEFINES -DBADA -DDISABLE_STDIO_FILESTREAM -DNONSTANDARD_PORT" - DEFINES="$DEFINES -DNO_STDERR_STDOUT" - DEFINES="$DEFINES -DDISABLE_COMMAND_LINE" - INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/bada ' - INCLUDES="$INCLUDES "'-I$(BADA_SDK)/include' - INCLUDES="$INCLUDES "'-I$(BADA_SDK_ROOT)/Include' - if test "$_debug_build" = yes; then - # debug using with the simulator - CXXFLAGS="$CXXFLAGS -D_DEBUG -DSHP -DBUILD_DLL -fmessage-length=0" - else - # created a shared library for inclusion via the eclipse build - CXXFLAGS="$CXXFLAGS -DSHP" - CXXFLAGS="$CXXFLAGS -fpic" - CXXFLAGS="$CXXFLAGS -fshort-wchar" - CXXFLAGS="$CXXFLAGS -mcpu=cortex-a8" - CXXFLAGS="$CXXFLAGS -mfpu=vfpv3" - CXXFLAGS="$CXXFLAGS -mfloat-abi=hard" - CXXFLAGS="$CXXFLAGS -mlittle-endian" - CXXFLAGS="$CXXFLAGS -mthumb-interwork" - CXXFLAGS="$CXXFLAGS -Wno-psabi" - CXXFLAGS="$CXXFLAGS -fno-strict-aliasing" - CXXFLAGS="$CXXFLAGS -fno-short-enums" - fi - HOSTEXEPRE=lib - HOSTEXEEXT=.a - ;; dc) INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/dc' INCLUDES="$INCLUDES "'-isystem $(ronindir)/include' @@ -2781,6 +2782,25 @@ case $_backend in LDFLAGS="$LDFLAGS -shared" LDFLAGS="$LDFLAGS -fpic" ;; + tizen) + # dirent.h not available. NONSTANDARD_PORT==ensure portdefs.h is included + DEFINES="$DEFINES -DTIZEN -DDISABLE_STDIO_FILESTREAM -DNONSTANDARD_PORT" + DEFINES="$DEFINES -DNO_STDERR_STDOUT" + DEFINES="$DEFINES -DDISABLE_COMMAND_LINE" + INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/tizen' + INCLUDES="$INCLUDES "'-I$(TIZEN_ROOTSTRAP)/usr/include' + INCLUDES="$INCLUDES "'-I$(TIZEN_ROOTSTRAP)/usr/include/osp' + if test "$_debug_build" = yes; then + CXXFLAGS="$CXXFLAGS -D_DEBUG -DBUILD_DLL -O0 -g3" + fi + # created a shared library for inclusion via the eclipse build + CXXFLAGS="$CXXFLAGS -Wno-psabi" + CXXFLAGS="$CXXFLAGS --sysroot=${TIZEN_ROOTSTRAP}" + CXXFLAGS="$CXXFLAGS -fmessage-length=0" + CXXFLAGS="$CXXFLAGS -fPIC" + HOSTEXEPRE=lib + HOSTEXEEXT=.a + ;; webos) # There is no sdl-config in the WebOS PDK so we don't use find_sdlconfig here. # The PDL library acts as the WebOS device toolchain, and is required to control the virtual keyboard among other OS-level events. @@ -2846,7 +2866,7 @@ esac # Enable 16bit support only for backends which support it # case $_backend in - android | bada | dingux | dreamcast | gph | iphone | maemo | openpandora | psp | samsungtv | sdl | webos | wii) + android | dingux | dc | gph | iphone | maemo | openpandora | psp | samsungtv | sdl | webos | tizen | wii) if test "$_16bit" = auto ; then _16bit=yes else @@ -2858,6 +2878,20 @@ case $_backend in ;; esac +# +# Enable Event Recorder only for backends that support it +# +case $_backend in + sdl) + if test "$_eventrec" = auto ; then + _eventrec=yes + fi + ;; + *) + _eventrec=no + ;; +esac + # # Disable savegame timestamp support for backends which don't have a reliable real time clock # @@ -2892,7 +2926,7 @@ case $_host_os in amigaos* | cygwin* | dreamcast | ds | gamecube | mingw* | n64 | ps2 | ps3 | psp | wii | wince) _posix=no ;; - android | beos* | bsd* | darwin* | freebsd* | gph-linux | haiku* | hpux* | iphone | irix* | linux* | maemo | mint* | netbsd* | openbsd* | solaris* | sunos* | uclinux* | webos) + android | beos* | bsd* | darwin* | freebsd* | gnu* | gph-linux | haiku* | hpux* | iphone | irix*| k*bsd*-gnu* | linux* | maemo | mint* | netbsd* | openbsd* | solaris* | sunos* | uclinux* | webos) _posix=yes ;; os2-emx*) @@ -3048,7 +3082,7 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive ' ;; - linux*) + linux* | gnu* | k*bsd*-gnu*) _plugin_prefix="lib" _plugin_suffix=".so" CXXFLAGS="$CXXFLAGS -fPIC" @@ -3437,6 +3471,52 @@ fi define_in_config_if_yes "$_zlib" 'USE_ZLIB' echo "$_zlib" +# +# Check for LibMPEG2 +# +echocheck "libmpeg2 >= 0.4.0" +if test "$_mpeg2" = auto ; then + _mpeg2=no + cat > $TMPC << EOF +typedef signed $type_1_byte int8_t; +typedef signed $type_2_byte int16_t; +typedef signed $type_4_byte int32_t; + +typedef unsigned $type_1_byte uint8_t; +typedef unsigned $type_2_byte uint16_t; +typedef unsigned $type_4_byte uint32_t; + +extern "C" { +#include +} + +int main(void) { + #if MPEG2_RELEASE < MPEG2_VERSION(0, 4, 0) + #error libmpeg2 version too low + #endif + + /* mpeg2_state_t first appears in 0.4.0 */ + mpeg2_state_t state; + + return 0; +} +EOF + + if test -n "$_host"; then + # don't execute while cross compiling + cc_check $MPEG2_CFLAGS $MPEG2_LIBS -lmpeg2 && _mpeg2=yes + else + cc_check_no_clean $MPEG2_CFLAGS $MPEG2_LIBS -lmpeg2 && $TMPO$HOSTEXEEXT && _mpeg2=yes + cc_check_clean + fi +fi +if test "$_mpeg2" = yes ; then + INCLUDES="$INCLUDES $MPEG2_CFLAGS" + LIBS="$LIBS $MPEG2_LIBS -lmpeg2" +fi +define_in_config_if_yes "$_mpeg2" 'USE_MPEG2' +echo "$_mpeg2" + # # Check for Sparkle if updates support is enabled # @@ -3461,50 +3541,6 @@ define_in_config_if_yes "$_sparkle" 'USE_SPARKLE' fi echo "$_sparkle" -# -# ResidualVM specific -# Check for LibMPEG2 -# -echocheck "libmpeg2 >= 0.3.2" -if test "$_mpeg2" = auto ; then - _mpeg2=no - cat > $TMPC << EOF -typedef signed $type_1_byte int8_t; -typedef signed $type_2_byte int16_t; -typedef signed $type_4_byte int32_t; - -typedef unsigned $type_1_byte uint8_t; -typedef unsigned $type_2_byte uint16_t; -typedef unsigned $type_4_byte uint32_t; - -#include -int main(void) { - /* mpeg2_state_t first appears in 0.4.0 */ - mpeg2_state_t state; - - #ifdef MPEG2_RELEASE - if (MPEG2_RELEASE >= MPEG2_VERSION(0, 3, 2)) - return 0; - #endif - return 1; -} -EOF - - if test -n "$_host"; then - # don't execute while cross compiling - cc_check $MPEG2_CFLAGS $MPEG2_LIBS -lmpeg2 && _mpeg2=yes - else - cc_check_no_clean $MPEG2_CFLAGS $MPEG2_LIBS -lmpeg2 && $TMPO$HOSTEXEEXT && _mpeg2=yes - cc_check_clean - fi -fi -if test "$_mpeg2" = yes ; then - INCLUDES="$INCLUDES $MPEG2_CFLAGS" - LIBS="$LIBS $MPEG2_LIBS -lmpeg2" -fi -define_in_config_if_yes "$_mpeg2" 'USE_MPEG2' -echo "$_mpeg2" - # # Check for libfluidsynth # @@ -3584,8 +3620,8 @@ if test "$_libunity" = auto ; then ;; *) # Unity has a lots of dependencies, update the libs and cflags var with them - LIBUNITY_LIBS="$LIBUNITY_LIBS $(pkg-config --libs unity = 3.8.4 2>> "$TMPLOG")" - LIBUNITY_CFLAGS="$LIBUNITY_CFLAGS $(pkg-config --cflags unity = 3.8.4 2>> "$TMPLOG")" + LIBUNITY_LIBS="$LIBUNITY_LIBS `pkg-config --libs unity = 3.8.4 2>> "$TMPLOG"`" + LIBUNITY_CFLAGS="$LIBUNITY_CFLAGS `pkg-config --cflags unity = 3.8.4 2>> "$TMPLOG"`" _libunity=no cat > $TMPC << EOF #include @@ -3680,7 +3716,8 @@ if test "$_opengl" = auto ; then # Test the current header for OpenGL cat > $TMPC << EOF #include <$i> -int main(void) { return GL_VERSION_1_1; } +#include +int main(void) { printf("ANTIVIRUS FALSE POSITIVE WORKAROUND"); return GL_VERSION_1_1; } EOF cc_check $DEFINES $OPENGL_CFLAGS $OPENGL_LIBS && _opengl=yes && break @@ -3715,6 +3752,7 @@ EOF break fi done +# ResidualVM specific start -> elif test "$_opengles2" = "yes" ; then OPENGL_LIBS="$OPENGL_LIBS -lGLESv2" else @@ -3733,6 +3771,7 @@ EOF then _opengl=yes fi +# ResidualVM specific end <- fi cc_check_clean @@ -3743,12 +3782,12 @@ EOF fi case $_host_os in - bada) + tizen) # components live in non-standard locations so just assume sane SDK _opengl=yes _opengles=yes ;; - android) + android) # ResidualVM specific case: _opengles2=yes LDFLAGS="$LDFLAGS -Landroid-toolchain/sysroot/usr/lib" LDFLAGS="$LDFLAGS -lGLESv2" @@ -3757,6 +3796,7 @@ esac if test "$_opengles" = "yes" ; then echo "yes (OpenGL ES)" +# ResidualVM specific: elif test "$_opengles2" = "yes" ; then echo "yes (OpenGL ES2)" else @@ -3765,6 +3805,7 @@ fi define_in_config_if_yes "$_opengl" "USE_OPENGL" define_in_config_if_yes "$_opengles" "USE_GLES" +# ResidualVM specific: define_in_config_if_yes "$_opengles2" "USE_GLES2" @@ -3827,10 +3868,21 @@ fi define_in_config_if_yes $_nasm 'USE_NASM' # -# Enable vkeybd / keymapper +# Enable vkeybd / keymapper / event recorder # define_in_config_if_yes $_vkeybd 'ENABLE_VKEYBD' define_in_config_if_yes $_keymapper 'ENABLE_KEYMAPPER' +define_in_config_if_yes $_eventrec 'ENABLE_EVENTRECORDER' + +# +# Check if the keymapper and the event recorder are enabled simultaneously +# +if test "$_keymapper" = yes ; then + if test "$_eventrec" = yes ; then + echo "ERROR: The keymapper and the event recorder cannot be enabled simultaneously currently, please disable one of the two" + exit 1 + fi +fi # Check whether to build translation support # @@ -3889,6 +3941,7 @@ define_in_config_if_yes $_bink 'USE_BINK' echo "$_bink" # +# ResidualVM specific: # Check whether to build Safedisc decryption support # echo_n "Building SafeDisc decryption support... " @@ -4006,7 +4059,11 @@ if test "$_vkeybd" = yes ; then fi if test "$_keymapper" = yes ; then - echo ", keymapper" + echo_n ", keymapper" +fi + +if test "$_eventrec" = yes ; then + echo ", event recorder" else echo fi @@ -4041,6 +4098,7 @@ case $_backend in # all toolchain symbols in *our* libraries rather # than pick up anything unhygenic from the Android libs. LIBS="-Wl,-Bstatic $static_libs -Wl,-Bdynamic -lgcc $system_libs -llog" + # ResidualVM specific if: if test "$_opengles2" != "yes" ; then LIBS="$LIBS -lGLESv1_CM" fi @@ -4239,6 +4297,7 @@ STAGINGPATH=$_stagingpath WIN32PATH=$_win32path AOS4PATH=$_aos4path STATICLIBPATH=$_staticlibpath +# ResidualVM specific: PATH := ${_android_sysroot_path}\$(PATH) BACKEND := $_backend diff --git a/devtools/create_project/config.h b/devtools/create_project/config.h index 0864820a9e0..532c915816b 100644 --- a/devtools/create_project/config.h +++ b/devtools/create_project/config.h @@ -29,9 +29,9 @@ #define REVISION_DEFINE "SCUMMVM_INTERNAL_REVISION" #define ENABLE_LANGUAGE_EXTENSIONS "grim,myst3" // Comma separated list of projects that need language extensions -#define DISABLE_EDIT_AND_CONTINUE "grim,myst3" // Comma separated list of projects that need Edit&Continue to be disabled for co-routine support (the main project is automatically added) +#define DISABLE_EDIT_AND_CONTINUE "grim,myst3,residualvm" // Comma separated list of projects that need Edit&Continue to be disabled for co-routine support (the main project is automatically added) -#define ADDITIONAL_LIBRARY "glu32" -#define NEEDS_RTTI 0 +#define ADDITIONAL_LIBRARY "glu32" // Add a single library to the list of externally linked libraries +#define NEEDS_RTTI 0 // Enable RTTI globally #endif // TOOLS_CREATE_PROJECT_CONFIG_H diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index c2beb39b1df..1b632004a31 100644 --- a/devtools/create_project/create_project.cpp +++ b/devtools/create_project/create_project.cpp @@ -189,7 +189,7 @@ int main(int argc, char *argv[]) { msvcVersion = atoi(argv[++i]); - if (msvcVersion != 8 && msvcVersion != 9 && msvcVersion != 10 && msvcVersion != 11) { + if (msvcVersion != 8 && msvcVersion != 9 && msvcVersion != 10 && msvcVersion != 11 && msvcVersion != 12) { std::cerr << "ERROR: Unsupported version: \"" << msvcVersion << "\" passed to \"--msvc-version\"!\n"; return -1; } @@ -310,6 +310,17 @@ int main(int argc, char *argv[]) { cout << " " << i->description << '\n'; } + // Check if the keymapper and the event recorder are enabled simultaneously + bool keymapperEnabled = false; + for (FeatureList::const_iterator i = setup.features.begin(); i != setup.features.end(); ++i) { + if (i->enable && !strcmp(i->name, "keymapper")) + keymapperEnabled = true; + if (i->enable && !strcmp(i->name, "eventrecorder") && keymapperEnabled) { + std::cerr << "ERROR: The keymapper and the event recorder cannot be enabled simultaneously currently, please disable one of the two\n"; + return -1; + } + } + // Setup defines and libraries setup.defines = getEngineDefines(setup.engines); setup.libraries = getFeatureLibraries(setup.features); @@ -588,7 +599,7 @@ void displayHelp(const char *exe) { " Additionally there are the following switches for changing various settings:\n" "\n" "Project specific settings:\n" - " --codeblock build Code::Blocks project files\n" + " --codeblocks build Code::Blocks project files\n" " --msvc build Visual Studio project files\n" " --xcode build XCode project files\n" " --file-prefix prefix allow overwriting of relative file prefix in the\n" @@ -609,9 +620,9 @@ void displayHelp(const char *exe) { " (default: false)\n" " --installer Create NSIS installer after the build (implies --build-events)\n" " (default: false)\n" - " --tools Create project files for the devtools\n" - " (ignores --build-events and --installer, as well as engine settings)\n" - " (default: false)\n" + " --tools Create project files for the devtools\n" + " (ignores --build-events and --installer, as well as engine settings)\n" + " (default: false)\n" "\n" "Engines settings:\n" " --list-engines list all available engines and their default state\n" @@ -810,18 +821,19 @@ const Feature s_features[] = { { "mpeg2", "USE_MPEG2", "libmpeg2", true, "mpeg2 codec for cutscenes" }, // Feature flags - { "bink", "USE_BINK", "", true, "Bink video support" }, - { "scalers", "USE_SCALERS", "", true, "Scalers" }, - { "hqscalers", "USE_HQ_SCALERS", "", true, "HQ scalers" }, - { "16bit", "USE_RGB_COLOR", "", true, "16bit color support" }, - { "mt32emu", "USE_MT32EMU", "", true, "integrated MT-32 emulator" }, - { "nasm", "USE_NASM", "", true, "IA-32 assembly support" }, // This feature is special in the regard, that it needs additional handling. - { "opengl", "USE_OPENGL", "opengl32", true, "OpenGL support" }, - { "taskbar", "USE_TASKBAR", "", true, "Taskbar integration support" }, - { "translation", "USE_TRANSLATION", "", false, "Translation support" }, - { "vkeybd", "ENABLE_VKEYBD", "", false, "Virtual keyboard support"}, - { "keymapper","ENABLE_KEYMAPPER", "", false, "Keymapper support"}, - { "langdetect", "USE_DETECTLANG", "", true, "System language detection support" } // This feature actually depends on "translation", there + { "bink", "USE_BINK", "", true, "Bink video support" }, + { "scalers", "USE_SCALERS", "", true, "Scalers" }, + { "hqscalers", "USE_HQ_SCALERS", "", true, "HQ scalers" }, + { "16bit", "USE_RGB_COLOR", "", true, "16bit color support" }, + { "mt32emu", "USE_MT32EMU", "", true, "integrated MT-32 emulator" }, + { "nasm", "USE_NASM", "", true, "IA-32 assembly support" }, // This feature is special in the regard, t + { "opengl", "USE_OPENGL", "opengl32", true, "OpenGL support" }, + { "taskbar", "USE_TASKBAR", "", true, "Taskbar integration support" }, + { "translation", "USE_TRANSLATION", "", false, "Translation support" }, + { "vkeybd", "ENABLE_VKEYBD", "", false, "Virtual keyboard support"}, + { "keymapper", "ENABLE_KEYMAPPER", "", false, "Keymapper support"}, + { "eventrecorder", "ENABLE_EVENTRECORDER", "", false, "Event recorder support"}, + { "langdetect", "USE_DETECTLANG", "", true, "System language detection support" } // This feature actually depends // is just no current way of properly detecting this... }; @@ -1192,6 +1204,7 @@ void ProjectProvider::createProject(const BuildSetup &setup) { in.push_back(setup.srcDir + "/COPYING.BSD"); in.push_back(setup.srcDir + "/COPYING.FREEFONT"); in.push_back(setup.srcDir + "/COPYRIGHT"); + //ResidualVM specific: in.push_back(setup.srcDir + "/KNOWN_BUGS"); in.push_back(setup.srcDir + "/NEWS"); in.push_back(setup.srcDir + "/README"); diff --git a/devtools/create_project/msbuild.cpp b/devtools/create_project/msbuild.cpp index 0f77d91852d..6af9323fcd2 100644 --- a/devtools/create_project/msbuild.cpp +++ b/devtools/create_project/msbuild.cpp @@ -52,6 +52,9 @@ int MSBuildProvider::getVisualStudioVersion() { if (_version == 11) return 2012; + if (_version == 12) + return 2013; + error("Unsupported version passed to getVisualStudioVersion"); } @@ -88,7 +91,7 @@ void MSBuildProvider::createProjectFile(const std::string &name, const std::stri error("Could not open \"" + projectFile + "\" for writing"); project << "\n" - "\n" + "= 12 ? _version : 4) << ".0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n" "\t\n"; outputConfiguration(project, "Debug", "Win32"); @@ -105,7 +108,7 @@ void MSBuildProvider::createProjectFile(const std::string &name, const std::stri "\t\t{" << uuid << "}\n" "\t\t" << name << "\n" "\t\tWin32Proj\n" - "\t\t$(VCTargetsPath11)\n" + "\t\t$(VCTargetsPath" << _version << ")\n" "\t\n"; // Shared configuration @@ -184,7 +187,7 @@ void MSBuildProvider::createFiltersFile(const BuildSetup &setup, const std::stri error("Could not open \"" + filtersFile + "\" for writing"); filters << "\n" - "\n"; + "= 12 ? _version : 4) << ".0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n"; // Output the list of filters filters << "\t\n"; @@ -314,9 +317,8 @@ void MSBuildProvider::outputGlobalPropFile(const BuildSetup &setup, std::ofstrea definesList += REVISION_DEFINE ";"; properties << "\n" - "\n" + "= 12 ? _version : 4) << ".0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n" "\t\n" - "\t\t<_ProjectFileVersion>10.0.40219.1\n" "\t\t<_PropertySheetDisplayName>" << setup.projectDescription << "_Global\n" "\t\t$(" << LIBS_DEFINE << ")\\bin;$(ExecutablePath)\n" "\t\t$(" << LIBS_DEFINE << ")\\lib\\" << (bits == 32 ? "x86" : "x64") << ";$(LibraryPath)\n" @@ -368,12 +370,11 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b error("Could not open \"" + setup.outputDir + '/' + setup.projectDescription + "_" + outputType + (isWin32 ? "" : "64") + getPropertiesExtension() + "\" for writing"); properties << "\n" - "\n" + "= 12 ? _version : 4) << ".0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n" "\t\n" "\t\t\n" "\t\n" "\t\n" - "\t\t<_ProjectFileVersion>10.0.40219.1\n" "\t\t<_PropertySheetDisplayName>" << setup.projectDescription << "_" << outputType << outputBitness << "\n" "\t\t" << (isRelease ? "false" : "true") << "\n" "\t\n" @@ -383,7 +384,7 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b if (isRelease) { properties << "\t\t\ttrue\n" "\t\t\ttrue\n" - "\t\t\tWIN32;RELEASE_BUILD;%(PreprocessorDefinitions)\n" + "\t\t\tWIN32;DISABLE_GUI_BUILTIN_THEME;RELEASE_BUILD;%(PreprocessorDefinitions)\n" "\t\t\ttrue\n" "\t\t\tfalse\n" "\t\t\t\n" @@ -395,7 +396,7 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b "\t\t\ttrue\n"; } else { properties << "\t\t\tDisabled\n" - "\t\t\tWIN32;%(PreprocessorDefinitions)\n" + "\t\t\tWIN32;DISABLE_GUI_BUILTIN_THEME;%(PreprocessorDefinitions)\n" "\t\t\ttrue\n" "\t\t\tEnableFastChecks\n" "\t\t\tMultiThreadedDebug\n" diff --git a/devtools/create_project/msvc.cpp b/devtools/create_project/msvc.cpp index f995c108c77..b8d2401af91 100644 --- a/devtools/create_project/msvc.cpp +++ b/devtools/create_project/msvc.cpp @@ -171,7 +171,7 @@ std::string MSVCProvider::getPostBuildEvent(bool isWin32, bool createInstaller) cmdLine += (isWin32) ? "x86" : "x64"; - cmdLine += " %SCUMMVM_LIBS% "; + cmdLine += " %" LIBS_DEFINE "% "; // Specify if installer needs to be built or not cmdLine += (createInstaller ? "1" : "0"); diff --git a/devtools/create_project/scripts/postbuild.cmd b/devtools/create_project/scripts/postbuild.cmd index 7c08f6c622c..88f571ae1dc 100644 --- a/devtools/create_project/scripts/postbuild.cmd +++ b/devtools/create_project/scripts/postbuild.cmd @@ -24,7 +24,10 @@ echo Copying data files echo. xcopy /F /Y "%~4/lib/%~3/SDL.dll" "%~2" 1>NUL 2>&1 +xcopy /F /Y "%~4/lib/%~3/freetype6.dll" "%~2" 1>NUL 2>&1 xcopy /F /Y "%~1/backends/vkeybd/packs/vkeybd_default.zip" "%~2" 1>NUL 2>&1 +REM ResidualVM not support it: +REM xcopy /F /Y "%~1/gui/themes/translations.dat" "%~2" 1>NUL 2>&1 if "%~5"=="0" goto done diff --git a/devtools/credits.pl b/devtools/credits.pl index a2639a2141c..bc588f5d23e 100755 --- a/devtools/credits.pl +++ b/devtools/credits.pl @@ -48,7 +48,7 @@ if ($mode eq "") { $Text::Wrap::unexpand = 0; if ($mode eq "TEXT") { $Text::Wrap::columns = 78; - $max_name_width = 21; # The maximal width of a name. + $max_name_width = 23; # The maximal width of a name. } elsif ($mode eq "CPP") { $Text::Wrap::columns = 48; # Approx. } @@ -65,6 +65,7 @@ sub html_entities_to_ascii { # ø -> o # ö -> o / oe # ä -> a + # ë -> e # ü -> ue # å -> aa # & -> & @@ -82,6 +83,7 @@ sub html_entities_to_ascii { $text =~ s/å/aa/g; $text =~ s/ä/a/g; + $text =~ s/ë/e/g; $text =~ s/ü/ue/g; # HACK: Torbj*o*rn but G*oe*ffringmann and R*oe*ver and J*oe*rg $text =~ s/Torbjörn/Torbjorn/g; @@ -108,6 +110,7 @@ sub html_entities_to_cpp { $text =~ s/å/\\345/g; $text =~ s/ä/\\344/g; + $text =~ s/ë/\\353/g; $text =~ s/ö/\\366/g; $text =~ s/ü/\\374/g; @@ -133,6 +136,7 @@ sub html_entities_to_rtf { # Back to hex numbers $text =~ s/ä/\\'8a/g; + $text =~ s/ë/\\'eb/g; $text =~ s/ö/\\'9a/g; $text =~ s/ü/\\'9f/g; @@ -156,6 +160,7 @@ sub html_entities_to_tex { $text =~ s/ä/\\"a/g; $text =~ s/ö/\\"o/g; + $text =~ s/ë/\\"e/g; $text =~ s/ü/\\"u/g; $text =~ s/&/\\&/g; @@ -267,14 +272,22 @@ sub begin_section { print '\f1\b0\fs24 \cf0 \\' . "\n"; } elsif ($mode eq "CPP") { if ($section_level eq 0) { - # TODO: Would be nice to have a 'fat' or 'large' mode for - # headlines... - $title = html_entities_to_cpp($title); - print '"C1""'.$title.'",' . "\n"; - print '"",' . "\n"; + # TODO: Would be nice to have a 'fat' or 'large' mode for + # headlines... + my $ascii_title = html_entities_to_ascii($title); + $title = html_entities_to_cpp($title); + if ($ascii_title ne $title) { + print '"A1""'.$ascii_title.'",' . "\n"; + } + print '"C1""'.$title.'",' . "\n"; + print '"",' . "\n"; } else { - $title = html_entities_to_cpp($title); - print '"C1""'.$title.'",' . "\n"; + my $ascii_title = html_entities_to_ascii($title); + $title = html_entities_to_cpp($title); + if ($ascii_title ne $title) { + print '"A1""'.$ascii_title.'",' . "\n"; + } + print '"C1""'.$title.'",' . "\n"; } } elsif ($mode eq "XML-DOC") { print " "; @@ -359,6 +372,9 @@ sub add_person { my $min_name_width = length $desc > 0 ? $max_name_width : 0; $name = $nick if $name eq ""; $name = html_entities_to_ascii($name); + if (length $name > $max_name_width) { + print STDERR "Warning: max_name_width is too small (" . $max_name_width . " < " . (length $name) . " for \"" . $name. "\")\n"; + } $desc = html_entities_to_ascii($desc); $tab = " " x ($section_level * 2 + 1); @@ -389,13 +405,21 @@ sub add_person { } } elsif ($mode eq "CPP") { $name = $nick if $name eq ""; + my $ascii_name = html_entities_to_ascii($name); $name = html_entities_to_cpp($name); + if ($ascii_name ne $name) { + print '"A0""'.$ascii_name.'",' . "\n"; + } print '"C0""'.$name.'",' . "\n"; # Print desc wrapped if (length $desc > 0) { + my $ascii_desc = html_entities_to_ascii($desc); $desc = html_entities_to_cpp($desc); + if ($ascii_desc ne $desc) { + print '"A2""'.$ascii_desc.'",' . "\n"; + } print '"C2""'.$desc.'",' . "\n"; } } elsif ($mode eq "XML-DOC") { diff --git a/devtools/module.mk b/devtools/module.mk index d9390e70e04..e5c29cf1649 100644 --- a/devtools/module.mk +++ b/devtools/module.mk @@ -41,8 +41,24 @@ credits: $(srcdir)/devtools/credits.pl --text > $(srcdir)/AUTHORS # $(srcdir)/devtools/credits.pl --rtf > $(srcdir)/Credits.rtf $(srcdir)/devtools/credits.pl --cpp > $(srcdir)/gui/credits.h - $(srcdir)/devtools/credits.pl --xml-website > $(srcdir)/../../web/trunk/data/credits.xml + $(srcdir)/devtools/credits.pl --xml-website > $(srcdir)/../../residualvm-web/data/credits.xml # $(srcdir)/devtools/credits.pl --xml-docbook > $(srcdir)/../../docs/trunk/docbook/credits.xml -.PHONY: clean-devtools devtools credits +# +# Rules which automatically and implicitly rebuild the credits and +# MD5 tables when needed. +# These are currently disabled, because if the input data changes, then +# the generated files should be checked in, too. Otherwise, we'd reduce +# portability to system on which our devtools can't (automatically) be +# run for some reason. +# + +#scumm/scumm-md5.h: $(srcdir)/devtools/scumm-md5.txt devtools/md5table$(EXEEXT) +# devtools/md5table$(EXEEXT) --c++ < $< > $@ + +#AUTHORS: $(srcdir)/devtools/credits.pl +# $(srcdir)/devtools/credits.pl --text > $@ + +#gui/credits.h: $(srcdir)/devtools/credits.pl +# $(srcdir)/devtools/credits.pl --cpp > $@.PHONY: clean-devtools devtools credits diff --git a/dists/msvc12/readme.txt b/dists/msvc12/readme.txt new file mode 100644 index 00000000000..760f9ff6010 --- /dev/null +++ b/dists/msvc12/readme.txt @@ -0,0 +1,6 @@ +The Visual Studio project files can now be created automatically from the GCC +files using the create_project tool inside the /devtools/create_project folder. + +To create the default project files, build create_project.exe, copy it inside +this folder and run the create_msvc12.bat file for a default build. You can run +create_project.exe with no parameters to check the possible command-line options diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp index b70075e3ccc..ce5b1d94873 100644 --- a/engines/advancedDetector.cpp +++ b/engines/advancedDetector.cpp @@ -29,7 +29,7 @@ #include "common/system.h" #include "common/textconsole.h" #include "common/translation.h" - +#include "gui/EventRecorder.h" #include "engines/advancedDetector.h" #include "engines/obsolete.h" @@ -78,7 +78,7 @@ static Common::String generatePreferredTarget(const Common::String &id, const AD res = res + "-cd"; } - if (desc->platform != Common::kPlatformPC && desc->platform != Common::kPlatformUnknown) { + if (desc->platform != Common::kPlatformDOS && desc->platform != Common::kPlatformUnknown) { res = res + "-" + getPlatformAbbrev(desc->platform); } @@ -301,6 +301,7 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) return Common::kUserCanceled; debug(2, "Running %s", gameDescriptor.description().c_str()); + initSubSystems(agdDesc); if (!createInstance(syst, engine, agdDesc)) return Common::kNoGameDataFoundError; else @@ -606,3 +607,9 @@ AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, con _maxScanDepth = 1; _directoryGlobs = NULL; } + +void AdvancedMetaEngine::initSubSystems(const ADGameDescription *gameDesc) const { + if (gameDesc) { + g_eventRec.processGameDescription(gameDesc); + } +} diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index 3eec33abe58..71d2c4a446c 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -280,6 +280,9 @@ protected: return 0; } +private: + void initSubSystems(const ADGameDescription *gameDesc) const; + protected: /** * Detect games in specified directory. diff --git a/engines/engine.cpp b/engines/engine.cpp index 5b02f5d57f9..d877cb21705 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -179,7 +179,12 @@ void initCommonGFX(bool defaultTo1XScaler) { } else { // Override global scaler with any game-specific define if (ConfMan.hasKey("gfx_mode")) { - g_system->setGraphicsMode(ConfMan.get("gfx_mode").c_str()); + Common::String gfxMode = ConfMan.get("gfx_mode"); + g_system->setGraphicsMode(gfxMode.c_str()); + + // HACK: For OpenGL modes, we will still honor the graphics scale override + if (defaultTo1XScaler && (gfxMode.equalsIgnoreCase("gl1") || gfxMode.equalsIgnoreCase("gl2") || gfxMode.equalsIgnoreCase("gl4"))) + g_system->resetGraphicsScale(); } } diff --git a/engines/grim/lua/lstrlib.cpp b/engines/grim/lua/lstrlib.cpp index b1db3af1f75..ecf42d294fa 100644 --- a/engines/grim/lua/lstrlib.cpp +++ b/engines/grim/lua/lstrlib.cpp @@ -3,6 +3,8 @@ ** See Copyright Notice in lua.h */ +#define FORBIDDEN_SYMBOL_EXCEPTION_iscntrl +#define FORBIDDEN_SYMBOL_EXCEPTION_ispunct #include "common/util.h" diff --git a/graphics/cursorman.cpp b/graphics/cursorman.cpp index c8181016456..6825767dfdc 100644 --- a/graphics/cursorman.cpp +++ b/graphics/cursorman.cpp @@ -48,6 +48,9 @@ bool CursorManager::isVisible() { bool CursorManager::showMouse(bool visible) { if (_cursorStack.empty()) return false; + if (_locked) { + return false; + } _cursorStack.top()->_visible = visible; @@ -225,6 +228,10 @@ void CursorManager::replaceCursorPalette(const byte *colors, uint start, uint nu } } +void CursorManager::lock(bool locked) { + _locked = locked; +} + CursorManager::Cursor::Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) { #ifdef USE_RGB_COLOR if (!format) diff --git a/graphics/cursorman.h b/graphics/cursorman.h index 66e8d1ba56c..b4d8ad94ce5 100644 --- a/graphics/cursorman.h +++ b/graphics/cursorman.h @@ -160,12 +160,15 @@ public: */ void replaceCursorPalette(const byte *colors, uint start, uint num); + void lock(bool locked); private: friend class Common::Singleton; // Even though this is basically the default constructor we implement it // ourselves, so it is private and thus there is no way to create this class // except from the Singleton code. - CursorManager() {} + CursorManager() { + _locked = false; + } ~CursorManager(); struct Cursor { @@ -198,6 +201,7 @@ private: }; Common::Stack _cursorStack; Common::Stack _cursorPaletteStack; + bool _locked; }; } // End of namespace Graphics diff --git a/graphics/decoders/jpeg.cpp b/graphics/decoders/jpeg.cpp index 08bc1f7a3d1..75fdcd6e5ac 100644 --- a/graphics/decoders/jpeg.cpp +++ b/graphics/decoders/jpeg.cpp @@ -74,7 +74,7 @@ const Surface *JPEGDecoder::getSurface() const { // Create an RGBA8888 surface _rgbSurface = new Graphics::Surface(); - _rgbSurface->create(_w, _h, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); + _rgbSurface->create(_w, _h, Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0)); // Get our component surfaces const Graphics::Surface *yComponent = getComponent(1); @@ -215,28 +215,34 @@ bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) { bool JPEGDecoder::readJFIF() { uint16 length = _stream->readUint16BE(); uint32 tag = _stream->readUint32BE(); + if (tag != MKTAG('J', 'F', 'I', 'F')) { warning("JPEGDecoder::readJFIF() tag mismatch"); return false; } + if (_stream->readByte() != 0) { // NULL warning("JPEGDecoder::readJFIF() NULL mismatch"); return false; } + byte majorVersion = _stream->readByte(); byte minorVersion = _stream->readByte(); - if (majorVersion != 1 || minorVersion != 1) - warning("JPEGDecoder::readJFIF() Non-v1.1 JPEGs may not be handled correctly"); + if (majorVersion != 1 || minorVersion > 2) + warning("JPEGDecoder::readJFIF(): v%d.%02d JPEGs may not be handled correctly", majorVersion, minorVersion); + /* byte densityUnits = */_stream->readByte(); /* uint16 xDensity = */_stream->readUint16BE(); /* uint16 yDensity = */_stream->readUint16BE(); byte thumbW = _stream->readByte(); byte thumbH = _stream->readByte(); + _stream->seek(thumbW * thumbH * 3, SEEK_CUR); // Ignore thumbnail if (length != (thumbW * thumbH * 3) + 16) { warning("JPEGDecoder::readJFIF() length mismatch"); return false; } + return true; } @@ -481,8 +487,8 @@ bool JPEGDecoder::readDQT() { // Validate the table id tableId &= 0xF; - if (tableId > JPEG_MAX_QUANT_TABLES) { - warning("JPEG: Invalid number of components"); + if (tableId >= JPEG_MAX_QUANT_TABLES) { + warning("JPEG: Invalid quantization table"); return false; } diff --git a/graphics/primitives.cpp b/graphics/primitives.cpp index b88db39f36c..c140dc86440 100644 --- a/graphics/primitives.cpp +++ b/graphics/primitives.cpp @@ -78,4 +78,4 @@ void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color drawLine(x0 + x, y0 + y, x1 + x, y1 + y, color, plotProc, data); } -} // End of namespace Graphics +} // End of namespace Graphics diff --git a/graphics/primitives.h b/graphics/primitives.h index f0780afc2e3..f4a92683abd 100644 --- a/graphics/primitives.h +++ b/graphics/primitives.h @@ -27,6 +27,6 @@ namespace Graphics { void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, int, int, void *), void *data); void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data); -} // End of namespace Graphics +} // End of namespace Graphics #endif diff --git a/graphics/scaler.h b/graphics/scaler.h index 242d2924346..0abe6a3cc1a 100644 --- a/graphics/scaler.h +++ b/graphics/scaler.h @@ -90,4 +90,10 @@ inline bool createThumbnailFromScreen(Graphics::Surface *surf) { return true; } */ extern bool createThumbnail(Graphics::Surface *surf, const uint8 *pixels, int w, int h, const uint8 *palette); +/** + * Downscale screenshot to thumbnale size. + * + */ +extern bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in); + #endif diff --git a/graphics/thumbnail.cpp b/graphics/thumbnail.cpp index db61d828d2c..d04c2186245 100644 --- a/graphics/thumbnail.cpp +++ b/graphics/thumbnail.cpp @@ -23,6 +23,7 @@ #include "graphics/scaler.h" #include "graphics/colormasks.h" #include "common/endian.h" +#include "common/algorithm.h" #include "common/system.h" #include "common/stream.h" #include "common/textconsole.h" @@ -30,17 +31,17 @@ namespace Graphics { namespace { -#define THMB_VERSION 1 +#define THMB_VERSION 2 struct ThumbnailHeader { uint32 type; uint32 size; byte version; uint16 width, height; - byte bpp; + PixelFormat format; }; -#define ThumbnailHeaderSize (4+4+1+2+2+1) +#define ThumbnailHeaderSize (4+4+1+2+2+(1+4+4)) bool loadHeader(Common::SeekableReadStream &in, ThumbnailHeader &header, bool outputWarnings) { header.type = in.readUint32BE(); @@ -64,7 +65,22 @@ bool loadHeader(Common::SeekableReadStream &in, ThumbnailHeader &header, bool ou header.width = in.readUint16BE(); header.height = in.readUint16BE(); - header.bpp = in.readByte(); + header.format.bytesPerPixel = in.readByte(); + // Starting from version 2 on we serialize the whole PixelFormat. + if (header.version >= 2) { + header.format.rLoss = in.readByte(); + header.format.gLoss = in.readByte(); + header.format.bLoss = in.readByte(); + header.format.aLoss = in.readByte(); + + header.format.rShift = in.readByte(); + header.format.gShift = in.readByte(); + header.format.bShift = in.readByte(); + header.format.aShift = in.readByte(); + } else { + // Version 1 used a hardcoded RGB565. + header.format = createPixelFormat<565>(); + } return true; } @@ -100,26 +116,34 @@ Graphics::Surface *loadThumbnail(Common::SeekableReadStream &in) { if (!loadHeader(in, header, true)) return 0; - if (header.bpp != 2) { - warning("trying to load thumbnail with unsupported bit depth %d", header.bpp); + if (header.format.bytesPerPixel != 2 && header.format.bytesPerPixel != 4) { + warning("trying to load thumbnail with unsupported bit depth %d", header.format.bytesPerPixel); return 0; } - Graphics::PixelFormat format = g_system->getOverlayFormat(); Graphics::Surface *const to = new Graphics::Surface(); - to->create(header.width, header.height, format); + to->create(header.width, header.height, header.format); - OverlayColor *pixels = (OverlayColor *)to->pixels; for (int y = 0; y < to->h; ++y) { - for (int x = 0; x < to->w; ++x) { - uint8 r, g, b; - colorToRGB >(in.readUint16BE(), r, g, b); + switch (header.format.bytesPerPixel) { + case 2: { + uint16 *pixels = (uint16 *)to->getBasePtr(0, y); + for (uint x = 0; x < to->w; ++x) { + *pixels++ = in.readUint16BE(); + } + } break; - // converting to current OSystem Color - *pixels++ = format.RGBToColor(r, g, b); + case 4: { + uint32 *pixels = (uint32 *)to->getBasePtr(0, y); + for (uint x = 0; x < to->w; ++x) { + *pixels++ = in.readUint32BE(); + } + } break; + + default: + assert(0); } } - return to; } @@ -138,8 +162,8 @@ bool saveThumbnail(Common::WriteStream &out) { } bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb) { - if (thumb.format.bytesPerPixel != 2) { - warning("trying to save thumbnail with bpp different than 2"); + if (thumb.format.bytesPerPixel != 2 && thumb.format.bytesPerPixel != 4) { + warning("trying to save thumbnail with bpp %u", thumb.format.bytesPerPixel); return false; } @@ -149,21 +173,98 @@ bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb) { header.version = THMB_VERSION; header.width = thumb.w; header.height = thumb.h; - header.bpp = thumb.format.bytesPerPixel; out.writeUint32BE(header.type); out.writeUint32BE(header.size); out.writeByte(header.version); out.writeUint16BE(header.width); out.writeUint16BE(header.height); - out.writeByte(header.bpp); - // TODO: for later this shouldn't be casted to uint16... - uint16 *pixels = (uint16 *)thumb.pixels; - for (uint16 p = 0; p < thumb.w*thumb.h; ++p, ++pixels) - out.writeUint16BE(*pixels); + // Serialize the PixelFormat + out.writeByte(thumb.format.bytesPerPixel); + out.writeByte(thumb.format.rLoss); + out.writeByte(thumb.format.gLoss); + out.writeByte(thumb.format.bLoss); + out.writeByte(thumb.format.aLoss); + out.writeByte(thumb.format.rShift); + out.writeByte(thumb.format.gShift); + out.writeByte(thumb.format.bShift); + out.writeByte(thumb.format.aShift); + + // Serialize the pixel data + for (uint y = 0; y < thumb.h; ++y) { + switch (thumb.format.bytesPerPixel) { + case 2: { + const uint16 *pixels = (const uint16 *)thumb.getBasePtr(0, y); + for (uint x = 0; x < thumb.w; ++x) { + out.writeUint16BE(*pixels++); + } + } break; + + case 4: { + const uint32 *pixels = (const uint32 *)thumb.getBasePtr(0, y); + for (uint x = 0; x < thumb.w; ++x) { + out.writeUint32BE(*pixels++); + } + } break; + + default: + assert(0); + } + } return true; } + +/** + * Returns an array indicating which pixels of a source image horizontally or vertically get + * included in a scaled image + */ +int *scaleLine(int size, int srcSize) { + int scale = 100 * size / srcSize; + assert(scale > 0); + int *v = new int[size]; + Common::fill(v, &v[size], 0); + + int distCtr = 0; + int *destP = v; + for (int distIndex = 0; distIndex < srcSize; ++distIndex) { + distCtr += scale; + while (distCtr >= 100) { + assert(destP < &v[size]); + *destP++ = distIndex; + distCtr -= 100; + } + } + + return v; +} + +Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize) { + Graphics::Surface *s = new Graphics::Surface(); + s->create(xSize, ySize, srcImage.format); + + int *horizUsage = scaleLine(xSize, srcImage.w); + int *vertUsage = scaleLine(ySize, srcImage.h); + + // Loop to create scaled version + for (int yp = 0; yp < ySize; ++yp) { + const byte *srcP = (const byte *)srcImage.getBasePtr(0, vertUsage[yp]); + byte *destP = (byte *)s->getBasePtr(0, yp); + + for (int xp = 0; xp < xSize; ++xp) { + const byte *tempSrcP = srcP + (horizUsage[xp] * srcImage.format.bytesPerPixel); + for (int byteCtr = 0; byteCtr < srcImage.format.bytesPerPixel; ++byteCtr) { + *destP++ = *tempSrcP++; + } + } + } + + // Delete arrays and return surface + delete[] horizUsage; + delete[] vertUsage; + return s; +} + } // End of namespace Graphics diff --git a/graphics/thumbnail.h b/graphics/thumbnail.h index df99568f421..c857809c916 100644 --- a/graphics/thumbnail.h +++ b/graphics/thumbnail.h @@ -50,8 +50,6 @@ bool skipThumbnail(Common::SeekableReadStream &in); /** * Loads a thumbnail from the given input stream. - * The loaded thumbnail will be automatically converted to the - * current overlay pixelformat. */ Graphics::Surface *loadThumbnail(Common::SeekableReadStream &in); @@ -66,6 +64,24 @@ bool saveThumbnail(Common::WriteStream &out); */ bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb); +/** + * Grabs framebuffer into surface + * + * @param surf a surface + * @return false if a error occurred + */ +bool createScreenShot(Graphics::Surface &surf); + +/** + * Scales a passed surface, creating a new surface with the result + * @param srcImage Source image to scale + * @param xSize New surface width + * @param ySize New surface height + * @remarks Caller is responsible for freeing the returned surface + */ +Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize); + + } // End of namespace Graphics #endif diff --git a/graphics/yuv_to_rgb.h b/graphics/yuv_to_rgb.h index f785422c5ac..a1e61ec7057 100644 --- a/graphics/yuv_to_rgb.h +++ b/graphics/yuv_to_rgb.h @@ -22,10 +22,17 @@ /** * @file - * YUV to RGB conversion used in engines: - * - mohawk - * - scumm (he) - * - sword25 + * YUV to RGB conversion. + * + * Used in graphics: + * - JPEGDecoder + * + * Used in video: + * - BinkDecoder + * - Indeo3Decoder + * - PSXStreamDecoder + * - TheoraDecoder + * - SVQ1Decoder */ #ifndef GRAPHICS_YUV_TO_RGB_H diff --git a/gui/EventRecorder.cpp b/gui/EventRecorder.cpp new file mode 100644 index 00000000000..47358a0b3d2 --- /dev/null +++ b/gui/EventRecorder.cpp @@ -0,0 +1,665 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include "gui/EventRecorder.h" + +namespace Common { +DECLARE_SINGLETON(GUI::EventRecorder); +} + +#ifdef ENABLE_EVENTRECORDER + +#include "common/debug-channels.h" +#include "backends/timer/sdl/sdl-timer.h" +#include "backends/mixer/sdl/sdl-mixer.h" +#include "common/config-manager.h" +#include "common/md5.h" +#include "gui/gui-manager.h" +#include "gui/widget.h" +#include "gui/onscreendialog.h" +#include "common/random.h" +#include "common/savefile.h" +#include "common/textconsole.h" +#include "graphics/thumbnail.h" +#include "graphics/surface.h" +#include "graphics/scaler.h" + +namespace GUI { + + +const int kMaxRecordsNames = 0x64; +const int kDefaultScreenshotPeriod = 60000; +const int kDefaultBPP = 2; + +uint32 readTime(Common::ReadStream *inFile) { + uint32 d = inFile->readByte(); + if (d == 0xff) { + d = inFile->readUint32LE(); + } + + return d; +} + +void writeTime(Common::WriteStream *outFile, uint32 d) { + //Simple RLE compression + if (d >= 0xff) { + outFile->writeByte(0xff); + outFile->writeUint32LE(d); + } else { + outFile->writeByte(d); + } +} + +EventRecorder::EventRecorder() { + _timerManager = NULL; + _recordMode = kPassthrough; + _fakeMixerManager = NULL; + _initialized = false; + _needRedraw = false; + _fastPlayback = false; + DebugMan.addDebugChannel(kDebugLevelEventRec, "EventRec", "Event recorder debug level"); +} + +EventRecorder::~EventRecorder() { + if (_timerManager != NULL) { + delete _timerManager; + } +} + +void EventRecorder::deinit() { + if (!_initialized) { + return; + } + setFileHeader(); + _needRedraw = false; + _initialized = false; + _recordMode = kPassthrough; + delete _fakeMixerManager; + _fakeMixerManager = NULL; + controlPanel->close(); + delete controlPanel; + debugC(1, kDebugLevelEventRec, "playback:action=stopplayback"); + g_system->getEventManager()->getEventDispatcher()->unregisterSource(this); + _recordMode = kPassthrough; + _playbackFile->close(); + delete _playbackFile; + switchMixer(); + switchTimerManagers(); + DebugMan.disableDebugChannel("EventRec"); +} + +void EventRecorder::processMillis(uint32 &millis, bool skipRecord) { + if (!_initialized) { + return; + } + if (skipRecord) { + millis = _fakeTimer; + return; + } + if (_recordMode == kRecorderPlaybackPause) { + millis = _fakeTimer; + } + uint32 millisDelay; + Common::RecorderEvent timerEvent; + switch (_recordMode) { + case kRecorderRecord: + updateSubsystems(); + millisDelay = millis - _lastMillis; + _lastMillis = millis; + _fakeTimer += millisDelay; + controlPanel->setReplayedTime(_fakeTimer); + timerEvent.recordedtype = Common::kRecorderEventTypeTimer; + timerEvent.time = _fakeTimer; + _playbackFile->writeEvent(timerEvent); + takeScreenshot(); + _timerManager->handler(); + break; + case kRecorderPlayback: + updateSubsystems(); + if (_nextEvent.recordedtype == Common::kRecorderEventTypeTimer) { + _fakeTimer = _nextEvent.time; + _nextEvent = _playbackFile->getNextEvent(); + _timerManager->handler(); + } else { + if (_nextEvent.type == Common::EVENT_RTL) { + error("playback:action=stopplayback"); + } else { + uint32 seconds = _fakeTimer / 1000; + Common::String screenTime = Common::String::format("%.2d:%.2d:%.2d", seconds / 3600 % 24, seconds / 60 % 60, seconds % 60); + error("playback:action=error reason=\"synchronization error\" time = %s", screenTime.c_str()); + } + } + millis = _fakeTimer; + controlPanel->setReplayedTime(_fakeTimer); + break; + case kRecorderPlaybackPause: + millis = _fakeTimer; + break; + default: + break; + } +} + +bool EventRecorder::processDelayMillis() { + return _fastPlayback; +} + +void EventRecorder::checkForKeyCode(const Common::Event &event) { + if ((event.type == Common::EVENT_KEYDOWN) && (event.kbd.flags & Common::KBD_CTRL) && (event.kbd.keycode == Common::KEYCODE_p) && (!event.synthetic)) { + togglePause(); + } +} + +bool EventRecorder::pollEvent(Common::Event &ev) { + if ((_recordMode != kRecorderPlayback) || !_initialized) + return false; + + if ((_nextEvent.recordedtype == Common::kRecorderEventTypeTimer) || (_nextEvent.type == Common::EVENT_INVALID)) { + return false; + } + + switch (_nextEvent.type) { + case Common::EVENT_MOUSEMOVE: + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONDOWN: + case Common::EVENT_RBUTTONUP: + case Common::EVENT_WHEELUP: + case Common::EVENT_WHEELDOWN: + g_system->warpMouse(_nextEvent.mouse.x, _nextEvent.mouse.y); + break; + default: + break; + } + ev = _nextEvent; + _nextEvent = _playbackFile->getNextEvent(); + return true; +} + +void EventRecorder::switchFastMode() { + if (_recordMode == kRecorderPlaybackPause) { + _fastPlayback = !_fastPlayback; + } +} + +void EventRecorder::togglePause() { + RecordMode oldState; + switch (_recordMode) { + case kRecorderPlayback: + case kRecorderRecord: + oldState = _recordMode; + _recordMode = kRecorderPlaybackPause; + controlPanel->runModal(); + _recordMode = oldState; + _initialized = true; + break; + case kRecorderPlaybackPause: + controlPanel->close(); + break; + default: + break; + } +} + +void EventRecorder::RegisterEventSource() { + g_system->getEventManager()->getEventDispatcher()->registerMapper(this, false); +} + +uint32 EventRecorder::getRandomSeed(const Common::String &name) { + uint32 result = g_system->getMillis(); + if (_recordMode == kRecorderRecord) { + _playbackFile->getHeader().randomSourceRecords[name] = result; + } else if (_recordMode == kRecorderPlayback) { + result = _playbackFile->getHeader().randomSourceRecords[name]; + } + return result; +} + +Common::String EventRecorder::generateRecordFileName(const Common::String &target) { + Common::String pattern(target+".r??"); + Common::StringArray files = g_system->getSavefileManager()->listSavefiles(pattern); + for (int i = 0; i < kMaxRecordsNames; ++i) { + Common::String recordName = Common::String::format("%s.r%02d", target.c_str(), i); + if (find(files.begin(), files.end(), recordName) != files.end()) { + continue; + } + return recordName; + } + return ""; +} + + +void EventRecorder::init(Common::String recordFileName, RecordMode mode) { + _fakeMixerManager = new NullSdlMixerManager(); + _fakeMixerManager->init(); + _fakeMixerManager->suspendAudio(); + _fakeTimer = 0; + _lastMillis = g_system->getMillis(); + _playbackFile = new Common::PlaybackFile(); + _lastScreenshotTime = 0; + _recordMode = mode; + _needcontinueGame = false; + if (ConfMan.hasKey("disable_display")) { + DebugMan.enableDebugChannel("EventRec"); + gDebugLevel = 1; + } + if (_recordMode == kRecorderPlayback) { + debugC(1, kDebugLevelEventRec, "playback:action=\"Load file\" filename=%s", recordFileName.c_str()); + } + g_system->getEventManager()->getEventDispatcher()->registerSource(this, false); + _screenshotPeriod = ConfMan.getInt("screenshot_period"); + if (_screenshotPeriod == 0) { + _screenshotPeriod = kDefaultScreenshotPeriod; + } + if (!openRecordFile(recordFileName)) { + deinit(); + error("playback:action=error reason=\"Record file loading error\""); + return; + } + if (_recordMode != kPassthrough) { + controlPanel = new GUI::OnScreenDialog(_recordMode == kRecorderRecord); + } + if (_recordMode == kRecorderPlayback) { + applyPlaybackSettings(); + _nextEvent = _playbackFile->getNextEvent(); + } + if (_recordMode == kRecorderRecord) { + getConfig(); + } + + switchMixer(); + switchTimerManagers(); + _needRedraw = true; + _initialized = true; +} + + +/** + * Opens or creates file depend of recording mode. + * + *@param id of recording or playing back game + *@return true in case of success, false in case of error + * + */ +bool EventRecorder::openRecordFile(const Common::String &fileName) { + bool result; + switch (_recordMode) { + case kRecorderRecord: + return _playbackFile->openWrite(fileName); + case kRecorderPlayback: + _recordMode = kPassthrough; + result = _playbackFile->openRead(fileName); + _recordMode = kRecorderPlayback; + return result; + default: + return false; + } + return true; +} + +bool EventRecorder::checkGameHash(const ADGameDescription *gameDesc) { + if ((gameDesc == NULL) && (_playbackFile->getHeader().hashRecords.size() != 0)) { + warning("Engine doesn't contain description table"); + return false; + } + for (const ADGameFileDescription *fileDesc = gameDesc->filesDescriptions; fileDesc->fileName; fileDesc++) { + if (_playbackFile->getHeader().hashRecords.find(fileDesc->fileName) == _playbackFile->getHeader().hashRecords.end()) { + warning("MD5 hash for file %s not found in record file", fileDesc->fileName); + debugC(1, kDebugLevelEventRec, "playback:action=\"Check game hash\" filename=%s filehash=%s storedhash=\"\" result=different", fileDesc->fileName, fileDesc->md5); + return false; + } + if (_playbackFile->getHeader().hashRecords[fileDesc->fileName] != fileDesc->md5) { + warning("Incorrect version of game file %s. Stored MD5 is %s. MD5 of loaded game is %s", fileDesc->fileName, _playbackFile->getHeader().hashRecords[fileDesc->fileName].c_str(), fileDesc->md5); + debugC(1, kDebugLevelEventRec, "playback:action=\"Check game hash\" filename=%s filehash=%s storedhash=%s result=different", fileDesc->fileName, fileDesc->md5, _playbackFile->getHeader().hashRecords[fileDesc->fileName].c_str()); + return false; + } + debugC(1, kDebugLevelEventRec, "playback:action=\"Check game hash\" filename=%s filehash=%s storedhash=%s result=equal", fileDesc->fileName, fileDesc->md5, _playbackFile->getHeader().hashRecords[fileDesc->fileName].c_str()); + } + return true; +} + +void EventRecorder::registerMixerManager(SdlMixerManager *mixerManager) { + _realMixerManager = mixerManager; +} + +void EventRecorder::switchMixer() { + if (_recordMode == kPassthrough) { + _realMixerManager->resumeAudio(); + } else { + _realMixerManager->suspendAudio(); + _fakeMixerManager->resumeAudio(); + } +} + +SdlMixerManager *EventRecorder::getMixerManager() { + if (_recordMode == kPassthrough) { + return _realMixerManager; + } else { + return _fakeMixerManager; + } +} + +void EventRecorder::getConfigFromDomain(Common::ConfigManager::Domain *domain) { + for (Common::ConfigManager::Domain::iterator entry = domain->begin(); entry!= domain->end(); ++entry) { + _playbackFile->getHeader().settingsRecords[entry->_key] = entry->_value; + } +} + +void EventRecorder::getConfig() { + getConfigFromDomain(ConfMan.getDomain(ConfMan.kApplicationDomain)); + getConfigFromDomain(ConfMan.getActiveDomain()); + _playbackFile->getHeader().settingsRecords["save_slot"] = ConfMan.get("save_slot"); +} + + +void EventRecorder::applyPlaybackSettings() { + for (Common::StringMap::iterator i = _playbackFile->getHeader().settingsRecords.begin(); i != _playbackFile->getHeader().settingsRecords.end(); ++i) { + Common::String currentValue = ConfMan.get(i->_key); + if (currentValue != i->_value) { + ConfMan.set(i->_key, i->_value, ConfMan.kTransientDomain); + debugC(1, kDebugLevelEventRec, "playback:action=\"Apply settings\" key=%s storedvalue=%s currentvalue=%s result=different", i->_key.c_str(), i->_value.c_str(), currentValue.c_str()); + } else { + debugC(1, kDebugLevelEventRec, "playback:action=\"Apply settings\" key=%s storedvalue=%s currentvalue=%s result=equal", i->_key.c_str(), i->_value.c_str(), currentValue.c_str()); + } + } + removeDifferentEntriesInDomain(ConfMan.getDomain(ConfMan.kApplicationDomain)); + removeDifferentEntriesInDomain(ConfMan.getActiveDomain()); +} + +void EventRecorder::removeDifferentEntriesInDomain(Common::ConfigManager::Domain *domain) { + for (Common::ConfigManager::Domain::iterator entry = domain->begin(); entry!= domain->end(); ++entry) { + if (_playbackFile->getHeader().settingsRecords.find(entry->_key) == _playbackFile->getHeader().settingsRecords.end()) { + debugC(1, kDebugLevelEventRec, "playback:action=\"Apply settings\" checksettings:key=%s storedvalue=%s currentvalue="" result=different", entry->_key.c_str(), entry->_value.c_str()); + domain->erase(entry->_key); + } + } +} + +DefaultTimerManager *EventRecorder::getTimerManager() { + return _timerManager; +} + +void EventRecorder::registerTimerManager(DefaultTimerManager *timerManager) { + _timerManager = timerManager; +} + +void EventRecorder::switchTimerManagers() { + delete _timerManager; + if (_recordMode == kPassthrough) { + _timerManager = new SdlTimerManager(); + } else { + _timerManager = new DefaultTimerManager(); + } +} + +void EventRecorder::updateSubsystems() { + if (_recordMode == kPassthrough) { + return; + } + RecordMode oldRecordMode = _recordMode; + _recordMode = kPassthrough; + _fakeMixerManager->update(); + _recordMode = oldRecordMode; +} + +Common::List EventRecorder::mapEvent(const Common::Event &ev, Common::EventSource *source) { + if ((!_initialized) && (_recordMode != kRecorderPlaybackPause)) { + return DefaultEventMapper::mapEvent(ev, source); + } + + checkForKeyCode(ev); + Common::Event evt = ev; + evt.mouse.x = evt.mouse.x * (g_system->getOverlayWidth() / g_system->getWidth()); + evt.mouse.y = evt.mouse.y * (g_system->getOverlayHeight() / g_system->getHeight()); + switch (_recordMode) { + case kRecorderPlayback: + if (ev.synthetic != true) { + return Common::List(); + } + return Common::DefaultEventMapper::mapEvent(ev, source); + break; + case kRecorderRecord: + g_gui.processEvent(evt, controlPanel); + if (((evt.type == Common::EVENT_LBUTTONDOWN) || (evt.type == Common::EVENT_LBUTTONUP) || (evt.type == Common::EVENT_MOUSEMOVE)) && controlPanel->isMouseOver()) { + return Common::List(); + } else { + Common::RecorderEvent e; + memcpy(&e, &ev, sizeof(ev)); + e.recordedtype = Common::kRecorderEventTypeNormal; + e.time = _fakeTimer; + _playbackFile->writeEvent(e); + return DefaultEventMapper::mapEvent(ev, source); + } + break; + case kRecorderPlaybackPause: { + Common::Event dialogEvent; + if (controlPanel->isEditDlgVisible()) { + dialogEvent = ev; + } else { + dialogEvent = evt; + } + g_gui.processEvent(dialogEvent, controlPanel->getActiveDlg()); + if (((dialogEvent.type == Common::EVENT_LBUTTONDOWN) || (dialogEvent.type == Common::EVENT_LBUTTONUP) || (dialogEvent.type == Common::EVENT_MOUSEMOVE)) && controlPanel->isMouseOver()) { + return Common::List(); + } + return Common::DefaultEventMapper::mapEvent(dialogEvent, source); + } + break; + default: + return Common::DefaultEventMapper::mapEvent(ev, source); + } +} + +void EventRecorder::setGameMd5(const ADGameDescription *gameDesc) { + for (const ADGameFileDescription *fileDesc = gameDesc->filesDescriptions; fileDesc->fileName; fileDesc++) { + if (fileDesc->md5 != NULL) { + _playbackFile->getHeader().hashRecords[fileDesc->fileName] = fileDesc->md5; + } + } +} + +void EventRecorder::processGameDescription(const ADGameDescription *desc) { + if (_recordMode == kRecorderRecord) { + setGameMd5(desc); + } + if ((_recordMode == kRecorderPlayback) && !checkGameHash(desc)) { + deinit(); + error("playback:action=error reason=\"\""); + } +} + +void EventRecorder::deleteRecord(const Common::String& fileName) { + g_system->getSavefileManager()->removeSavefile(fileName); +} + +void EventRecorder::takeScreenshot() { + if ((_fakeTimer - _lastScreenshotTime) > _screenshotPeriod) { + Graphics::Surface screen; + uint8 md5[16]; + if (grabScreenAndComputeMD5(screen, md5)) { + _lastScreenshotTime = _fakeTimer; + _playbackFile->saveScreenShot(screen, md5); + screen.free(); + } + } +} + +bool EventRecorder::grabScreenAndComputeMD5(Graphics::Surface &screen, uint8 md5[16]) { + if (!createScreenShot(screen)) { + warning("Can't save screenshot"); + return false; + } + Common::MemoryReadStream bitmapStream((const byte*)screen.pixels, screen.w * screen.h * screen.format.bytesPerPixel); + computeStreamMD5(bitmapStream, md5); + return true; +} + +Common::SeekableReadStream *EventRecorder::processSaveStream(const Common::String &fileName) { + Common::InSaveFile *saveFile; + switch (_recordMode) { + case kRecorderPlayback: + debugC(1, kDebugLevelEventRec, "playback:action=\"Process save file\" filename=%s len=%d", fileName.c_str(), _playbackFile->getHeader().saveFiles[fileName].size); + return new Common::MemoryReadStream(_playbackFile->getHeader().saveFiles[fileName].buffer, _playbackFile->getHeader().saveFiles[fileName].size); + case kRecorderRecord: + saveFile = _realSaveManager->openForLoading(fileName); + if (saveFile != NULL) { + _playbackFile->addSaveFile(fileName, saveFile); + saveFile->seek(0); + } + return saveFile; + default: + return NULL; + break; + } +} + +Common::SaveFileManager *EventRecorder::getSaveManager(Common::SaveFileManager *realSaveManager) { + _realSaveManager = realSaveManager; + if (_recordMode != kPassthrough) { + return &_fakeSaveManager; + } else { + return realSaveManager; + } +} + +void EventRecorder::preDrawOverlayGui() { + if ((_initialized) || (_needRedraw)) { + RecordMode oldMode = _recordMode; + _recordMode = kPassthrough; + g_system->showOverlay(); + g_gui.theme()->clearAll(); + g_gui.theme()->openDialog(true, GUI::ThemeEngine::kShadingNone); + controlPanel->drawDialog(); + g_gui.theme()->finishBuffering(); + g_gui.theme()->updateScreen(); + _recordMode = oldMode; + } +} + +void EventRecorder::postDrawOverlayGui() { + if ((_initialized) || (_needRedraw)) { + RecordMode oldMode = _recordMode; + _recordMode = kPassthrough; + g_system->hideOverlay(); + _recordMode = oldMode; + } +} + +Common::StringArray EventRecorder::listSaveFiles(const Common::String &pattern) { + if (_recordMode == kRecorderPlayback) { + Common::StringArray result; + for (Common::HashMap::iterator i = _playbackFile->getHeader().saveFiles.begin(); i != _playbackFile->getHeader().saveFiles.end(); ++i) { + if (i->_key.matchString(pattern, false, true)) { + result.push_back(i->_key); + } + } + return result; + } else { + return _realSaveManager->listSavefiles(pattern); + } +} + +void EventRecorder::setFileHeader() { + if (_recordMode != kRecorderRecord) { + return; + } + TimeDate t; + const EnginePlugin *plugin = 0; + GameDescriptor desc = EngineMan.findGame(ConfMan.getActiveDomainName(), &plugin); + g_system->getTimeAndDate(t); + if (_author.empty()) { + setAuthor("Unknown Author"); + } + if (_name.empty()) { + g_eventRec.setName(Common::String::format("%.2d.%.2d.%.4d ", t.tm_mday, t.tm_mon, 1900 + t.tm_year) + desc.description()); + } + _playbackFile->getHeader().author = _author; + _playbackFile->getHeader().notes = _desc; + _playbackFile->getHeader().name = _name; +} + +SDL_Surface *EventRecorder::getSurface(int width, int height) { + // Create a RGB565 surface of the requested dimensions. + return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 16, 0xF800, 0x07E0, 0x001F, 0x0000); +} + +bool EventRecorder::switchMode() { + const Common::String gameId = ConfMan.get("gameid"); + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + bool metaInfoSupport = (*plugin)->hasFeature(MetaEngine::kSavesSupportMetaInfo); + bool featuresSupport = metaInfoSupport && + g_engine->canSaveGameStateCurrently() && + (*plugin)->hasFeature(MetaEngine::kSupportsListSaves) && + (*plugin)->hasFeature(MetaEngine::kSupportsDeleteSave); + if (!featuresSupport) { + return false; + } + + int emptySlot = 1; + SaveStateList saveList = (*plugin)->listSaves(gameId.c_str()); + for (SaveStateList::const_iterator x = saveList.begin(); x != saveList.end(); ++x) { + int saveSlot = x->getSaveSlot(); + if (saveSlot == 0) { + continue; + } + if (emptySlot != saveSlot) { + break; + } + emptySlot++; + } + Common::String saveName; + if (emptySlot >= 0) { + saveName = Common::String::format("Save %d", emptySlot + 1); + Common::Error status = g_engine->saveGameState(emptySlot, saveName); + if (status.getCode() == Common::kNoError) { + Common::Event eventRTL; + eventRTL.type = Common::EVENT_RTL; + g_system->getEventManager()->pushEvent(eventRTL); + } + } + ConfMan.set("record_mode", "", Common::ConfigManager::kTransientDomain); + ConfMan.setInt("save_slot", emptySlot, Common::ConfigManager::kTransientDomain); + _needcontinueGame = true; + return true; +} + +bool EventRecorder::checkForContinueGame() { + bool result = _needcontinueGame; + _needcontinueGame = false; + return result; +} + +void EventRecorder::deleteTemporarySave() { + if (_temporarySlot == -1) return; + const Common::String gameId = ConfMan.get("gameid"); + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + (*plugin)->removeSaveState(gameId.c_str(), _temporarySlot); + _temporarySlot = -1; +} + +} // End of namespace GUI + +#endif // ENABLE_EVENTRECORDER + diff --git a/gui/EventRecorder.h b/gui/EventRecorder.h new file mode 100644 index 00000000000..3e32d892324 --- /dev/null +++ b/gui/EventRecorder.h @@ -0,0 +1,293 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GUI_EVENTRECORDER_H +#define GUI_EVENTRECORDER_H + +#include "common/system.h" + +#include "common/events.h" +#include "common/savefile.h" +#include "common/singleton.h" + +#include "engines/advancedDetector.h" + +#ifdef ENABLE_EVENTRECORDER + +#include "common/mutex.h" +#include "common/array.h" +#include "common/memstream.h" +#include "backends/keymapper/keymapper.h" +#include "backends/mixer/sdl/sdl-mixer.h" +#include "common/hashmap.h" +#include "common/hash-str.h" +#include "backends/timer/sdl/sdl-timer.h" +#include "common/config-manager.h" +#include "common/recorderfile.h" +#include "backends/saves/recorder/recorder-saves.h" +#include "backends/mixer/nullmixer/nullsdl-mixer.h" +#include "backends/saves/default/default-saves.h" + + +#define g_eventRec (GUI::EventRecorder::instance()) + +namespace GUI { + class OnScreenDialog; +} + +namespace GUI { +class RandomSource; +class SeekableReadStream; +class WriteStream; + + +/** + * Our generic event recorder. + * + * TODO: Add more documentation. + */ +class EventRecorder : private Common::EventSource, public Common::Singleton, private Common::DefaultEventMapper { + friend class Common::Singleton; + EventRecorder(); + ~EventRecorder(); +public: + /** Specify operation mode of Event Recorder */ + enum RecordMode { + kPassthrough = 0, /**< kPassthrough, do nothing */ + kRecorderRecord = 1, /**< kRecorderRecord, do the recording */ + kRecorderPlayback = 2, /**< kRecorderPlayback, playback existing recording */ + kRecorderPlaybackPause = 3 /**< kRecordetPlaybackPause, interal state when user pauses the playback */ + }; + + void init(Common::String recordFileName, RecordMode mode); + void deinit(); + bool processDelayMillis(); + uint32 getRandomSeed(const Common::String &name); + void processMillis(uint32 &millis, bool skipRecord); + bool processAudio(uint32 &samples, bool paused); + void processGameDescription(const ADGameDescription *desc); + Common::SeekableReadStream *processSaveStream(const Common::String & fileName); + + /** Hooks for intercepting into GUI processing, so required events could be shoot + * or filtered out */ + void preDrawOverlayGui(); + void postDrawOverlayGui(); + + /** Set recording author + * + * @see getAuthor + */ + void setAuthor(const Common::String &author) { + _author = author; + } + + /** Set recording notes + * + * @see getNotes + */ + void setNotes(const Common::String &desc){ + _desc = desc; + } + + /** Set descriptive name of the recording + * + * @see getName + */ + void setName(const Common::String &name) { + _name = name; + } + + /** Get recording author + * + * @see getAuthor + */ + const Common::String getAuthor() { + return _author; + } + + /** Get recording notes + * + * @see setNotes + */ + const Common::String getNotes() { + return _desc; + } + + /** Get recording name + * + * @see setName + */ + const Common::String getName() { + return _name; + } + void setRedraw(bool redraw) { + _needRedraw = redraw; + } + + void registerMixerManager(SdlMixerManager *mixerManager); + void registerTimerManager(DefaultTimerManager *timerManager); + + SdlMixerManager *getMixerManager(); + DefaultTimerManager *getTimerManager(); + + void deleteRecord(const Common::String& fileName); + bool checkForContinueGame(); + + void suspendRecording() { + _savedState = _initialized; + _initialized = false; + } + + void resumeRecording() { + _initialized = _savedState; + } + + Common::StringArray listSaveFiles(const Common::String &pattern); + Common::String generateRecordFileName(const Common::String &target); + + Common::SaveFileManager *getSaveManager(Common::SaveFileManager *realSaveManager); + SDL_Surface *getSurface(int width, int height); + void RegisterEventSource(); + + /** Retrieve game screenshot and compute its checksum for comparison */ + bool grabScreenAndComputeMD5(Graphics::Surface &screen, uint8 md5[16]); + + void updateSubsystems(); + bool switchMode(); + void switchFastMode(); + +private: + virtual Common::List mapEvent(const Common::Event &ev, Common::EventSource *source); + bool notifyPoll(); + bool pollEvent(Common::Event &ev); + bool _initialized; + volatile uint32 _fakeTimer; + bool _savedState; + bool _needcontinueGame; + int _temporarySlot; + Common::String _author; + Common::String _desc; + Common::String _name; + + Common::SaveFileManager *_realSaveManager; + SdlMixerManager *_realMixerManager; + DefaultTimerManager *_timerManager; + RecorderSaveFileManager _fakeSaveManager; + NullSdlMixerManager *_fakeMixerManager; + GUI::OnScreenDialog *controlPanel; + Common::RecorderEvent _nextEvent; + + void setFileHeader(); + void setGameMd5(const ADGameDescription *gameDesc); + void getConfig(); + void getConfigFromDomain(Common::ConfigManager::Domain *domain); + void removeDifferentEntriesInDomain(Common::ConfigManager::Domain *domain); + void applyPlaybackSettings(); + + void switchMixer(); + void switchTimerManagers(); + + void togglePause(); + + void takeScreenshot(); + + bool openRecordFile(const Common::String &fileName); + + bool checkGameHash(const ADGameDescription *desc); + + void checkForKeyCode(const Common::Event &event); + bool allowMapping() const { return false; } + + volatile uint32 _lastMillis; + uint32 _lastScreenshotTime; + uint32 _screenshotPeriod; + Common::PlaybackFile *_playbackFile; + + void saveScreenShot(); + void checkRecordedMD5(); + void deleteTemporarySave(); + volatile RecordMode _recordMode; + Common::String _recordFileName; + bool _fastPlayback; + bool _needRedraw; +}; + +} // End of namespace GUI + +#else + +#ifdef SDL_BACKEND +#include "backends/timer/default/default-timer.h" +#include "backends/mixer/sdl/sdl-mixer.h" +#endif + +#define g_eventRec (GUI::EventRecorder::instance()) + +namespace GUI { + +class EventRecorder : private Common::EventSource, public Common::Singleton, private Common::DefaultEventMapper { + friend class Common::Singleton; + + public: + EventRecorder() { +#ifdef SDL_BACKEND + _timerManager = NULL; + _realMixerManager = NULL; +#endif + } + ~EventRecorder() {} + + bool pollEvent(Common::Event &ev) { return false; } + void RegisterEventSource() {} + void deinit() {} + void suspendRecording() {} + void resumeRecording() {} + void preDrawOverlayGui() {} + void postDrawOverlayGui() {} + void processGameDescription(const ADGameDescription *desc) {} + void updateSubsystems() {} + uint32 getRandomSeed(const Common::String &name) { return g_system->getMillis(); } + Common::SaveFileManager *getSaveManager(Common::SaveFileManager *realSaveManager) { return realSaveManager; } + +#ifdef SDL_BACKEND + private: + DefaultTimerManager *_timerManager; + SdlMixerManager *_realMixerManager; + + public: + DefaultTimerManager *getTimerManager() { return _timerManager; } + void registerTimerManager(DefaultTimerManager *timerManager) { _timerManager = timerManager; } + + SdlMixerManager *getMixerManager() { return _realMixerManager; } + void registerMixerManager(SdlMixerManager *mixerManager) { _realMixerManager = mixerManager; } + + void processMillis(uint32 &millis, bool skipRecord) {} + bool processDelayMillis() { return false; } +#endif + +}; + +} // namespace GUI + +#endif // ENABLE_EVENTRECORDER + +#endif diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index 522998a7763..996e8f24248 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -50,6 +50,14 @@ const char * const ThemeEngine::kImageEraser = "eraser.bmp"; const char * const ThemeEngine::kImageDelbtn = "delbtn.bmp"; const char * const ThemeEngine::kImageList = "list.bmp"; const char * const ThemeEngine::kImageGrid = "grid.bmp"; +const char * const ThemeEngine::kImageStopbtn = "stopbtn.bmp"; +const char * const ThemeEngine::kImageEditbtn = "editbtn.bmp"; +const char * const ThemeEngine::kImageSwitchModebtn = "switchbtn.bmp"; +const char * const ThemeEngine::kImageFastReplaybtn = "fastreplay.bmp"; +const char * const ThemeEngine::kImageStopSmallbtn = "stopbtn_small.bmp"; +const char * const ThemeEngine::kImageEditSmallbtn = "editbtn_small.bmp"; +const char * const ThemeEngine::kImageSwitchModeSmallbtn = "switchbtn_small.bmp"; +const char * const ThemeEngine::kImageFastReplaySmallbtn = "fastreplay_small.bmp"; struct TextDrawData { const Graphics::Font *_fontPtr; @@ -465,11 +473,7 @@ void ThemeEngine::enable() { if (_enabled) return; - if (_useCursor) { - CursorMan.pushCursorPalette(_cursorPal, 0, _cursorPalSize); - CursorMan.pushCursor(_cursor, _cursorWidth, _cursorHeight, _cursorHotspotX, _cursorHotspotY, 255, true); - CursorMan.showMouse(true); - } + showCursor(); _system->showOverlay(); clearAll(); @@ -482,10 +486,8 @@ void ThemeEngine::disable() { _system->hideOverlay(); - if (_useCursor) { - CursorMan.popCursorPalette(); - CursorMan.popCursor(); - } + hideCursor(); + _enabled = false; } @@ -1787,5 +1789,20 @@ Common::String ThemeEngine::getThemeId(const Common::String &filename) { } } +void ThemeEngine::showCursor() { + if (_useCursor) { + CursorMan.pushCursorPalette(_cursorPal, 0, _cursorPalSize); + CursorMan.pushCursor(_cursor, _cursorWidth, _cursorHeight, _cursorHotspotX, _cursorHotspotY, 255, true); + CursorMan.showMouse(true); + } +} + +void ThemeEngine::hideCursor() { + if (_useCursor) { + CursorMan.popCursorPalette(); + CursorMan.popCursor(); + } +} + } // End of namespace GUI. diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index 6fb93d3b462..160ceb3259c 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -35,7 +35,7 @@ #include "graphics/pixelformat.h" -#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.16" +#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.20" class OSystem; @@ -234,6 +234,14 @@ public: static const char *const kImageDelbtn; ///< Delete characters in the predictive dialog static const char *const kImageList; ///< List image used in save/load chooser selection static const char *const kImageGrid; ///< Grid image used in save/load chooser selection + static const char *const kImageStopbtn; ///< Stop recording button in recorder onscreen dialog + static const char *const kImageEditbtn; ///< Edit recording metadata in recorder onscreen dialog + static const char *const kImageSwitchModebtn; ///< Switch mode button in recorder onscreen dialog + static const char *const kImageFastReplaybtn; ///< Fast playback mode button in recorder onscreen dialog + static const char *const kImageStopSmallbtn; ///< Stop recording button in recorder onscreen dialog (for 320xY) + static const char *const kImageEditSmallbtn; ///< Edit recording metadata in recorder onscreen dialog (for 320xY) + static const char *const kImageSwitchModeSmallbtn; ///< Switch mode button in recorder onscreen dialog (for 320xY) + static const char *const kImageFastReplaySmallbtn; ///< Fast playback mode button in recorder onscreen dialog (for 320xY) /** * Graphics mode enumeration. @@ -275,8 +283,13 @@ public: void refresh(); void enable(); + + void showCursor(); + void hideCursor(); + void disable(); + /** * Query the set up pixel format. */ diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp index 8285aff7ca4..bd5b406ca83 100644 --- a/gui/ThemeParser.cpp +++ b/gui/ThemeParser.cpp @@ -335,11 +335,15 @@ bool ThemeParser::parserCallback_drawstep(ParserNode *node) { drawstep->drawingCall = getDrawingFunctionCallback(functionName); - if (drawstep->drawingCall == 0) + if (drawstep->drawingCall == 0) { + delete drawstep; return parserError(functionName + " is not a valid drawing function name"); + } - if (!parseDrawStep(node, drawstep, true)) + if (!parseDrawStep(node, drawstep, true)) { + delete drawstep; return false; + } _theme->addDrawStep(getParentNode(node)->values["id"], *drawstep); delete drawstep; @@ -691,7 +695,7 @@ bool ThemeParser::parserCallback_layout(ParserNode *node) { return false; } - Common::parseBool(node->values["center"], center); + (void)Common::parseBool(node->values["center"], center); if (node->values["type"] == "vertical") _theme->getEvaluator()->addLayout(GUI::ThemeLayout::kLayoutVertical, spacing, center); diff --git a/gui/about.cpp b/gui/about.cpp index 37617ea238c..9b52c53b851 100644 --- a/gui/about.cpp +++ b/gui/about.cpp @@ -37,14 +37,16 @@ enum { kScrollMillisPerPixel = 60 }; -// The following commands can be put at the start of a line (all subject to change): -// \C, \L, \R -- set center/left/right alignment -// \c0 - \c4 -- set a custom color: -// 0 normal text (green) -// 1 highlighted text (light green) -// 2 light border (light gray) -// 3 dark border (dark gray) -// 4 background (black) +// Every Line should start with a letter followed by a digit. Currently those can be +// (all subject to change) +// Letter: +// C, L, R -- set center/left/right alignment +// A -- ASCII text to replace the next (latin1) line +// Digit: +// 0 - 2 -- set a custom color: +// 0 normal text +// 1 highlighted text +// 2 disabled text // TODO: Maybe add a tab/indent feature; that is, make it possible to specify // an amount by which that line shall be indented (the indent of course would have // to be considered while performing any word wrapping, too). @@ -110,16 +112,16 @@ AboutDialog::AboutDialog() const EnginePlugin::List &plugins = EngineMan.getPlugins(); EnginePlugin::List::const_iterator iter = plugins.begin(); for (; iter != plugins.end(); ++iter) { - Common::String str; - str = "C0"; - str += (**iter).getName(); - addLine(str.c_str()); + Common::String str; + str = "C0"; + str += (**iter).getName(); + addLine(str.c_str()); - str = "C2"; - str += (**iter)->getOriginalCopyright(); - addLine(str.c_str()); + str = "C2"; + str += (**iter)->getOriginalCopyright(); + addLine(str.c_str()); - //addLine(""); + //addLine(""); } for (i = 0; i < ARRAYSIZE(gpl_text); i++) @@ -137,9 +139,26 @@ void AboutDialog::addLine(const char *str) { } else { Common::String format(str, 2); str += 2; - + + static Common::String asciiStr; + if (format[0] == 'A') { + bool useAscii = false; +#ifdef USE_TRANSLATION + // We could use TransMan.getCurrentCharset() but rather than compare strings + // it is easier to use TransMan.getCharsetMapping() (non null in case of non + // ISO-8859-1 mapping) + useAscii = (TransMan.getCharsetMapping() != NULL); +#endif + if (useAscii) + asciiStr = str; + return; + } StringArray wrappedLines; - g_gui.getFont().wordWrapText(str, _w - 2 * _xOff, wrappedLines); + if (!asciiStr.empty()) { + g_gui.getFont().wordWrapText(asciiStr, _w - 2 * _xOff, wrappedLines); + asciiStr.clear(); + } else + g_gui.getFont().wordWrapText(str, _w - 2 * _xOff, wrappedLines); for (StringArray::const_iterator i = wrappedLines.begin(); i != wrappedLines.end(); ++i) { _lines.push_back(format + *i); @@ -285,7 +304,7 @@ void AboutDialog::reflowLayout() { int maxW = _w - 2*_xOff; _w = 0; for (i = 0; i < ARRAYSIZE(credits); i++) { - int tmp = g_gui.getStringWidth(credits[i] + 5); + int tmp = g_gui.getStringWidth(credits[i]) + 5; if (_w < tmp && tmp <= maxW) { _w = tmp; } diff --git a/gui/browser.cpp b/gui/browser.cpp index 2b4f2541565..84f2d0f7475 100644 --- a/gui/browser.cpp +++ b/gui/browser.cpp @@ -32,7 +32,8 @@ namespace GUI { enum { kChooseCmd = 'Chos', - kGoUpCmd = 'GoUp' + kGoUpCmd = 'GoUp', + kHiddenCmd = 'Hidd' }; /* We want to use this as a general directory selector at some point... possible uses @@ -47,6 +48,7 @@ BrowserDialog::BrowserDialog(const char *title, bool dirBrowser) _isDirBrowser = dirBrowser; _fileList = NULL; _currentPath = NULL; + _showHidden = ConfMan.getBool("gui_browser_show_hidden", Common::ConfigManager::kApplicationDomain); // Headline - TODO: should be customizable during creation time new StaticTextWidget(this, "Browser.Headline", title); @@ -61,6 +63,9 @@ BrowserDialog::BrowserDialog(const char *title, bool dirBrowser) _backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain; + // Checkbox for the "show hidden files" state. + _showHiddenWidget = new CheckboxWidget(this, "Browser.Hidden", _("Show hidden files"), _("Show files marked with the hidden attribute"), kHiddenCmd); + // Buttons if (g_system->getOverlayWidth() > 320) new ButtonWidget(this, "Browser.Up", _("Go up"), _("Go to previous directory level"), kGoUpCmd); @@ -132,6 +137,15 @@ void BrowserDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data if (data != (uint32)-1 && _isDirBrowser && !_nodeContent[data].isDirectory()) _fileList->setSelected(-1); break; + case kHiddenCmd: + // Update whether the user wants hidden files to be shown + _showHidden = _showHiddenWidget->getState(); + // We save the state in the application domain to avoid cluttering and + // to prevent odd behavior. + ConfMan.setBool("gui_browser_show_hidden", _showHidden, Common::ConfigManager::kApplicationDomain); + // Update the file listing + updateListing(); + break; default: Dialog::handleCommand(sender, cmd, data); } @@ -145,7 +159,7 @@ void BrowserDialog::updateListing() { ConfMan.set("browser_lastpath", _node.getPath()); // Read in the data from the file system - if (!_node.getChildren(_nodeContent, Common::FSNode::kListAll)) + if (!_node.getChildren(_nodeContent, Common::FSNode::kListAll, _showHidden)) _nodeContent.clear(); else Common::sort(_nodeContent.begin(), _nodeContent.end()); diff --git a/gui/browser.h b/gui/browser.h index 5cf091fbf41..b82fe516f97 100644 --- a/gui/browser.h +++ b/gui/browser.h @@ -29,6 +29,7 @@ namespace GUI { class ListWidget; class StaticTextWidget; +class CheckboxWidget; class CommandSender; class BrowserDialog : public Dialog { @@ -40,6 +41,7 @@ public: virtual int runModal(); #else virtual void open(); + virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); #endif @@ -49,11 +51,14 @@ protected: #ifdef MACOSX const void *_titleRef; const void *_chooseRef; + const void *_hiddenFilesRef; #else ListWidget *_fileList; StaticTextWidget *_currentPath; Common::FSNode _node; Common::FSList _nodeContent; + bool _showHidden; + CheckboxWidget *_showHiddenWidget; #endif Common::FSNode _choice; bool _isDirBrowser; diff --git a/gui/browser_osx.mm b/gui/browser_osx.mm index ecd60915f8e..642718df947 100644 --- a/gui/browser_osx.mm +++ b/gui/browser_osx.mm @@ -30,10 +30,55 @@ #include "common/algorithm.h" #include "common/translation.h" +#include #include +#include #include #include +@interface ShowHiddenFilesController : NSObject { + NSOpenPanel* _panel; +} + +- (id) init; +- (void) dealloc; +- (void) setOpenPanel : (NSOpenPanel*) panel; +- (IBAction) showHiddenFiles : (id) sender; + +@end + +@implementation ShowHiddenFilesController + +- (id) init { + self = [super init]; + _panel = 0; + + return self; +} + +- (void) dealloc { + [_panel release]; + [super dealloc]; +} + +- (void) setOpenPanel : (NSOpenPanel*) panel { + _panel = panel; + [_panel retain]; +} + + +- (IBAction) showHiddenFiles : (id) sender { + if ([sender state] == NSOnState) { + [_panel setShowsHiddenFiles: YES]; + ConfMan.setBool("gui_browser_show_hidden", true, Common::ConfigManager::kApplicationDomain); + } else { + [_panel setShowsHiddenFiles: NO]; + ConfMan.setBool("gui_browser_show_hidden", false, Common::ConfigManager::kApplicationDomain); + } +} + +@end + namespace GUI { BrowserDialog::BrowserDialog(const char *title, bool dirBrowser) @@ -56,11 +101,13 @@ BrowserDialog::BrowserDialog(const char *title, bool dirBrowser) // Convert button text to NSString _chooseRef = CFStringCreateWithCString(0, _("Choose"), stringEncoding); + _hiddenFilesRef = CFStringCreateWithCString(0, _("Show hidden files"), stringEncoding); } BrowserDialog::~BrowserDialog() { CFRelease(_titleRef); CFRelease(_chooseRef); + CFRelease(_hiddenFilesRef); } int BrowserDialog::runModal() { @@ -82,6 +129,29 @@ int BrowserDialog::runModal() { [panel setCanChooseDirectories:_isDirBrowser]; [panel setTitle:(NSString *)_titleRef]; [panel setPrompt:(NSString *)_chooseRef]; + + NSButton *showHiddenFilesButton = 0; + ShowHiddenFilesController *showHiddenFilesController = 0; + if ([panel respondsToSelector:@selector(setShowsHiddenFiles:)]) { + showHiddenFilesButton = [[NSButton alloc] init]; + [showHiddenFilesButton setButtonType:NSSwitchButton]; + [showHiddenFilesButton setTitle:(NSString *)_hiddenFilesRef]; + [showHiddenFilesButton sizeToFit]; + if (ConfMan.getBool("gui_browser_show_hidden", Common::ConfigManager::kApplicationDomain)) { + [showHiddenFilesButton setState:NSOnState]; + [panel setShowsHiddenFiles: YES]; + } else { + [showHiddenFilesButton setState:NSOffState]; + [panel setShowsHiddenFiles: NO]; + } + [panel setAccessoryView:showHiddenFilesButton]; + + showHiddenFilesController = [[ShowHiddenFilesController alloc] init]; + [showHiddenFilesController setOpenPanel:panel]; + [showHiddenFilesButton setTarget:showHiddenFilesController]; + [showHiddenFilesButton setAction:@selector(showHiddenFiles:)]; + } + if ([panel runModal] == NSOKButton) { NSURL *url = [panel URL]; if ([url isFileURL]) { @@ -91,6 +161,9 @@ int BrowserDialog::runModal() { } } + [showHiddenFilesButton release]; + [showHiddenFilesController release]; + // If we were in fullscreen mode, switch back if (wasFullscreen) { g_system->beginGFXTransaction(); diff --git a/gui/debugger.cpp b/gui/debugger.cpp index 972163df6fd..35627dd584a 100644 --- a/gui/debugger.cpp +++ b/gui/debugger.cpp @@ -554,4 +554,4 @@ bool Debugger::debuggerCompletionCallback(GUI::ConsoleDialog *console, const cha #endif -} // End of namespace GUI +} // End of namespace GUI diff --git a/gui/debugger.h b/gui/debugger.h index 3a587d27232..b79e8723c1c 100644 --- a/gui/debugger.h +++ b/gui/debugger.h @@ -207,6 +207,6 @@ public: }; -} // End of namespace GUI +} // End of namespace GUI #endif diff --git a/gui/dialog.h b/gui/dialog.h index 1773c6633ea..d269a2f6452 100644 --- a/gui/dialog.h +++ b/gui/dialog.h @@ -37,6 +37,8 @@ struct Event; namespace GUI { +class EventRecorder; + class Widget; // Some "common" commands sent to handleCommand() @@ -47,6 +49,7 @@ enum { class Dialog : public GuiObject { friend class GuiManager; + friend class EventRecorder; friend class Tooltip; protected: Widget *_mouseWidget; diff --git a/gui/editrecorddialog.cpp b/gui/editrecorddialog.cpp new file mode 100644 index 00000000000..a6a7a2560e9 --- /dev/null +++ b/gui/editrecorddialog.cpp @@ -0,0 +1,87 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "editrecorddialog.h" +#include "gui/widgets/edittext.h" +#include "common/translation.h" + + +namespace GUI { + +const Common::String EditRecordDialog::getAuthor() { + return _authorEdit->getEditString(); +} + +void EditRecordDialog::setAuthor(const Common::String &author) { + _authorEdit->setEditString(author); +} + +const Common::String EditRecordDialog::getNotes() { + return _notesEdit->getEditString(); +} + +void EditRecordDialog::setNotes(const Common::String &desc) { + _notesEdit->setEditString(desc); +} + +const Common::String EditRecordDialog::getName() { + return _nameEdit->getEditString(); +} + +void EditRecordDialog::setName(const Common::String &name) { + _nameEdit->setEditString(name); +} + +EditRecordDialog::~EditRecordDialog() { +} + +EditRecordDialog::EditRecordDialog(const Common::String author, const Common::String name, const Common::String notes) : Dialog("EditRecordDialog") { + new StaticTextWidget(this,"EditRecordDialog.AuthorLabel",_("Author:")); + new StaticTextWidget(this,"EditRecordDialog.NameLabel",_("Name:")); + new StaticTextWidget(this,"EditRecordDialog.NotesLabel",_("Notes:")); + _authorEdit = new EditTextWidget(this, "EditRecordDialog.AuthorEdit",""); + _notesEdit = new EditTextWidget(this, "EditRecordDialog.NotesEdit",""); + _nameEdit = new EditTextWidget(this, "EditRecordDialog.NameEdit",""); + _authorEdit->setEditString(author); + _notesEdit->setEditString(notes); + _nameEdit->setEditString(name); + new GUI::ButtonWidget(this, "EditRecordDialog.Cancel", _("Cancel"), 0, kCloseCmd); + new GUI::ButtonWidget(this, "EditRecordDialog.OK", _("Ok"), 0, kOKCmd); +} + +void EditRecordDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { + switch(cmd) { + case kCloseCmd: + setResult(kCloseCmd); + close(); + break; + case kOKCmd: + setResult(kOKCmd); + close(); + break; + default: + Dialog::handleCommand(sender, cmd, data); + break; + } +} + +} diff --git a/gui/editrecorddialog.h b/gui/editrecorddialog.h new file mode 100644 index 00000000000..c8da4521ca3 --- /dev/null +++ b/gui/editrecorddialog.h @@ -0,0 +1,56 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GUI_EDITRECORDDIALOG_H +#define GUI_EDITRECORDDIALOG_H + +#include "gui/dialog.h" + +namespace GUI { + +class EditTextWidget; +class StaticTextWidget; + +class EditRecordDialog : public Dialog { +private: + EditTextWidget *_notesEdit; + EditTextWidget *_nameEdit; + EditTextWidget *_authorEdit; + EditRecordDialog() : Dialog("EditRecordDialog") {}; +public: + EditRecordDialog(const Common::String author, const Common::String name, const Common::String notes); + ~EditRecordDialog(); + + const Common::String getAuthor(); + const Common::String getNotes(); + const Common::String getName(); + + void setAuthor(const Common::String &author); + void setNotes(const Common::String &desc); + void setName(const Common::String &name); + + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); +}; + +}// End of namespace GUI + +#endif diff --git a/gui/fluidsynth-dialog.cpp b/gui/fluidsynth-dialog.cpp new file mode 100644 index 00000000000..662518b5577 --- /dev/null +++ b/gui/fluidsynth-dialog.cpp @@ -0,0 +1,366 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "gui/fluidsynth-dialog.h" +#include "gui/message.h" +#include "gui/widgets/tab.h" +#include "gui/widgets/popup.h" + +#include "common/config-manager.h" +#include "common/translation.h" +#include "common/debug.h" + +namespace GUI { + +enum { + kActivateChorusCmd = 'acho', + kChorusVoiceCountChangedCmd = 'cvcc', + kChorusLevelChangedCmd = 'clec', + kChorusSpeedChangedCmd = 'cspc', + kChorusDepthChangedCmd = 'cdec', + + kActivateReverbCmd = 'arev', + kReverbRoomSizeChangedCmd = 'rrsc', + kReverbDampingChangedCmd = 'rdac', + kReverbWidthChangedCmd = 'rwic', + kReverbLevelChangedCmd = 'rlec', + + kResetSettingsCmd = 'rese' +}; + +enum { + kWaveFormTypeSine = 0, + kWaveFormTypeTriangle = 1 +}; + +enum { + kInterpolationNone = 0, + kInterpolationLinear = 1, + kInterpolation4thOrder = 2, + kInterpolation7thOrder = 3 +}; + +FluidSynthSettingsDialog::FluidSynthSettingsDialog() + : Dialog("FluidSynthSettings") { + _domain = Common::ConfigManager::kApplicationDomain; + + _tabWidget = new TabWidget(this, "FluidSynthSettings.TabWidget"); + + _tabWidget->addTab(_("Reverb")); + + _reverbActivate = new CheckboxWidget(_tabWidget, "FluidSynthSettings_Reverb.EnableTabCheckbox", _("Active"), 0, kActivateReverbCmd); + + _reverbRoomSizeDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.RoomSizeText", _("Room:")); + _reverbRoomSizeSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Reverb.RoomSizeSlider", 0, kReverbRoomSizeChangedCmd); + // 0.00 - 1.20, Default: 0.20 + _reverbRoomSizeSlider->setMinValue(0); + _reverbRoomSizeSlider->setMaxValue(120); + _reverbRoomSizeLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.RoomSizeLabel", "20"); + + _reverbDampingDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.DampingText", _("Damp:")); + _reverbDampingSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Reverb.DampingSlider", 0, kReverbDampingChangedCmd); + // 0.00 - 1.00, Default: 0.00 + _reverbDampingSlider->setMinValue(0); + _reverbDampingSlider->setMaxValue(100); + _reverbDampingLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.DampingLabel", "0"); + + _reverbWidthDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.WidthText", _("Width:")); + _reverbWidthSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Reverb.WidthSlider", 0, kReverbWidthChangedCmd); + // 0 - 100, Default: 1 + _reverbWidthSlider->setMinValue(0); + _reverbWidthSlider->setMaxValue(100); + _reverbWidthLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.WidthLabel", "1"); + + _reverbLevelDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.LevelText", _("Level:")); + _reverbLevelSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Reverb.LevelSlider", 0, kReverbLevelChangedCmd); + // 0.00 - 1.00, Default: 0.90 + _reverbLevelSlider->setMinValue(0); + _reverbLevelSlider->setMaxValue(100); + _reverbLevelLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Reverb.LevelLabel", "90"); + + _tabWidget->addTab(_("Chorus")); + + _chorusActivate = new CheckboxWidget(_tabWidget, "FluidSynthSettings_Chorus.EnableTabCheckbox", _("Active"), 0, kActivateChorusCmd); + + _chorusVoiceCountDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.VoiceCountText", _("N:")); + _chorusVoiceCountSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Chorus.VoiceCountSlider", 0, kChorusVoiceCountChangedCmd); + // 0-99, Default: 3 + _chorusVoiceCountSlider->setMinValue(0); + _chorusVoiceCountSlider->setMaxValue(99); + _chorusVoiceCountLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.VoiceCountLabel", "3"); + + _chorusLevelDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.LevelText", _("Level:")); + _chorusLevelSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Chorus.LevelSlider", 0, kChorusLevelChangedCmd); + // 0.00 - 1.00, Default: 1.00 + _chorusLevelSlider->setMinValue(0); + _chorusLevelSlider->setMaxValue(100); + _chorusLevelLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.LevelLabel", "100"); + + _chorusSpeedDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.SpeedText", _("Speed:")); + _chorusSpeedSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Chorus.SpeedSlider", 0, kChorusSpeedChangedCmd); + // 0.30 - 5.00, Default: 0.30 + _chorusSpeedSlider->setMinValue(30); + _chorusSpeedSlider->setMaxValue(500); + _chorusSpeedLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.SpeedLabel", "30"); + + _chorusDepthDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.DepthText", _("Depth:")); + _chorusDepthSlider = new SliderWidget(_tabWidget, "FluidSynthSettings_Chorus.DepthSlider", 0, kChorusDepthChangedCmd); + // 0.00 - 21.00, Default: 8.00 + _chorusDepthSlider->setMinValue(0); + _chorusDepthSlider->setMaxValue(210); + _chorusDepthLabel = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.DepthLabel", "80"); + + _chorusWaveFormTypePopUpDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Chorus.WaveFormTypeText", _("Type:")); + _chorusWaveFormTypePopUp = new PopUpWidget(_tabWidget, "FluidSynthSettings_Chorus.WaveFormType"); + + _chorusWaveFormTypePopUp->appendEntry(_("Sine"), kWaveFormTypeSine); + _chorusWaveFormTypePopUp->appendEntry(_("Triangle"), kWaveFormTypeTriangle); + + _tabWidget->addTab(_("Misc")); + + _miscInterpolationPopUpDesc = new StaticTextWidget(_tabWidget, "FluidSynthSettings_Misc.InterpolationText", _("Interpolation:")); + _miscInterpolationPopUp = new PopUpWidget(_tabWidget, "FluidSynthSettings_Misc.Interpolation"); + + _miscInterpolationPopUp->appendEntry(_("None (fastest)"), kInterpolationNone); + _miscInterpolationPopUp->appendEntry(_("Linear"), kInterpolationLinear); + _miscInterpolationPopUp->appendEntry(_("Fourth-order"), kInterpolation4thOrder); + _miscInterpolationPopUp->appendEntry(_("Seventh-order"), kInterpolation7thOrder); + + _tabWidget->setActiveTab(0); + + new ButtonWidget(this, "FluidSynthSettings.ResetSettings", _("Reset"), _("Reset all FluidSynth settings to their default values."), kResetSettingsCmd); + + new ButtonWidget(this, "FluidSynthSettings.Cancel", _("Cancel"), 0, kCloseCmd); + new ButtonWidget(this, "FluidSynthSettings.Ok", _("OK"), 0, kOKCmd); +} + +FluidSynthSettingsDialog::~FluidSynthSettingsDialog() { +} + +void FluidSynthSettingsDialog::open() { + Dialog::open(); + + // Reset result value + setResult(0); + + readSettings(); +} + +void FluidSynthSettingsDialog::close() { + if (getResult()) { + writeSettings(); + } + + Dialog::close(); +} + +void FluidSynthSettingsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + switch (cmd) { + case kActivateChorusCmd: + setChorusSettingsState(data); + break; + case kChorusVoiceCountChangedCmd: + _chorusVoiceCountLabel->setLabel(Common::String::format("%d", _chorusVoiceCountSlider->getValue())); + _chorusVoiceCountLabel->draw(); + break; + case kChorusLevelChangedCmd: + _chorusLevelLabel->setLabel(Common::String::format("%d", _chorusLevelSlider->getValue())); + _chorusLevelLabel->draw(); + break; + case kChorusSpeedChangedCmd: + _chorusSpeedLabel->setLabel(Common::String::format("%d", _chorusSpeedSlider->getValue())); + _chorusSpeedLabel->draw(); + break; + case kChorusDepthChangedCmd: + _chorusDepthLabel->setLabel(Common::String::format("%d", _chorusDepthSlider->getValue())); + _chorusDepthLabel->draw(); + break; + case kActivateReverbCmd: + setReverbSettingsState(data); + break; + case kReverbRoomSizeChangedCmd: + _reverbRoomSizeLabel->setLabel(Common::String::format("%d", _reverbRoomSizeSlider->getValue())); + _reverbRoomSizeLabel->draw(); + break; + case kReverbDampingChangedCmd: + _reverbDampingLabel->setLabel(Common::String::format("%d", _reverbDampingSlider->getValue())); + _reverbDampingLabel->draw(); + break; + case kReverbWidthChangedCmd: + _reverbWidthLabel->setLabel(Common::String::format("%d", _reverbWidthSlider->getValue())); + _reverbWidthLabel->draw(); + break; + case kReverbLevelChangedCmd: + _reverbLevelLabel->setLabel(Common::String::format("%d", _reverbLevelSlider->getValue())); + _reverbLevelLabel->draw(); + break; + case kResetSettingsCmd: { + MessageDialog alert(_("Do you really want to reset all FluidSynth settings to their default values?"), _("Yes"), _("No")); + if (alert.runModal() == GUI::kMessageOK) { + resetSettings(); + readSettings(); + draw(); + } + break; + } + case kOKCmd: + setResult(1); + close(); + break; + default: + Dialog::handleCommand(sender, cmd, data); + break; + } +} + +void FluidSynthSettingsDialog::setChorusSettingsState(bool enabled) { + _chorusVoiceCountDesc->setEnabled(enabled); + _chorusVoiceCountSlider->setEnabled(enabled); + _chorusVoiceCountLabel->setEnabled(enabled); + _chorusLevelDesc->setEnabled(enabled); + _chorusLevelSlider->setEnabled(enabled); + _chorusLevelLabel->setEnabled(enabled); + _chorusSpeedDesc->setEnabled(enabled); + _chorusSpeedSlider->setEnabled(enabled); + _chorusSpeedLabel->setEnabled(enabled); + _chorusDepthDesc->setEnabled(enabled); + _chorusDepthSlider->setEnabled(enabled); + _chorusDepthLabel->setEnabled(enabled); + _chorusWaveFormTypePopUpDesc->setEnabled(enabled); + _chorusWaveFormTypePopUp->setEnabled(enabled); +} + +void FluidSynthSettingsDialog::setReverbSettingsState(bool enabled) { + _reverbRoomSizeDesc->setEnabled(enabled); + _reverbRoomSizeSlider->setEnabled(enabled); + _reverbRoomSizeLabel->setEnabled(enabled); + _reverbDampingDesc->setEnabled(enabled); + _reverbDampingSlider->setEnabled(enabled); + _reverbDampingLabel->setEnabled(enabled); + _reverbWidthDesc->setEnabled(enabled); + _reverbWidthSlider->setEnabled(enabled); + _reverbWidthLabel->setEnabled(enabled); + _reverbLevelDesc->setEnabled(enabled); + _reverbLevelSlider->setEnabled(enabled); + _reverbLevelLabel->setEnabled(enabled); +} + +void FluidSynthSettingsDialog::readSettings() { + _chorusVoiceCountSlider->setValue(ConfMan.getInt("fluidsynth_chorus_nr", _domain)); + _chorusVoiceCountLabel->setLabel(Common::String::format("%d", _chorusVoiceCountSlider->getValue())); + _chorusLevelSlider->setValue(ConfMan.getInt("fluidsynth_chorus_level", _domain)); + _chorusLevelLabel->setLabel(Common::String::format("%d", _chorusLevelSlider->getValue())); + _chorusSpeedSlider->setValue(ConfMan.getInt("fluidsynth_chorus_speed", _domain)); + _chorusSpeedLabel->setLabel(Common::String::format("%d", _chorusSpeedSlider->getValue())); + _chorusDepthSlider->setValue(ConfMan.getInt("fluidsynth_chorus_depth", _domain)); + _chorusDepthLabel->setLabel(Common::String::format("%d", _chorusDepthSlider->getValue())); + + Common::String waveForm = ConfMan.get("fluidsynth_chorus_waveform", _domain); + if (waveForm == "sine") { + _chorusWaveFormTypePopUp->setSelectedTag(kWaveFormTypeSine); + } else if (waveForm == "triangle") { + _chorusWaveFormTypePopUp->setSelectedTag(kWaveFormTypeTriangle); + } + + _reverbRoomSizeSlider->setValue(ConfMan.getInt("fluidsynth_reverb_roomsize", _domain)); + _reverbRoomSizeLabel->setLabel(Common::String::format("%d", _reverbRoomSizeSlider->getValue())); + _reverbDampingSlider->setValue(ConfMan.getInt("fluidsynth_reverb_damping", _domain)); + _reverbDampingLabel->setLabel(Common::String::format("%d", _reverbDampingSlider->getValue())); + _reverbWidthSlider->setValue(ConfMan.getInt("fluidsynth_reverb_width", _domain)); + _reverbWidthLabel->setLabel(Common::String::format("%d", _reverbWidthSlider->getValue())); + _reverbLevelSlider->setValue(ConfMan.getInt("fluidsynth_reverb_level", _domain)); + _reverbLevelLabel->setLabel(Common::String::format("%d", _reverbLevelSlider->getValue())); + + Common::String interpolation = ConfMan.get("fluidsynth_misc_interpolation", _domain); + if (interpolation == "none") { + _miscInterpolationPopUp->setSelectedTag(kInterpolationNone); + } else if (interpolation == "linear") { + _miscInterpolationPopUp->setSelectedTag(kInterpolationLinear); + } else if (interpolation == "4th") { + _miscInterpolationPopUp->setSelectedTag(kInterpolation4thOrder); + } else if (interpolation == "7th") { + _miscInterpolationPopUp->setSelectedTag(kInterpolation7thOrder); + } + + // This may trigger redrawing, so don't do it until all sliders have + // their proper values. Otherwise, the dialog may crash because of + // invalid slider values. + _chorusActivate->setState(ConfMan.getBool("fluidsynth_chorus_activate", _domain)); + _reverbActivate->setState(ConfMan.getBool("fluidsynth_reverb_activate", _domain)); +} + +void FluidSynthSettingsDialog::writeSettings() { + ConfMan.setBool("fluidsynth_chorus_activate", _chorusActivate->getState()); + ConfMan.setInt("fluidsynth_chorus_nr", _chorusVoiceCountSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_chorus_level", _chorusLevelSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_chorus_speed", _chorusSpeedSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_chorus_depth", _chorusDepthSlider->getValue(), _domain); + + uint32 waveForm = _chorusWaveFormTypePopUp->getSelectedTag(); + if (waveForm == kWaveFormTypeSine) { + ConfMan.set("fluidsynth_chorus_waveform", "sine", _domain); + } else if (waveForm == kWaveFormTypeTriangle) { + ConfMan.set("fluidsynth_chorus_waveform", "triangle", _domain); + } else { + ConfMan.removeKey("fluidsynth_chorus_waveform", _domain); + } + + ConfMan.setBool("fluidsynth_reverb_activate", _reverbActivate->getState()); + ConfMan.setInt("fluidsynth_reverb_roomsize", _reverbRoomSizeSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_reverb_damping", _reverbDampingSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_reverb_width", _reverbWidthSlider->getValue(), _domain); + ConfMan.setInt("fluidsynth_reverb_level", _reverbLevelSlider->getValue(), _domain); + + uint32 interpolation = _miscInterpolationPopUp->getSelectedTag(); + if (interpolation == kInterpolationNone) { + ConfMan.set("fluidsynth_misc_interpolation", "none", _domain); + } else if (interpolation == kInterpolationLinear) { + ConfMan.set("fluidsynth_misc_interpolation", "linear", _domain); + } else if (interpolation == kInterpolation4thOrder) { + ConfMan.set("fluidsynth_misc_interpolation", "4th", _domain); + } else if (interpolation == kInterpolation7thOrder) { + ConfMan.set("fluidsynth_misc_interpolation", "7th", _domain); + } else { + ConfMan.removeKey("fluidsynth_misc_interpolation", _domain); + } + + // The main options dialog is responsible for writing the config file. + // That's why we don't actually flush the settings to the file here. +} + +void FluidSynthSettingsDialog::resetSettings() { + ConfMan.removeKey("fluidsynth_chorus_activate", _domain); + ConfMan.removeKey("fluidsynth_chorus_nr", _domain); + ConfMan.removeKey("fluidsynth_chorus_level", _domain); + ConfMan.removeKey("fluidsynth_chorus_speed", _domain); + ConfMan.removeKey("fluidsynth_chorus_depth", _domain); + ConfMan.removeKey("fluidsynth_chorus_waveform", _domain); + + ConfMan.removeKey("fluidsynth_reverb_activate", _domain); + ConfMan.removeKey("fluidsynth_reverb_roomsize", _domain); + ConfMan.removeKey("fluidsynth_reverb_damping", _domain); + ConfMan.removeKey("fluidsynth_reverb_width", _domain); + ConfMan.removeKey("fluidsynth_reverb_level", _domain); + + ConfMan.removeKey("fluidsynth_misc_interpolation", _domain); +} + +} // End of namespace GUI diff --git a/gui/fluidsynth-dialog.h b/gui/fluidsynth-dialog.h new file mode 100644 index 00000000000..4d74c9f93e9 --- /dev/null +++ b/gui/fluidsynth-dialog.h @@ -0,0 +1,104 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FLUIDSYNTH_DIALOG_H +#define FLUIDSYNTH_DIALOG_H + +#include "common/str.h" +#include "gui/dialog.h" + +namespace GUI { + +class TabWidget; +class CheckboxWidget; +class SliderWidget; +class StaticTextWidget; +class PopUpWidget; + +class FluidSynthSettingsDialog : public Dialog { +public: + FluidSynthSettingsDialog(); + ~FluidSynthSettingsDialog(); + + void open(); + void close(); + void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); + +protected: + void setChorusSettingsState(bool enabled); + void setReverbSettingsState(bool enabled); + + void readSettings(); + void writeSettings(); + + void resetSettings(); + +private: + Common::String _domain; + + TabWidget *_tabWidget; + + CheckboxWidget *_chorusActivate; + + StaticTextWidget *_chorusVoiceCountDesc; + SliderWidget *_chorusVoiceCountSlider; + StaticTextWidget *_chorusVoiceCountLabel; + + StaticTextWidget *_chorusLevelDesc; + SliderWidget *_chorusLevelSlider; + StaticTextWidget *_chorusLevelLabel; + + StaticTextWidget *_chorusSpeedDesc; + SliderWidget *_chorusSpeedSlider; + StaticTextWidget *_chorusSpeedLabel; + + StaticTextWidget *_chorusDepthDesc; + SliderWidget *_chorusDepthSlider; + StaticTextWidget *_chorusDepthLabel; + + StaticTextWidget *_chorusWaveFormTypePopUpDesc; + PopUpWidget *_chorusWaveFormTypePopUp; + + CheckboxWidget *_reverbActivate; + + StaticTextWidget *_reverbRoomSizeDesc; + SliderWidget *_reverbRoomSizeSlider; + StaticTextWidget *_reverbRoomSizeLabel; + + StaticTextWidget *_reverbDampingDesc; + SliderWidget *_reverbDampingSlider; + StaticTextWidget *_reverbDampingLabel; + + StaticTextWidget *_reverbWidthDesc; + SliderWidget *_reverbWidthSlider; + StaticTextWidget *_reverbWidthLabel; + + StaticTextWidget *_reverbLevelDesc; + SliderWidget *_reverbLevelSlider; + StaticTextWidget *_reverbLevelLabel; + + StaticTextWidget *_miscInterpolationPopUpDesc; + PopUpWidget *_miscInterpolationPopUp; +}; + +} // End of namespace GUI + +#endif diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp index 995818d53ee..163aeebc92a 100644 --- a/gui/gui-manager.cpp +++ b/gui/gui-manager.cpp @@ -27,6 +27,7 @@ #include "common/rect.h" #include "common/textconsole.h" #include "common/translation.h" +#include "gui/EventRecorder.h" #include "backends/keymapper/keymapper.h" @@ -253,12 +254,13 @@ Dialog *GuiManager::getTopDialog() const { void GuiManager::runLoop() { Dialog * const activeDialog = getTopDialog(); bool didSaveState = false; - int button; - uint32 time; if (activeDialog == 0) return; + // Suspend recording while GUI is shown + g_eventRec.suspendRecording(); + if (!_stateIsSaved) { saveState(); _theme->enable(); @@ -296,10 +298,10 @@ void GuiManager::runLoop() { // _theme->updateScreen(); // _system->updateScreen(); - if (lastRedraw + waitTime < _system->getMillis()) { + if (lastRedraw + waitTime < _system->getMillis(true)) { _theme->updateScreen(); _system->updateScreen(); - lastRedraw = _system->getMillis(); + lastRedraw = _system->getMillis(true); } Common::Event event; @@ -314,72 +316,21 @@ void GuiManager::runLoop() { if (activeDialog != getTopDialog() && event.type != Common::EVENT_SCREEN_CHANGED) continue; - Common::Point mouse(event.mouse.x - activeDialog->_x, event.mouse.y - activeDialog->_y); - - switch (event.type) { - case Common::EVENT_KEYDOWN: - activeDialog->handleKeyDown(event.kbd); - break; - case Common::EVENT_KEYUP: - activeDialog->handleKeyUp(event.kbd); - break; - case Common::EVENT_MOUSEMOVE: - activeDialog->handleMouseMoved(mouse.x, mouse.y, 0); - - if (mouse.x != _lastMousePosition.x || mouse.y != _lastMousePosition.y) { - _lastMousePosition.x = mouse.x; - _lastMousePosition.y = mouse.y; - _lastMousePosition.time = _system->getMillis(); - } + processEvent(event, activeDialog); + if (event.type == Common::EVENT_MOUSEMOVE) { tooltipCheck = true; - break; - // We don't distinguish between mousebuttons (for now at least) - case Common::EVENT_LBUTTONDOWN: - case Common::EVENT_RBUTTONDOWN: - button = (event.type == Common::EVENT_LBUTTONDOWN ? 1 : 2); - time = _system->getMillis(); - if (_lastClick.count && (time < _lastClick.time + kDoubleClickDelay) - && ABS(_lastClick.x - event.mouse.x) < 3 - && ABS(_lastClick.y - event.mouse.y) < 3) { - _lastClick.count++; - } else { - _lastClick.x = event.mouse.x; - _lastClick.y = event.mouse.y; - _lastClick.count = 1; - } - _lastClick.time = time; - activeDialog->handleMouseDown(mouse.x, mouse.y, button, _lastClick.count); - break; - case Common::EVENT_LBUTTONUP: - case Common::EVENT_RBUTTONUP: - button = (event.type == Common::EVENT_LBUTTONUP ? 1 : 2); - activeDialog->handleMouseUp(mouse.x, mouse.y, button, _lastClick.count); - break; - case Common::EVENT_WHEELUP: - activeDialog->handleMouseWheel(mouse.x, mouse.y, -1); - break; - case Common::EVENT_WHEELDOWN: - activeDialog->handleMouseWheel(mouse.x, mouse.y, 1); - break; - case Common::EVENT_SCREEN_CHANGED: - screenChange(); - break; - default: -#ifdef ENABLE_KEYMAPPER - activeDialog->handleOtherEvent(event); -#endif - break; } - if (lastRedraw + waitTime < _system->getMillis()) { + + if (lastRedraw + waitTime < _system->getMillis(true)) { _theme->updateScreen(); _system->updateScreen(); - lastRedraw = _system->getMillis(); + lastRedraw = _system->getMillis(true); } } - if (tooltipCheck && _lastMousePosition.time + kTooltipDelay < _system->getMillis()) { + if (tooltipCheck && _lastMousePosition.time + kTooltipDelay < _system->getMillis(true)) { Widget *wdg = activeDialog->findWidget(_lastMousePosition.x, _lastMousePosition.y); if (wdg && wdg->hasTooltip() && !(wdg->getFlags() & WIDGET_PRESSED)) { Tooltip *tooltip = new Tooltip(); @@ -409,6 +360,9 @@ void GuiManager::runLoop() { restoreState(); _useStdCursor = false; } + + // Resume recording once GUI is shown + g_eventRec.resumeRecording(); } #pragma mark - @@ -492,7 +446,7 @@ void GuiManager::setupCursor() { // very much. We could plug in a different cursor here if we like to. void GuiManager::animateCursor() { - int time = _system->getMillis(); + int time = _system->getMillis(true); if (time > _cursorAnimateTimer + kCursorAnimateDelay) { for (int i = 0; i < 15; i++) { if ((i < 6) || (i > 8)) { @@ -537,4 +491,64 @@ void GuiManager::screenChange() { _system->updateScreen(); } +void GuiManager::processEvent(const Common::Event &event, Dialog *const activeDialog) { + int button; + uint32 time; + Common::Point mouse(event.mouse.x - activeDialog->_x, event.mouse.y - activeDialog->_y); + switch (event.type) { + case Common::EVENT_KEYDOWN: + activeDialog->handleKeyDown(event.kbd); + break; + case Common::EVENT_KEYUP: + activeDialog->handleKeyUp(event.kbd); + break; + case Common::EVENT_MOUSEMOVE: + activeDialog->handleMouseMoved(mouse.x, mouse.y, 0); + + if (mouse.x != _lastMousePosition.x || mouse.y != _lastMousePosition.y) { + _lastMousePosition.x = mouse.x; + _lastMousePosition.y = mouse.y; + _lastMousePosition.time = _system->getMillis(true); + } + + break; + // We don't distinguish between mousebuttons (for now at least) + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_RBUTTONDOWN: + button = (event.type == Common::EVENT_LBUTTONDOWN ? 1 : 2); + time = _system->getMillis(true); + if (_lastClick.count && (time < _lastClick.time + kDoubleClickDelay) + && ABS(_lastClick.x - event.mouse.x) < 3 + && ABS(_lastClick.y - event.mouse.y) < 3) { + _lastClick.count++; + } else { + _lastClick.x = event.mouse.x; + _lastClick.y = event.mouse.y; + _lastClick.count = 1; + } + _lastClick.time = time; + activeDialog->handleMouseDown(mouse.x, mouse.y, button, _lastClick.count); + break; + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONUP: + button = (event.type == Common::EVENT_LBUTTONUP ? 1 : 2); + activeDialog->handleMouseUp(mouse.x, mouse.y, button, _lastClick.count); + break; + case Common::EVENT_WHEELUP: + activeDialog->handleMouseWheel(mouse.x, mouse.y, -1); + break; + case Common::EVENT_WHEELDOWN: + activeDialog->handleMouseWheel(mouse.x, mouse.y, 1); + break; + case Common::EVENT_SCREEN_CHANGED: + screenChange(); + break; + default: + #ifdef ENABLE_KEYMAPPER + activeDialog->handleOtherEvent(event); + #endif + break; + } +} + } // End of namespace GUI diff --git a/gui/gui-manager.h b/gui/gui-manager.h index 49542fd0018..b52d91ba087 100644 --- a/gui/gui-manager.h +++ b/gui/gui-manager.h @@ -35,6 +35,10 @@ namespace Graphics { class Font; } +namespace Common { + struct Event; +} + namespace GUI { class Dialog; @@ -67,6 +71,8 @@ public: // until no dialogs are active anymore. void runLoop(); + void processEvent(const Common::Event &event, Dialog *const activeDialog); + bool isActive() const { return ! _dialogStack.empty(); } bool loadNewTheme(Common::String id, ThemeEngine::GraphicsMode gfx = ThemeEngine::kGfxDisabled, bool force = false); diff --git a/gui/launcher.cpp b/gui/launcher.cpp index a43f50e9ec2..a14c7aec9e4 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -37,6 +37,11 @@ #include "gui/message.h" #include "gui/gui-manager.h" #include "gui/options.h" +#ifdef ENABLE_EVENTRECORDER +#include "gui/onscreendialog.h" +#include "gui/recorderdialog.h" +#include "gui/EventRecorder.h" +#endif #include "gui/saveload.h" #include "gui/widgets/edittext.h" #include "gui/widgets/list.h" @@ -381,7 +386,7 @@ void EditGameDialog::open() { ConfMan.hasKey("sfx_volume", _domain) || ConfMan.hasKey("speech_volume", _domain); _globalVolumeOverride->setState(e); -/* +/* ResiduealVM: disable midi if (!_guioptions.contains(GUIO_NOMIDI)) { e = ConfMan.hasKey("soundfont", _domain) || ConfMan.hasKey("multi_midi", _domain) || @@ -596,7 +601,6 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat LauncherDialog::LauncherDialog() : Dialog(0, 0, 640, 400) {//ResidualVM specific _backgroundType = GUI::ThemeEngine::kDialogBackgroundMain; - const int screenW = g_system->getOverlayWidth(); const int screenH = g_system->getOverlayHeight(); @@ -779,10 +783,9 @@ void LauncherDialog::updateListing() { } void LauncherDialog::addGame() { - int modifiers = g_system->getEventManager()->getModifierState(); #ifndef DISABLE_MASS_ADD - const bool massAdd = (modifiers & Common::KBD_SHIFT) != 0; + const bool massAdd = checkModifier(Common::KBD_SHIFT); if (massAdd) { MessageDialog alert(_("Do you really want to run the mass game detector? " @@ -975,6 +978,49 @@ void LauncherDialog::editGame(int item) { } } +void LauncherDialog::loadGameButtonPressed(int item) { +#ifdef ENABLE_EVENTRECORDER + const bool shiftPressed = checkModifier(Common::KBD_SHIFT); + if (shiftPressed) { + recordGame(item); + } else { + loadGame(item); + } + updateButtons(); +#else + loadGame(item); +#endif +} + +#ifdef ENABLE_EVENTRECORDER +void LauncherDialog::recordGame(int item) { + RecorderDialog recorderDialog; + MessageDialog alert(_("Do you want to load savegame?"), + _("Yes"), _("No")); + switch(recorderDialog.runModal(_domains[item])) { + case RecorderDialog::kRecordDialogClose: + break; + case RecorderDialog::kRecordDialogPlayback: + ConfMan.setActiveDomain(_domains[item]); + close(); + ConfMan.set("record_mode", "playback", ConfigManager::kTransientDomain); + ConfMan.set("record_file_name", recorderDialog.getFileName(), ConfigManager::kTransientDomain); + break; + case RecorderDialog::kRecordDialogRecord: + ConfMan.setActiveDomain(_domains[item]); + if (alert.runModal() == GUI::kMessageOK) { + loadGame(item); + } + close(); + g_eventRec.setAuthor(recorderDialog._author); + g_eventRec.setName(recorderDialog._name); + g_eventRec.setNotes(recorderDialog._notes); + ConfMan.set("record_mode", "record", ConfigManager::kTransientDomain); + break; + } +} +#endif + void LauncherDialog::loadGame(int item) { String gameId = ConfMan.get("gameid", _domains[item]); if (gameId.empty()) @@ -1039,7 +1085,7 @@ void LauncherDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat editGame(item); break; case kLoadGameCmd: - loadGame(item); + loadGameButtonPressed(item); break; case kOptionsCmd: { GlobalOptionsDialog options; @@ -1109,26 +1155,34 @@ void LauncherDialog::updateButtons() { _loadButton->setEnabled(en); _loadButton->draw(); } + switchButtonsText(_addButton, "~A~dd Game...", "Mass Add..."); +#ifdef ENABLE_EVENTRECORDER + switchButtonsText(_loadButton, "~L~oad...", "Record..."); +#endif +} - // Update the label of the "Add" button depending on whether shift is pressed or not - int modifiers = g_system->getEventManager()->getModifierState(); - const bool massAdd = (modifiers & Common::KBD_SHIFT) != 0; +// Update the label of the button depending on whether shift is pressed or not +void LauncherDialog::switchButtonsText(ButtonWidget *button, const char *normalText, const char *shiftedText) { + const bool shiftPressed = checkModifier(Common::KBD_SHIFT); const bool lowRes = g_system->getOverlayWidth() <= 320; - const char *newAddButtonLabel = massAdd - ? (lowRes ? _c("Mass Add...", "lowres") : _("Mass Add...")) - : (lowRes ? _c("~A~dd Game...", "lowres") : _("~A~dd Game...")); + const char *newAddButtonLabel = shiftPressed + ? (lowRes ? _c(shiftedText, "lowres") : _(shiftedText)) + : (lowRes ? _c(normalText, "lowres") : _(normalText)); - if (_addButton->getLabel() != newAddButtonLabel) - _addButton->setLabel(newAddButtonLabel); + if (button->getLabel() != newAddButtonLabel) + button->setLabel(newAddButtonLabel); } + + + void LauncherDialog::reflowLayout() { #ifndef DISABLE_FANCY_THEMES if (g_gui.xmlEval()->getVar("Globals.ShowLauncherLogo") == 1 && g_gui.theme()->supportsImages()) { StaticTextWidget *ver = (StaticTextWidget *)findWidget("Launcher.Version"); if (ver) { - ver->setAlign((Graphics::TextAlign)g_gui.xmlEval()->getVar("Launcher.Version.Align", Graphics::kTextAlignCenter)); + ver->setAlign(g_gui.xmlEval()->getWidgetTextHAlign("Launcher.Version")); ver->setLabel(gScummVMVersionDate); } @@ -1139,7 +1193,7 @@ void LauncherDialog::reflowLayout() { } else { StaticTextWidget *ver = (StaticTextWidget *)findWidget("Launcher.Version"); if (ver) { - ver->setAlign((Graphics::TextAlign)g_gui.xmlEval()->getVar("Launcher.Version.Align", Graphics::kTextAlignCenter)); + ver->setAlign(g_gui.xmlEval()->getWidgetTextHAlign("Launcher.Version")); ver->setLabel(gScummVMFullVersion); } @@ -1186,4 +1240,9 @@ void LauncherDialog::reflowLayout() { Dialog::reflowLayout(); } +bool LauncherDialog::checkModifier(int checkedModifier) { + int modifiers = g_system->getEventManager()->getModifierState(); + return (modifiers & checkedModifier) != 0; +} + } // End of namespace GUI diff --git a/gui/launcher.h b/gui/launcher.h index fc0484350a9..2ab47be98dc 100644 --- a/gui/launcher.h +++ b/gui/launcher.h @@ -56,7 +56,7 @@ protected: ListWidget *_list; ButtonWidget *_addButton; Widget *_startButton; - Widget *_loadButton; + ButtonWidget *_loadButton; Widget *_editButton; Widget *_removeButton; #ifndef DISABLE_FANCY_THEMES @@ -80,6 +80,7 @@ protected: void updateListing(); void updateButtons(); + void switchButtonsText(ButtonWidget *button, const char *normalText, const char *shiftedText); void open(); void close(); @@ -99,6 +100,16 @@ protected: */ void editGame(int item); + /** + * Facade for "Load..."/"Record..." buttons. + */ + void loadGameButtonPressed(int item); + + /** + * Handle "Record..." button. + */ + void recordGame(int item); + /** * Handle "Load..." button. */ @@ -111,6 +122,8 @@ protected: * @target name of target to select */ void selectTarget(const String &target); +private: + bool checkModifier(int modifier); }; } // End of namespace GUI diff --git a/gui/module.mk b/gui/module.mk index a435d8cca7b..338e43c6a4b 100644 --- a/gui/module.mk +++ b/gui/module.mk @@ -7,6 +7,7 @@ MODULE_OBJS := \ debugger.o \ dialog.o \ error.o \ + EventRecorder.o \ gui-manager.o \ launcher.o \ massadd.o \ @@ -38,5 +39,17 @@ MODULE_OBJS += \ browser.o endif +ifdef ENABLE_EVENTRECORDER +MODULE_OBJS += \ + editrecorddialog.o \ + onscreendialog.o \ + recorderdialog.o +endif + +ifdef USE_FLUIDSYNTH +MODULE_OBJS += \ + fluidsynth-dialog.o +endif + # Include common rules include $(srcdir)/rules.mk diff --git a/gui/onscreendialog.cpp b/gui/onscreendialog.cpp new file mode 100644 index 00000000000..efe8038e689 --- /dev/null +++ b/gui/onscreendialog.cpp @@ -0,0 +1,231 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" + +#include "gui/gui-manager.h" + +#include "gui/EventRecorder.h" + +#include "common/events.h" +#include "common/rect.h" +#include "common/translation.h" + +#include "graphics/cursorman.h" + +#include "gui/editrecorddialog.h" +#include "gui/ThemeEval.h" + +#include "gui/onscreendialog.h" + +namespace GUI { + +bool OnScreenDialog::isVisible() const { + return true; +} + +enum { + kStopCmd = 'STOP', + kEditCmd = 'EDIT', + kSwitchModeCmd = 'MODE', + kFastModeCmd = 'FAST' +}; + +void OnScreenDialog::reflowLayout() { + GuiObject::reflowLayout(); +} + +void OnScreenDialog::releaseFocus() { +} + +OnScreenDialog::OnScreenDialog(bool isRecord) : Dialog("OnScreenDialog") { + _x = _y = 0; + +#ifndef DISABLE_FANCY_THEMES + if (g_gui.xmlEval()->getVar("Globals.OnScreenDialog.ShowPics") == 1 && g_gui.theme()->supportsImages()) { + GUI::PicButtonWidget *btn; + btn = new PicButtonWidget(this, "OnScreenDialog.StopButton", 0, kStopCmd, 0); + btn->useThemeTransparency(true); + + if (g_system->getOverlayWidth() > 320) + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageStopbtn)); + else + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageStopSmallbtn)); + + if (isRecord) { + btn = new PicButtonWidget(this, "OnScreenDialog.EditButton", 0, kEditCmd, 0); + btn->useThemeTransparency(true); + + if (g_system->getOverlayWidth() > 320) + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageEditbtn)); + else + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageEditSmallbtn)); + } else { + btn = new PicButtonWidget(this, "OnScreenDialog.SwitchModeButton", 0, kSwitchModeCmd, 0); + btn->useThemeTransparency(true); + if (g_system->getOverlayWidth() > 320) + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageSwitchModebtn)); + else + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageSwitchModeSmallbtn)); + + btn = new PicButtonWidget(this, "OnScreenDialog.FastReplayButton", 0, kFastModeCmd, 0); + btn->useThemeTransparency(true); + if (g_system->getOverlayWidth() > 320) + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageFastReplaybtn)); + else + btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageFastReplaySmallbtn)); + } + } else +#endif + { + GUI::ButtonWidget *btn; + if (g_system->getOverlayWidth() > 320) + btn = new ButtonWidget(this, "OnScreenDialog.StopButton", "[ ]", _("Stop"), kStopCmd); + else + btn = new ButtonWidget(this, "OnScreenDialog.StopButton", "[]", _("Stop"), kStopCmd); + + if (isRecord) { + btn = new ButtonWidget(this, "OnScreenDialog.EditButton", "E", _("Edit record description"), kEditCmd); + } else { + btn = new ButtonWidget(this, "OnScreenDialog.SwitchModeButton", "G", _("Switch to Game"), kSwitchModeCmd); + + btn = new ButtonWidget(this, "OnScreenDialog.FastReplayButton", ">>", _("Fast replay"), kFastModeCmd); + } + } + + + text = new GUI::StaticTextWidget(this, "OnScreenDialog.TimeLabel", "00:00:00"); + _enableDrag = false; + _mouseOver = false; + _editDlgShown = false; +} + +void OnScreenDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + Common::Event eventRTL; + switch (cmd) { + case kStopCmd: + eventRTL.type = Common::EVENT_RTL; + g_system->getEventManager()->pushEvent(eventRTL); + close(); + break; + case kEditCmd: + dlg = new EditRecordDialog(g_eventRec.getAuthor(), g_eventRec.getName(), g_eventRec.getNotes()); + CursorMan.lock(false); + g_eventRec.setRedraw(false); + g_system->showOverlay(); + _editDlgShown = true; + dlg->runModal(); + _editDlgShown = false; + g_system->hideOverlay(); + g_eventRec.setRedraw(true); + CursorMan.lock(true); + g_eventRec.setAuthor(((EditRecordDialog *)dlg)->getAuthor()); + g_eventRec.setName(((EditRecordDialog *)dlg)->getName()); + g_eventRec.setNotes(((EditRecordDialog *)dlg)->getNotes()); + delete dlg; + break; + case kSwitchModeCmd: + if (g_eventRec.switchMode()) { + close(); + } + break; + case kFastModeCmd: + g_eventRec.switchFastMode(); + break; + } +} + +void OnScreenDialog::setReplayedTime(uint32 newTime) { + if (newTime - lastTime > 1000) { + uint32 seconds = newTime / 1000; + text->setLabel(Common::String::format("%.2d:%.2d:%.2d", seconds / 3600 % 24, seconds / 60 % 60, seconds % 60)); + lastTime = newTime; + } +} + +OnScreenDialog::~OnScreenDialog() { +} + +void OnScreenDialog::handleMouseMoved(int x, int y, int button) { + if (_enableDrag) { + _x = _x + x - _dragPoint.x; + _y = _y + y - _dragPoint.y; + } + Dialog::handleMouseMoved(x, y, button); + if (isMouseOver(x, y)) { + if (_mouseOver == false) { + g_gui.theme()->showCursor(); + CursorMan.lock(true); + } + _mouseOver = true; + } else { + if (_mouseOver == true) { + CursorMan.lock(false); + g_gui.theme()->hideCursor(); + } + _mouseOver = false; + } +} + +void OnScreenDialog::handleMouseDown(int x, int y, int button, int clickCount) { + if (isMouseOver(x, y)) { + _dragPoint.x = x; + _dragPoint.y = y; + _enableDrag = true; + } + Dialog::handleMouseDown(x, y, button, clickCount); +} + +void OnScreenDialog::handleMouseUp(int x, int y, int button, int clickCount) { + if (isMouseOver(x, y)) { + + } + _enableDrag = false; + Dialog::handleMouseUp(x, y, button, clickCount); +} + +bool OnScreenDialog::isMouseOver(int x, int y) { + return (x >= 0 && x < _w && y >= 0 && y < _h); +} + +bool OnScreenDialog::isMouseOver() { + return _mouseOver; +} + +void OnScreenDialog::close() { + CursorMan.lock(false); + Dialog::close(); +} + +Dialog *OnScreenDialog::getActiveDlg() { + if (_editDlgShown) { + return dlg; + } else { + return this; + } +} + +bool OnScreenDialog::isEditDlgVisible() { + return _editDlgShown; +} + +} diff --git a/gui/onscreendialog.h b/gui/onscreendialog.h new file mode 100644 index 00000000000..4f3839acb6e --- /dev/null +++ b/gui/onscreendialog.h @@ -0,0 +1,64 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GUI_ONSCREENDIALOG_H +#define GUI_ONSCREENDIALOG_H + +#include "gui/dialog.h" +#include "gui/widget.h" + +namespace GUI { + +class OnScreenDialog : public Dialog { +private: + uint32 lastTime; + bool _enableDrag; + bool _mouseOver; + bool _editDlgShown; + Common::Point _dragPoint; + GUI::StaticTextWidget *text; + Dialog *dlg; + bool isMouseOver(int x, int y); +public: + OnScreenDialog(bool recordingMode); + ~OnScreenDialog(); + virtual void close(); + virtual bool isVisible() const; + virtual void reflowLayout(); + + void setReplayedTime(uint32 newTime); + + virtual void handleMouseMoved(int x, int y, int button); + virtual void handleMouseDown(int x, int y, int button, int clickCount); + virtual void handleMouseUp(int x, int y, int button, int clickCount); + void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); + + bool isMouseOver(); + bool isEditDlgVisible(); + Dialog *getActiveDlg(); +protected: + virtual void releaseFocus(); +}; + +} // End of namespace GUI + +#endif diff --git a/gui/options.cpp b/gui/options.cpp index 659e9373326..74521eec0cf 100644 --- a/gui/options.cpp +++ b/gui/options.cpp @@ -75,6 +75,12 @@ enum { }; #endif +#ifdef USE_FLUIDSYNTH +enum { + kFluidSynthSettingsCmd = 'flst' +}; +#endif + static const char *savePeriodLabels[] = { _s("Never"), _s("every 5 mins"), _s("every 10 mins"), _s("every 15 mins"), _s("every 30 mins"), 0 }; static const int savePeriodValues[] = { 0, 5 * 60, 10 * 60, 15 * 60, 30 * 60, -1 }; static const char *outputRateLabels[] = { _s(""), _s("8 kHz"), _s("11kHz"), _s("22 kHz"), _s("44 kHz"), _s("48 kHz"), 0 }; @@ -102,7 +108,7 @@ void OptionsDialog::init() { _renderModePopUpDesc = 0; _fullscreenCheckbox = 0; _aspectCheckbox = 0; - _softwareRenderingCheckbox = 0; + _softwareRenderingCheckbox = 0; // ResidualVM specific _enableAudioSettings = false; _midiPopUp = 0; _midiPopUpDesc = 0; @@ -169,15 +175,60 @@ void OptionsDialog::open() { // Graphic options if (_fullscreenCheckbox) { +#if 0 // ResidualVM specific + _gfxPopUp->setSelected(0); + + if (ConfMan.hasKey("gfx_mode", _domain)) { + const OSystem::GraphicsMode *gm = g_system->getSupportedGraphicsModes(); + Common::String gfxMode(ConfMan.get("gfx_mode", _domain)); + int gfxCount = 1; + while (gm->name) { + gfxCount++; + + if (scumm_stricmp(gm->name, gfxMode.c_str()) == 0) + _gfxPopUp->setSelected(gfxCount); + + gm++; + } + } + + _renderModePopUp->setSelected(0); + + if (ConfMan.hasKey("render_mode", _domain)) { + const Common::RenderModeDescription *p = Common::g_renderModes; + const Common::RenderMode renderMode = Common::parseRenderMode(ConfMan.get("render_mode", _domain)); + int sel = 0; + for (int i = 0; p->code; ++p, ++i) { + if (renderMode == p->id) + sel = p->id; + } + _renderModePopUp->setSelectedTag(sel); + } +#endif #ifdef SMALL_SCREEN_DEVICE _fullscreenCheckbox->setState(true); _fullscreenCheckbox->setEnabled(false); +#if 0 // ResidualVM specific + _aspectCheckbox->setState(false); + _aspectCheckbox->setEnabled(false); +#endif #else // !SMALL_SCREEN_DEVICE // Fullscreen setting _fullscreenCheckbox->setState(ConfMan.getBool("fullscreen", _domain)); + +#if 0 // ResidualVM specific + // Aspect ratio setting + if (_guioptions.contains(GUIO_NOASPECT)) { + _aspectCheckbox->setState(false); + _aspectCheckbox->setEnabled(false); + } else { + _aspectCheckbox->setEnabled(true); + _aspectCheckbox->setState(ConfMan.getBool("aspect_ratio", _domain)); + } +#endif #endif // SMALL_SCREEN_DEVICE - // Software rendering setting - ResidualVM specific + // Software rendering setting - ResidualVM specific lines _softwareRenderingCheckbox->setEnabled(true); _softwareRenderingCheckbox->setState(ConfMan.getBool("soft_renderer", _domain)); } @@ -283,10 +334,47 @@ void OptionsDialog::close() { if (_enableGraphicSettings) { if (ConfMan.getBool("fullscreen", _domain) != _fullscreenCheckbox->getState()) graphicsModeChanged = true; +#if 0 // ResidualVM specific + if (ConfMan.getBool("aspect_ratio", _domain) != _aspectCheckbox->getState()) + graphicsModeChanged = true; +#endif ConfMan.setBool("fullscreen", _fullscreenCheckbox->getState(), _domain); +#if 0 // ResidualVM specific + ConfMan.setBool("aspect_ratio", _aspectCheckbox->getState(), _domain); + + bool isSet = false; + + if ((int32)_gfxPopUp->getSelectedTag() >= 0) { + const OSystem::GraphicsMode *gm = g_system->getSupportedGraphicsModes(); + + while (gm->name) { + if (gm->id == (int)_gfxPopUp->getSelectedTag()) { + if (ConfMan.get("gfx_mode", _domain) != gm->name) + graphicsModeChanged = true; + ConfMan.set("gfx_mode", gm->name, _domain); + isSet = true; + break; + } + gm++; + } + } + if (!isSet) + ConfMan.removeKey("gfx_mode", _domain); + + if ((int32)_renderModePopUp->getSelectedTag() >= 0) + ConfMan.set("render_mode", Common::getRenderModeCode((Common::RenderMode)_renderModePopUp->getSelectedTag()), _domain); +#endif + +// ResidualVM specific ConfMan.setBool("soft_renderer", _softwareRenderingCheckbox->getState(), _domain); } else { ConfMan.removeKey("fullscreen", _domain); +#if 0 // ResidualVM specific + ConfMan.removeKey("aspect_ratio", _domain); + ConfMan.removeKey("gfx_mode", _domain); + ConfMan.removeKey("render_mode", _domain); +#endif +// ResidualVM specific ConfMan.removeKey("soft_renderer", _domain); } } @@ -532,9 +620,22 @@ void OptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data void OptionsDialog::setGraphicSettingsState(bool enabled) { _enableGraphicSettings = enabled; +#if 0 // ResidualVM specific + _gfxPopUpDesc->setEnabled(enabled); + _gfxPopUp->setEnabled(enabled); + _renderModePopUpDesc->setEnabled(enabled); + _renderModePopUp->setEnabled(enabled); +#endif #ifndef SMALL_SCREEN_DEVICE _fullscreenCheckbox->setEnabled(enabled); +#if 0 // ResidualVM specific + if (_guioptions.contains(GUIO_NOASPECT)) + _aspectCheckbox->setEnabled(false); + else + _aspectCheckbox->setEnabled(enabled); #endif +#endif +// ResidualVM specific: if (enabled) _softwareRenderingCheckbox->setEnabled(true); else @@ -654,9 +755,46 @@ void OptionsDialog::setSubtitleSettingsState(bool enabled) { } void OptionsDialog::addGraphicControls(GuiObject *boss, const Common::String &prefix) { +#if 0 // ResidualVM specific + const OSystem::GraphicsMode *gm = g_system->getSupportedGraphicsModes(); + Common::String context; + if (g_system->getOverlayWidth() <= 320) + context = "lowres"; + + // The GFX mode popup + _gfxPopUpDesc = new StaticTextWidget(boss, prefix + "grModePopupDesc", _("Graphics mode:")); + _gfxPopUp = new PopUpWidget(boss, prefix + "grModePopup"); + + _gfxPopUp->appendEntry(_("")); + _gfxPopUp->appendEntry(""); + while (gm->name) { + _gfxPopUp->appendEntry(_c(gm->description, context), gm->id); + gm++; + } + + // RenderMode popup + const Common::String allFlags = Common::allRenderModesGUIOs(); + bool renderingTypeDefined = (strpbrk(_guioptions.c_str(), allFlags.c_str()) != NULL); + + _renderModePopUpDesc = new StaticTextWidget(boss, prefix + "grRenderPopupDesc", _("Render mode:"), _("Special dithering modes supported by some games")); + _renderModePopUp = new PopUpWidget(boss, prefix + "grRenderPopup", _("Special dithering modes supported by some games")); + _renderModePopUp->appendEntry(_(""), Common::kRenderDefault); + _renderModePopUp->appendEntry(""); + const Common::RenderModeDescription *rm = Common::g_renderModes; + for (; rm->code; ++rm) { + Common::String renderGuiOption = Common::renderMode2GUIO(rm->id); + if ((_domain == Common::ConfigManager::kApplicationDomain) || (_domain != Common::ConfigManager::kApplicationDomain && !renderingTypeDefined) || (_guioptions.contains(renderGuiOption))) + _renderModePopUp->appendEntry(_c(rm->description, context), rm->id); + } +#endif // Fullscreen checkbox _fullscreenCheckbox = new CheckboxWidget(boss, prefix + "grFullscreenCheckbox", _("Fullscreen mode")); +#if 0 // ResidualVM specific + // Aspect ratio checkbox + _aspectCheckbox = new CheckboxWidget(boss, prefix + "grAspectCheckbox", _("Aspect ratio correction"), _("Correct aspect ratio for 320x200 games")); +#endif +// ResidualVM specific option: _softwareRenderingCheckbox = new CheckboxWidget(boss, prefix + "grSoftwareRendering", _("Software Rendering"), _("Enable software rendering")); _enableGraphicSettings = true; } @@ -768,6 +906,10 @@ void OptionsDialog::addMIDIControls(GuiObject *boss, const Common::String &prefi _midiGainSlider->setMaxValue(1000); _midiGainLabel = new StaticTextWidget(boss, prefix + "mcMidiGainLabel", "1.00"); +#ifdef USE_FLUIDSYNTH + new ButtonWidget(boss, prefix + "mcFluidSynthSettings", _("FluidSynth Settings"), 0, kFluidSynthSettingsCmd); +#endif + _enableMIDISettings = true; } @@ -783,7 +925,7 @@ void OptionsDialog::addMT32Controls(GuiObject *boss, const Common::String &prefi _mt32Checkbox = new CheckboxWidget(boss, prefix + "mcMt32Checkbox", _c("True Roland MT-32 (no GM emulation)", "lowres"), _("Check if you want to use your real hardware Roland-compatible sound device connected to your computer")); // GS Extensions setting - _enableGSCheckbox = new CheckboxWidget(boss, prefix + "mcGSCheckbox", _("Enable Roland GS Mode"), _("Turns off General MIDI mapping for games with Roland MT-32 soundtrack")); + _enableGSCheckbox = new CheckboxWidget(boss, prefix + "mcGSCheckbox", _("Roland GS Device (enable MT-32 mappings)"), _("Check if you want to enable patch mappings to emulate an MT-32 on a Roland GS device")); const MusicPlugin::List p = MusicMan.getPlugins(); // Make sure the null device is the first one in the list to avoid undesired @@ -1137,12 +1279,20 @@ GlobalOptionsDialog::GlobalOptionsDialog() #ifdef SMALL_SCREEN_DEVICE _keysDialog = new KeysDialog(); #endif + +#ifdef USE_FLUIDSYNTH + _fluidSynthSettingsDialog = new FluidSynthSettingsDialog(); +#endif } GlobalOptionsDialog::~GlobalOptionsDialog() { #ifdef SMALL_SCREEN_DEVICE delete _keysDialog; #endif + +#ifdef USE_FLUIDSYNTH + delete _fluidSynthSettingsDialog; +#endif } void GlobalOptionsDialog::open() { @@ -1371,6 +1521,11 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3 case kChooseKeyMappingCmd: _keysDialog->runModal(); break; +#endif +#ifdef USE_FLUIDSYNTH + case kFluidSynthSettingsCmd: + _fluidSynthSettingsDialog->runModal(); + break; #endif default: OptionsDialog::handleCommand(sender, cmd, data); @@ -1382,11 +1537,12 @@ void GlobalOptionsDialog::reflowLayout() { if (_midiTabId != -1) { _tabWidget->setActiveTab(_midiTabId); -/* Residual do not use it +#if 0 // Residual do not use it _tabWidget->removeWidget(_soundFontClearButton); _soundFontClearButton->setNext(0); delete _soundFontClearButton; - _soundFontClearButton = addClearButton(_tabWidget, "GlobalOptions_MIDI.mcFontClearButton", kClearSoundFontCmd);*/ + _soundFontClearButton = addClearButton(_tabWidget, "GlobalOptions_MIDI.mcFontClearButton", kClearSoundFontCmd); +#endif } if (_pathsTabId != -1) { diff --git a/gui/options.h b/gui/options.h index 0bc17f6b3c2..2ee7beee820 100644 --- a/gui/options.h +++ b/gui/options.h @@ -32,6 +32,10 @@ #include "gui/KeysDialog.h" #endif +#ifdef USE_FLUIDSYNTH +#include "gui/fluidsynth-dialog.h" +#endif + namespace GUI { class CheckboxWidget; @@ -104,7 +108,7 @@ private: PopUpWidget *_gfxPopUp; CheckboxWidget *_fullscreenCheckbox; CheckboxWidget *_aspectCheckbox; - CheckboxWidget *_softwareRenderingCheckbox; + CheckboxWidget *_softwareRenderingCheckbox; // ResidualVM specific StaticTextWidget *_renderModePopUpDesc; PopUpWidget *_renderModePopUp; @@ -208,6 +212,9 @@ public: protected: #ifdef SMALL_SCREEN_DEVICE KeysDialog *_keysDialog; +#endif +#ifdef USE_FLUIDSYNTH + FluidSynthSettingsDialog *_fluidSynthSettingsDialog; #endif StaticTextWidget *_savePath; ButtonWidget *_savePathClearButton; diff --git a/gui/predictivedialog.cpp b/gui/predictivedialog.cpp index ed18847a40c..5ce093e054f 100644 --- a/gui/predictivedialog.cpp +++ b/gui/predictivedialog.cpp @@ -779,7 +779,7 @@ bool PredictiveDialog::searchWord(const char *const where, const Common::String } void PredictiveDialog::addWord(Dict &dict, const Common::String &word, const Common::String &code) { - char *newLine; + char *newLine = 0; Common::String tmpCode = code + ' '; int line = binarySearch(dict.dictLine, tmpCode, dict.dictLineCount); if (line >= 0) { @@ -856,6 +856,9 @@ void PredictiveDialog::addWord(Dict &dict, const Common::String &word, const Com char **newDictLine = (char **)calloc(1, sizeof(char *) * (dict.dictLineCount + 1)); if (!newDictLine) { warning("Predictive Dialog: cannot allocate memory for index buffer"); + + free(newLine); + return; } newDictLine[dict.dictLineCount] = '\0'; diff --git a/gui/recorderdialog.cpp b/gui/recorderdialog.cpp new file mode 100644 index 00000000000..55f342d4a1d --- /dev/null +++ b/gui/recorderdialog.cpp @@ -0,0 +1,291 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "common/algorithm.h" +#include "common/bufferedstream.h" +#include "common/savefile.h" +#include "common/system.h" +#include "graphics/colormasks.h" +#include "graphics/palette.h" +#include "graphics/scaler.h" +#include "graphics/thumbnail.h" +#include "common/translation.h" +#include "gui/widgets/list.h" +#include "gui/editrecorddialog.h" +#include "gui/EventRecorder.h" +#include "gui/message.h" +#include "gui/saveload.h" +#include "common/system.h" +#include "gui/ThemeEval.h" +#include "gui/gui-manager.h" +#include "recorderdialog.h" + +#define MAX_RECORDS_NAMES 0xFF + +namespace GUI { + +enum { + kRecordCmd = 'RCRD', + kPlaybackCmd = 'PBCK', + kDeleteCmd = 'DEL ', + kNextScreenshotCmd = 'NEXT', + kPrevScreenshotCmd = 'PREV', + kEditRecordCmd = 'EDIT' +}; + +RecorderDialog::RecorderDialog() : Dialog("RecorderDialog"), _list(0), _currentScreenshot(0) { + _backgroundType = ThemeEngine::kDialogBackgroundSpecial; + + new StaticTextWidget(this, "SaveLoadChooser.Title", _("Recorder or Playback Gameplay")); + + _list = new GUI::ListWidget(this, "RecorderDialog.List"); + _list->setNumberingMode(GUI::kListNumberingOff); + + _deleteButton = new GUI::ButtonWidget(this, "RecorderDialog.Delete", _("Delete"), 0, kDeleteCmd); + new GUI::ButtonWidget(this, "RecorderDialog.Cancel", _("Cancel"), 0, kCloseCmd); + new GUI::ButtonWidget(this, "RecorderDialog.Record", _("Record"), 0, kRecordCmd); + _playbackButton = new GUI::ButtonWidget(this, "RecorderDialog.Playback", _("Playback"), 0, kPlaybackCmd); + + _editButton = new GUI::ButtonWidget(this, "RecorderDialog.Edit", _("Edit"), 0, kEditRecordCmd); + + _editButton->setEnabled(false); + _deleteButton->setEnabled(false); + _playbackButton->setEnabled(false); + + _gfxWidget = new GUI::GraphicsWidget(this, 0, 0, 10, 10); + _container = new GUI::ContainerWidget(this, 0, 0, 10, 10); + if (g_gui.xmlEval()->getVar("Globals.RecorderDialog.ExtInfo.Visible") == 1) { + new GUI::ButtonWidget(this,"RecorderDialog.NextScreenShotButton", "<", 0, kPrevScreenshotCmd); + new GUI::ButtonWidget(this, "RecorderDialog.PreviousScreenShotButton", ">", 0, kNextScreenshotCmd); + _currentScreenshotText = new StaticTextWidget(this, "RecorderDialog.currentScreenshot", "0/0"); + _authorText = new StaticTextWidget(this, "RecorderDialog.Author", _("Author: ")); + _notesText = new StaticTextWidget(this, "RecorderDialog.Notes", _("Notes: ")); + } + if (_gfxWidget) + _gfxWidget->setGfx(0); +} + + +void RecorderDialog::reflowLayout() { + if (g_gui.xmlEval()->getVar("Globals.RecorderDialog.ExtInfo.Visible") == 1) { + int16 x, y; + uint16 w, h; + + if (!g_gui.xmlEval()->getWidgetData("RecorderDialog.Thumbnail", x, y, w, h)) { + error("Error when loading position data for Recorder Thumbnails"); + } + + int thumbW = kThumbnailWidth; + int thumbH = kThumbnailHeight2; + int thumbX = x + (w >> 1) - (thumbW >> 1); + int thumbY = y + kLineHeight; + + _container->resize(x, y, w, h); + _gfxWidget->resize(thumbX, thumbY, thumbW, thumbH); + + _container->setVisible(true); + _gfxWidget->setVisible(true); + updateSelection(false); + } else { + _container->setVisible(false); + _gfxWidget->setVisible(false); + } + Dialog::reflowLayout(); +} + + + +void RecorderDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + switch(cmd) { + case kEditRecordCmd: { + if (_list->getSelected() >= 0) { + EditRecordDialog editDlg(_fileHeaders[_list->getSelected()].author, _fileHeaders[_list->getSelected()].name, _fileHeaders[_list->getSelected()].notes); + if (editDlg.runModal() != kOKCmd) { + return; + } + _playbackFile.openRead(_fileHeaders[_list->getSelected()].fileName); + _playbackFile.getHeader().author = editDlg.getAuthor(); + _playbackFile.getHeader().name = editDlg.getName(); + _playbackFile.getHeader().notes = editDlg.getNotes(); + _playbackFile.updateHeader(); + _fileHeaders[_list->getSelected()] = _playbackFile.getHeader(); + int oldselection = _list->getSelected(); + updateList(); + _list->setSelected(oldselection); + updateSelection(true); + _playbackFile.close(); + } + } + break; + case kNextScreenshotCmd: + ++_currentScreenshot; + updateScreenshot(); + break; + case kPrevScreenshotCmd: + --_currentScreenshot; + updateScreenshot(); + break; + case kDeleteCmd: + if (_list->getSelected() >= 0) { + MessageDialog alert(_("Do you really want to delete this record?"), + _("Delete"), _("Cancel")); + if (alert.runModal() == GUI::kMessageOK) { + _playbackFile.close(); + g_eventRec.deleteRecord(_fileHeaders[_list->getSelected()].fileName); + _list->setSelected(-1); + updateList(); + } + } + break; + case GUI::kListSelectionChangedCmd: + updateSelection(true); + break; + case kRecordCmd: { + TimeDate t; + Common::String gameId = ConfMan.get("gameid", _target); + const EnginePlugin *plugin = 0; + GameDescriptor desc = EngineMan.findGame(gameId, &plugin); + g_system->getTimeAndDate(t); + EditRecordDialog editDlg("Unknown Author", Common::String::format("%.2d.%.2d.%.4d ", t.tm_mday, t.tm_mon, 1900 + t.tm_year) + desc.description(), ""); + if (editDlg.runModal() != kOKCmd) { + return; + } + _author = editDlg.getAuthor(); + _name = editDlg.getName(); + _notes = editDlg.getNotes(); + _filename = g_eventRec.generateRecordFileName(_target); + setResult(kRecordDialogRecord); + close(); + } + break; + case kPlaybackCmd: + if (_list->getSelected() >= 0) { + _filename = _fileHeaders[_list->getSelected()].fileName; + setResult(kRecordDialogPlayback); + close(); + } + break; + case kCloseCmd: + setResult(kRecordDialogClose); + default: + Dialog::handleCommand(sender, cmd, data); + } + } + +void RecorderDialog::updateList() { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::String pattern(_target+".r??"); + Common::StringArray files = saveFileMan->listSavefiles(pattern); + Common::PlaybackFile file; + Common::StringArray namesList; + _fileHeaders.clear(); + for (Common::StringArray::iterator i = files.begin(); i != files.end(); ++i) { + if (file.openRead(*i)) { + namesList.push_back(file.getHeader().name); + _fileHeaders.push_back(file.getHeader()); + } + file.close(); + } + _list->setList(namesList); + _list->draw(); +} + +int RecorderDialog::runModal(Common::String &target) { + _target = target; + updateList(); + return Dialog::runModal(); +} + +RecorderDialog::~RecorderDialog() { +} + +void RecorderDialog::updateSelection(bool redraw) { + if (_list->getSelected() >= 0) { + _editButton->setEnabled(true); + _deleteButton->setEnabled(true); + _playbackButton->setEnabled(true); + } + + if (g_gui.xmlEval()->getVar("Globals.RecorderDialog.ExtInfo.Visible") != 1) + return; + + _gfxWidget->setGfx(-1, -1, 0, 0, 0); + _screenShotsCount = 0; + _currentScreenshot = 0; + updateScreenShotsText(); + if (_list->getSelected() >= 0) { + _authorText->setLabel(_("Author: ") + _fileHeaders[_list->getSelected()].author); + _notesText->setLabel(_("Notes: ") + _fileHeaders[_list->getSelected()].notes); + + _firstScreenshotUpdate = true; + updateScreenshot(); + if ((_screenShotsCount) > 0) { + _currentScreenshot = 1; + } + updateScreenshot(); + } else { + _authorText->setLabel(_("Author: ")); + _notesText->setLabel(_("Notes: ")); + _screenShotsCount = -1; + _currentScreenshot = 0; + _gfxWidget->setGfx(-1, -1, 0, 0, 0); + _gfxWidget->draw(); + updateScreenShotsText(); + } +} + +void RecorderDialog::updateScreenshot() { + if (_list->getSelected() == -1) { + return; + } + if (_currentScreenshot < 1) { + _currentScreenshot = _screenShotsCount; + } + if (_currentScreenshot > _screenShotsCount) { + _currentScreenshot = 1; + } + if (_firstScreenshotUpdate) { + _playbackFile.openRead(_fileHeaders[_list->getSelected()].fileName); + _screenShotsCount = _playbackFile.getScreensCount(); + _firstScreenshotUpdate = false; + } + Graphics::Surface *srcsf = _playbackFile.getScreenShot(_currentScreenshot); + if (srcsf != NULL) { + Graphics::Surface *destsf = Graphics::scale(*srcsf, _gfxWidget->getWidth(), _gfxWidget->getHeight()); + _gfxWidget->setGfx(destsf); + updateScreenShotsText(); + delete destsf; + delete srcsf; + } else { + _gfxWidget->setGfx(-1, -1, 0, 0, 0); + } + _gfxWidget->draw(); +} + +void RecorderDialog::updateScreenShotsText() { + if (_screenShotsCount == -1) { + _currentScreenshotText->setLabel(Common::String::format("%d / ?", _currentScreenshot)); + } else { + _currentScreenshotText->setLabel(Common::String::format("%d / %d", _currentScreenshot, _screenShotsCount)); + } +} + +} // End of namespace GUI diff --git a/gui/recorderdialog.h b/gui/recorderdialog.h new file mode 100644 index 00000000000..eb690a4f38c --- /dev/null +++ b/gui/recorderdialog.h @@ -0,0 +1,81 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GUI_RECORDER_DIALOG_H +#define GUI_RECORDER_DIALOG_H +#include "common/stream.h" +#include "common/recorderfile.h" +#include "gui/dialog.h" +namespace GUI { + +class ListWidget; +class GraphicsWidget; +class ButtonWidget; +class CommandSender; +class ContainerWidget; +class StaticTextWidget; + +class RecorderDialog : public GUI::Dialog { +private: + bool _firstScreenshotUpdate; + Common::PlaybackFile _playbackFile; + Common::String _target; + Common::String _filename; + int _currentScreenshot; + int _screenShotsCount; + Common::Array _fileHeaders; + GUI::ListWidget *_list; + GUI::ContainerWidget *_container; + GUI::GraphicsWidget *_gfxWidget; + GUI::StaticTextWidget *_currentScreenshotText; + GUI::StaticTextWidget *_authorText; + GUI::StaticTextWidget *_notesText; + GUI::ButtonWidget *_editButton; + GUI::ButtonWidget *_deleteButton; + GUI::ButtonWidget *_playbackButton; + + void updateList(); + void updateScreenShotsText(); + void updateSelection(bool redraw); + void updateScreenshot(); +public: + Common::String _author; + Common::String _name; + Common::String _notes; + enum DialogResult { + kRecordDialogClose, + kRecordDialogRecord, + kRecordDialogPlayback + }; + RecorderDialog(); + ~RecorderDialog(); + + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + virtual void reflowLayout(); + + int runModal(Common::String &target); + const Common::String getFileName() {return _filename;} +}; + +} // End of namespace GUI + + +#endif diff --git a/gui/themes/default.inc b/gui/themes/default.inc index b8a14f25024..665bb18ad14 100644 --- a/gui/themes/default.inc +++ b/gui/themes/default.inc @@ -610,6 +610,1272 @@ "/> " " " " " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " " " " " " " @@ -622,6 +1888,8 @@ " " " " " " +" " +" " " " " " " " @@ -678,11 +1946,18 @@ "size='32,18' " "padding='0,0,1,0' " "/> " +" " +" " " " " " " " " " " " " " " " -" " +" " +" " +" " " " @@ -753,6 +2032,7 @@ "/> " " " " " +" " " " " " " " @@ -894,7 +2174,7 @@ " " " " " " -" " +" " " " " " " " +" " " " " " " " @@ -1362,6 +2646,153 @@ " " " " " " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " " " " " " " @@ -1408,6 +2839,98 @@ " " " " " " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " " " " " " " " " " " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " diff --git a/gui/themes/modern.zip b/gui/themes/modern.zip index 79135b659476760a1d508e7ea86349a27f221c00..e4c3aaa2f856c8c341b5646a328d6a677589ca61 100644 GIT binary patch delta 41329 zcmeHw33yaR*60mMr_-GT5^~SYo^(i{19Xz^Y=kY4undSo5SJNdFzF;s(vYN+PKQk( zFrfSrWEsaP9A$ICe;gH2F^Ue@<9z%B>Jy&x+{OhSBM6AXj5_`zeO;B=l}N8J%{HGte6oR#&ZKWj$5;hlyw$$8Z4n<^zrTY!}CMK zHjh7D{@iliO9SasXU(04mj<%($|v}_QS9-${}Wyj#vVt1z|Rd$##)XeIT^)K9EZP= z9LG6oot|n>Q)k-~{5v7U)S;VEZT>T-P)x(osPtI=*m+$ zjtd6E$C25S!iONo8SPQg(ZS$`3>m`6_fINTHz_$NuX%ZSUG-E%uh*NKo12@1|L`X# z$3lNCQW|L)$97`0(NKv$D0kUhnwQ@v;DLW*V;hjJYL#`=tDWox6op@EbQ6}GkCbI(0%)~xxD zp3<^q%UJ7nc6P?b#*Pl2N=ixq;I6ywV(|O+?Hf04+}(HIy>jKsUk0SLKUurBt*vds zgb8=vc_*u|ckkXAGiL1Gz56%6`OUU%+a8e8{WR^|xwECErJ|zZm%scagMaeLC+E(k zHX^OPy1IJh%9SktvBw^3YHE7snP-L!8B!RKa&mH3u3YJMyJf+4@7}#~<;sqZjyK+T z#ZI3+ih|A?-@b}+;|I<%Dy=v8}<;$0^2uQcze*2m=YhHNag@p?j zGWe}qx4!@W`yzQZkx?dUKK$@Qr_;%@Hf-2%;J|^Gx=miM*W>Xp_*JV`1$2~uTC-*i zs(LZiHF4&D#xOTD4b{$|dw8KVhY#d9Zoa+M<90MR*q5?tywlX7iw!Y&z20n^5L24^ zt2N5&^@h=uolOCfF=qfxVouH=uh+}R@o_xQ^KoKk0%pbzj>R;1u$Vbwu-EI&R3tGU zFh4^|CR2u(Zyx9MdJ8b+=NIH-vgK!IXRF#-np167RT42|P4IfXl<@p`>gc6(Ko-EOb4S7EB8pH)>=N>pC2x3aSGw%cx_ADHH`fAi*vsFJ67z20Uy zx!6CKtJy_UlZ?&l^)}Je*wonA)YycdO^uiu8X6l#YzfHNDopL|tJ=l1lKoq`vb`OR zq}0wUR;;8yG%a7T0!PSlmWX}0kc@4$iKUI3Hg4LqQITXkT^urY_uU&dY@i?Ngm}`h z<92M{PJec67t^+F+qWx`Fg)8HK=X7zwNTVL;U^-=K>c~*NtW>M6Eyi@U`QVS{o{}S z{_)={5@N{0midF2Uit(5k*i5@FCySlpm`|Gc9YVr=C<{}GiMAeJ6A4^jcud)%o z+38J~K7G0@DSKGGdNma)f#W#zZJE)dp9>Z&h>VQH(uCxMgoK=&oc#QJ8DRPH8InD$TD5B3x^;KlamV7ti%*|E-NoUAbmyISu3fwKuDkBK{}=b) z?^j{hu3fu#@7}s~D}(Ohy7uqi|M0^PKlzQYs!J-G5Nyk3w ztbD{!7ReqR`wd^P$92)i!`DS0*LOxAkGOF+EnPS7kYU}tL&kOU4n?k;cPMJzyhG9J z<{cWiZr-6m>*gJbSvT)c?7De}26xUo6nEtmt+IN-9ScJ4STNv@1)+B=2-~+n_p95( zDms>wcjgQge##b*oiqNx7sm`lPEO?Rc{(D7<04|nrq}uS&dK*0;$s+Ncf8I&HHcNc zD_f`tB`dOp2s$IJdwIRC^X2v7ll5$ZiuhG{>7;>iQ%{$}BYVi9n>p^#&1Bbcek8fD z2eLzrrW{f>8YYt4o`Q*F$5T*1vo4K>GBSP)Odu=9KskAN42+@qU1MMpNq-v3$%K=9 z8CiXjx6=H}V_^zOe+DLy%4gulOrt5sXv)bcHI8#Tnra>HN@vr2SEB^0l~cSx&i{?qrx^_fgTY-_Z5nSf=a06SY_@!h$yPYpY&011O39v& z`2>>lF|SWo0**qMf>9Qet#}jzG?H6C=F=$NFt?-DOtyT?$LnMqDde?}`J{0A-AF$F zm>O92{RB>8dU@_UO1r}3Lu|)sAQ~XeJ@D!g) zJ~+iEiKMTb;*(Vs3#}HDZFC{ZDODVMnolJ^JI$NOveR=Y9^n3 zB8xrf44*8Yvr z1B~@~nM&9=hLTMxQUSIBFCtc#AbOC{tBbHwj*p0{&}bN!C$%uP_Iy{9r?r$^IM4GD zwT}7rwg!)xM1965l0E17G;;lU-XM;IiqH5|vieg#nQZ%%6=x<>HEVV`n>>}R&Sg|Z zrC~u)lDp6I`tH%iF_>QCOqS7Q`Y{%#Z*sXC?G0uhvN_!_h?r$2Zik~ug?82jK893X;8Rp+cV6J5G;<@fDxFP>9Bz-J zmdPdKmW@STsmSrp3w)%$vfj1G;Wm?*7x{R95|PHrT4$@hx&dwUj*BWDGLn11KO|hl zobz`+S%G;Z#S|mk1E!Tm^2Fcyu^~os?(e*nr2K=Q5n?0@{=p9?)-QMydG;UtU=lJ6 zQb<|^B$DHo_~bYlL#^AsxYq8mml_ecL;+Wp1#yYWHZ_b>LLdEtPa@~O;PZ$*5`b90 ztrh#a$V_R8`K=zeYavxYtGOHU5;V2oh^4?x>?M#01)|cZ z1^nPEJ`?%K@wEx-HAB6_Sy%5t40LKU7z|6BU6ijHN0SE?qAUv%G~4*VKlx!yxUR2x z0}1(>7pR0onS?$`B+NdUZB{d>{F-MSsAuri!$zi6WAzrib^GXpBklO};f$SX#X>|CA9ktk5 z>#3(2AfZ(#h1E=KCg6dg_1BGy0|3JEEmkvWPJ~2F$)u(fg|b}!j)lr(oXSE`@5i82 z^rD_bN~F^88YA4G7AJ?aCqX#vBa|K8FL_vF%SkrGU+1LJE01*>?K}a{31*0@9g7^a zYi(v?%LO2pyLh8G`F#&v*$d$deh6}3v)P6pp zm?A1$U8U?E*b0l#^T(x95uHp7218Hm9r!1o811oFx0=vo;gBDkA-euT#PSHR zn%RtQ)Azbj4*Zi(A}>Th4B4#*8`Z`vCXWwo;M7wj5_&HKQe%URwG|N%rKu1*BVaf) z0TUx3oScjRHs$q>E;R>RjF67=P_L$iqXw`k=p`E=z7KM$==s|qP5B}tAw#BT>MJRm z9{8vdT;JnIk&QVp48eDocU{^3EU_LN;FF z4N8uDLrgO9%5Epeih(XdshhDn226>fZpcF|Um9wNRh%2&NPsk2MeI>|8qt^=#d$40vW_+rdu*?pw7*meeN=jCQO&`V8V8B2RcFNkT}nvKzQ1Co1X~1M7Y)_RsZ~uEJTS7q~A7q0b7h;Lz3O@O-#c+pBblrNG8F2qla@%FWB(<1tukpCt zxF=#YlXuLJMz`X~plmQQPu7|Z#vrSQBz9mEXmCwNvML({Cec&)bZsTVzLPArLL#=` zqp{)$!v7fCD67SkpO2??#j=mA+%FY-TADbj+dLi@+v=p9wQU5%Yls#uZ!!13(|Quo ze~}YJL^9R_12uiPDhni`6a`uWkWVdO{0_O-Tql;Hb5Eq?6RWMKGScSp;jzPm9^MOrVhr zoTO}Y5uKu*4wBgvfeY$hTJh&bft5}Il5JA%`KYEEhWmQ|xmt6!8JDNJvEaM%k2fg3 zjGqy*Z!^d3+e|(#f!y!#21SH)gMu6%2bqyZ!??NzSGB#NwUoq+hmmC5co>NXSmPm= z+&3O9O*Y9)^#kXdhU`%!Yk1I)j!Q4C87Y^G%EFbP~JKQR8ygI^2_;_6AoSS-%gG z$5JpqxLHke=&==p%bip2T3q3*X*HV`*&Eu}NiQY7={XpwvmxW;;B$~0ZnM(o%85`I zVk9FvVFY=3B7~EOt!77jt+xacB_$!al4i+)NbXFo(BuwX2*n8ZsRtYqPS zFyfUHgHJxJuVj4lpTICC_a+RK!26fZp{gZr!kny<; zR1KA%uK>d{PigcS$xBnfN_NhKRAQWpdtB>)CvV&dItw!|^2T0%N#HJ23G)N!$1kp4@c>BJ{*2U+YdslRI6L`XjKg5 z)HyY4b84O86QWyGn`c0zM02c9BxSRy+7Ruk)SmPxo*Ld#BiZc*GkJ0i=*jY#kcs-4 zO=3DAGNhDg=;rIeK*~VK!cj#P;*YqgsK}s-Y`OvTsGB^wum*r!xCKq;1E5E(0K+tA zZ7sb$LTt-GFi?JJAMUDzq7c^jI%?}^_X#S2M6JjZ`{+WZpqC_9cZ6Y5z01{#*Ul)G zt7;Z+4WyHIK7oP6)UIfoLQ-}HOep9`Qnd&M4OHrFUMZRV3ZxL52LyTi?EN()&>S|* znc6G5Lis!{Pwp0P4VcJ*HZYRLvtVJ}Qnd)u$d-8F7UDewCf#I5YmJ%M9)c8^nD-E5 zkl9B7*f{hfCfAK8%l`{v0;n zW;DAAPpd|f%|C}^=52^7uhcP_P9iA{ zWu_y5L?T)73osz{01}B2^OrDhpqFIQ$jcjpNHSEDLRz>2zqr58My^r}#TP|$F1ZcT zsEH+;k3txE>NYkjpq1@&zh#j0C7>gg%0PP);(%mQSp|A69g+$oy0IH{2yJ-_lJ(;B zr#6yUqN}ib>iwLLEpMTpgWd!B9oiaBjZf~|5Ti0al;Fj;A)Oqo2Az7~Uwj*esg<1q z&Z>cMTvcO|ow0&^#z1h|*^M<*8`_+;bCx!F>gPB-9%oZstLRwCX!5OAGug5ZhV{Y> z-so=uLmZG5q~V8l)8@2c8sCFKs)nJtPrV1xy|>BwI zTk2qVPn}4+3ta`$bpoQ)IBE8t55P!~$a9^PZTO87qI!k@#A-+)A36PbKCv3o{CPgH z8bbHbX4sK)KBD`3xlH*8n8%6zIu@ zW=QC1JpETQTu1(J3dWL|Es#j+TVROf$c{e^iRAGXNK)lqYymS#bVC|D?;f|9E|_WT z!+7%z_GU9F`viEUIq>3xA^zwJ(3||-@`17>Omj4d+sfS_roC;Q z0lJ=}jY$0-3;8jIwqTfuvO z_f?cGqz^A`o??1{C#R%16ikhh7A zq>?0<>t-~JW1Eq3*ssPa*Mv9q8thAHcox}l3Iqcl*3>#1&828>d)h9wpM?16R)@#8 zY$o>U;B%k55$2X_EllO4dY8M6#I}Qw7APtnREfu9RI+Tgu|=&JI>sSJl5q!sWbdVQ zk}$~StZ__owKaLDt6?M=Rgfev4??Da5GzASriko1s%Qg*$&g_b2U9*R#Z3y_k=Ir! zi5G~x9z%G7@Bz`ZG$;>{O3V989}laH%??Mc7I`d;!a{B8Se$~z{UwfWQsiVuv*_;y zb^=7xQf=~BJC&AS4gJBl+7~&dy4;O7Ep2vaH3E_us01<_N!e_OyY-qY`i+i74tF)# zxdz}GX}bL+2+=NA;~eKQP1&$~kXWwW$Ow@xLR?KVM^CmlHanZ@g0=7 z;%c~vycHuDgYgheOM`QeO3SZ`{&zjnXI*2ZB(V*U+%5-#{EyQ#^6ig^K6yKt>QSvT6#1wi2tEWNd{v zL3WT-bH9ptdOc(geU}5R#IyS;3Uv807Ro{7Pu$L0Wd#j^B)^QR3p?FsHwA!vlqC3V z9#bdgsjZ;XnD=pWGn^go(xrSqdT+sfFhcGEFeSjkPh!W(Yq7{;yFB3rDE11iq1z}k}?%dFiOg$3kZ6iKcLirORIQtG*$?k z!tQU)sdsslE-NP4$!51|Sn{Fv<>r-AeC0K5ZnvY!!!WkiyF4;OK3q0mHRbIWF_*+9 z35n_@Ui>dA+>S*~S6k~f;gmIzZ_wiXlkl=OPrb`cE=CF_I$|+XH`C>Dw9>pZ+6T(z z1!D2xz_qRaMFX6vw1*IcQH2Y1owvLxm7Q1(LRhWS<97;&-nb6$<@x2K-HK?iFRiAp z{lSg2t5jm?Rv#*HW{43Fiqr5;Q&LKi#e{D|vX~0YCic1qTu*r1Z4PrOZi)!`u4bhk z$vRX(?}aP7T+9)#ClpSpb*eP5S2b}PW;8X4H@IpY{USVOs=d{7BYm4=pX=i&p6_f_ zIz%$tMdHBo4VVI!6MQo)a7l-a!Ym5y3fYQ8kqd)`7@rjpk40tT0R~dKm6k*{wt^wd zhB0xdqJ>QLmA;Gt7qHl?KH`dEydz0#j%76wNE+sW6bcZ~Fo4J!_Ph0s0nLAe-(wXPK#f#zZFuR$0vgAIM@ik`thjiqdgf^8NU z&_Bu}N52I_|8)r0(wVq7shE;|+MO?YAJS~QI#x(hZ|a|T0Q_=P4uzm=z{_F%@E-2l z2Gjh-3W*cwg?{Bc7BDK%ZNS%#DR0Z|jT2Hs*fxjcU9|iWz~Hvmc$_u3=))ZlsR-L7 zV#<SCyOamz&e;jk3mrsu*#*K$z;bI`j)MvrU!d`MQ;>C)=E*Lta-v>Du@R$j|{RZP0*95=O89T41K^@6Y))Va_j_+kVF$K2eJePnv~$% zbfhRs44lcskeLFEmB|aICl54WCJ$p}W?-01jzrNH6!Xu{;6X9JP{*PKjbc%RQpEj$ zqgdEAAgP5%%K<1X&IO}ObTo*C?A;GMS@@zdawbkQK826SQHJ>hi%==R&MYzep^Q)N zCP0PEo&~l&4Je^^ z(wPh3a|@)H5)1+Hoqgc0{?$aG3{lu3acfCjM@x1`yqJ!j$^hnm*vO&-cpn`X&lnVf zGkzj@`DGX=+dNu^aUdFHxNasBuG1{T#WUG?P>EC9Oz%Aj<)QiF`iRDHXi%bDU@(YA z8Cqp>{EFhrVW1S)xRD;aTUzq^K>Frg?e@Y9+4T)4ra$7#c8%jf_MU{B`x_4;)%{nA2ci5*fgk;f2k~LJ zig*wSNq^!&aP*T$UxPR~vg3OX38C!IzNg<87y4#FXdn{?gfKHgW0-J5Q#v!*d=&IF z_Cvi^qXuyEQ5dMcs4r#fr8_Jbp%T>2%0yO$3OX_oKLYMQVp)_15vcoy&UCa#hVa^4tuiP0^dQfM{?*W++{_BBE@uEO~8kEfARZ_`q+CC zMv;5phS(oa=!bk|uluFH5l4zL^h4^0A6V#z;%4o&jsH+wcm3{yKtv+fJ`zMkOs_M4 zui+rwH(PIGLa4G~Oh|&J2akJ&QV_S#gYO|$gefq(+88S$?lJjR1==_z`0j&96sTPQ zeL?+Wbcqpfe$(iZfut!?h$a901bCdBd+p51Yo&hrvcBMw3+Et-ttYNQa7p*bdKFyK zJ&KWi3ohviS)YPSdP3Hx;1V&uUmiumbm=AEzTsI=q&4Jmc=jUbgkr{%vv>?T@!t;! zj=INS6x`zsE1?%EsGEY;1;bJTOhB=w#ggE=k5{3nrN9*FIKl1 z$-Ms<$P)DI(Lazy`SOZ?<3N_{1tF3|Uxw7+r}Q?|RR1HsWGRw`jY|3KE&!MgHF`Qg zH+(Q|1*2rI;6g+Z*@9Oj!Z1=Lu*eSs2B-uAA#xp!NYO5%F(_JuRoIg?D?{i_*9Sw3 zAt*|;z3|Qx)IleBh0JG#zRPv&&CGCpm#bKj#CNxby&0FUf{wjxhuYzQHx>j;6w+}r z3r_KE>tfgI3h9B3@68fbI$?z7rV6|7MX$MK9D5jRPcFR-DPD1O+XC0bjr|H`2yYG?1q(30*$Tkx#o$Iz00 z&CrsbHT}oX68Z`}IpX2R&=TbfCIYXt288bQKD2}?qukK{gP|q<*Kf(+BZUm+hRabO zG~|H#QL4ZV`DID+CJn-ws;T-Ee5IHRANS2Pgl|Bq0T zxI&Ff_}Y;rbcrbICLVHz<(o^5>C#Chk_b0Z2-arT*S4`#Bift&+ zh;6X>!f>#jDz<@cr3DQ?lS3Qm`_J*^W|XjekuYEg;I&Z%@O+C&zB{QM z=OD!{^dZh6fy^=q^naKQ=o&I9S;$b4_K$R6mF1rH4|mWiR>VAXM_MT`MXLyX&_b(- zf6yvc1VQwPsv;Ib3#}p`BCyyO713Lge!&reC{+;>e#NRViS8)U9b|UpU)jpIR#Jyc z_z`$MQy9VqvlJ8I2Ov!l>f-5r81=!!Ce~~tWt|T{WylK+qmSF)4KETO|L_98J~Vmr z_|xT&CA>GHIEv%&H+SAn8_5}YQ<{F=bxQcE zs>;gBs!IBaAC*<&pGy483Y74kwl+^2P0|y7rOz}UKa}w6S?XB7z5_qx)X^b*>sYUZ z-?4M&&Yk$NW5*8siyva%4$Q&=CH%hb({os?gn#wOkt44jc@;m79C`Is_DKKYBg;_2 zA3uKlqvPy@{uPt*`M47P%P+tD>Z>om`cg{1Ct5(iOYosyua}e!7qUX#Zg*#Fhi+yl zF?Hy2$etH?V+Jbi&(@n36o+yg{>J*0wz9R+-q1kGh6-1RwL>>{FvoFvy*?r$LQhjj zNC+xu+Tgsrya|dldh}?PUs+kXK#`nIC(GZsapS#;bl-jVvHSxE4jepq@MW3~9z1yX z@L`sJ?%cUg6zR(^zr_45DnKdg2ElHA7shm2J9N<@It~+Q87joxf*X+4ktfw;xmPrCKt-( zT^v5MuYQ>_aNq;Uc-8>za=wg#QB&en!pP6T9^@2=P%e^5aAgWH{VFhMr96Q-T^to7 zV}guYhL@8vM@d3v0+Afc7HYy-6>{a*x_JXwkNhv}BJ%*MxS6Q9%U_!Lku>t+l#1&c zdm~8vvj&?&c}KL%$}6{O_xh_;RaV7MRlCv3t}YbF>-FLTOJv4yAy%!LW`$OckQ6e_ zr;6E8tT7mMRF#ZD${eZT2P!jJn+-u|D}EFxS~N#&(d%hjM*qC&ba~e4s7H%Ajw{|0 zpEe-hx@A;mtg&K7$N-+>xd9wU=WjAQPe=;I@zIngB+_}DY{?OlLam`3c``>x55ry| zb1g#BfcfF1*LehX?fY}M;<4w4p=65`uj- zisKx$PEWO`$)6-vY#~LnB>ZU9eHCw>ELUV|=3gDzt9$F__PeF}YX0{BgyS4;d#fWr zRHSRrJ-kq2SnN(wu7nvJH{agsansjv_!GstRWH_lEYak-3MJFaZdF#+<{MGyo{KDx ziR3u^jh7J6_WHkbN#i4iM6%H$M6v$tKxHt`=g5EN30W-b81_@Tn4v_(h_tLRu#PA=Jmq?G8}fG z>AZU)chA!iF&u}#f{NFNHpY?WTp^Q@JAgGm+8#&#Y85gWIa|Jvlyc9Laa@D5)#FbN z`;Tn52uY(q-y6p@xawS55d4z2zZNEO9R8|1YSarnxo8oR+wuR-gGcs|LpO6A{z_mW zv_Y4C%X5vcT8F!-vTpv8+*Z#LrP{0i-#zA7U}AL_-Uw1%_TNnTSH~db6kn7w$0djj z#OlvFNsifsVQc`tpDQGV&U6dpzjK8YmNhd+ND5osCU9g{5kQWTF>IaRz4QL-^Fuif zfBlqui9jl?;^6c5=2^nQH*L8f`S$*8iyGqZQTZx9`X6$|;7VK)J)~Xb(D>>{Z4i9! zjTUMDDaBW{ajT-LHgTUFE#f#drh2hHYxTd}FK{1u@JGZ$BO|mAp$-PMmbj|7`uRkzBTlU+u)``T{l7~6AE9@}#-gl6>W-&JoX_SsmOZebW9-6H1t%^%<-RV1-k!MrAKdNNI$ZrDg-u4rB$5?%uqj+$2$J0N%ah)>XQ#t-LrC)Q;$5}M-*lthS*lv$wY_}(UY`4cb zw%e02w%e0Aw%a3(?e@6Fc6+kMc6+i(X*QS?KPpm|NfPS!=U-*7E&-cGucv{?rPnNta#T zI+?${0y^w6FI`FhyL%-xstx;Bftw#)33Yav|MG%+kzM9xpSl;@WxnB4_k5$%wRV}G zRvIcVx;q^>Eq~!(rkC*1i|*+XQE_?ey!`zA*`0Qozj6oQTsf;@MYYyg*Dkx-Vv)gc zB(|Y{uqWCsyQH?E&_HBlB+@P)_$th>%Lkr>GP``>B`C^}U6NGaF7x}RfoFC!78>l0 z`0z7HiuHDR7kYhj8~om2<6OTt*fN(|>f2?P%zbt!r-@j4EE4v~e1jbdO|#X|=i*es zh9NZ%aTMao|Z{ylxug%rJMPc^m!S z<`%7|OXhb@gIRWBugosb%$W zhX$g(-mXyhrrua|>tNW6^5%JI;I+#!zm!_eB_~tYyF2(UAHf^4zBYlzBurm0u zG$<-JEdf{1I?wObWrUMKYxgEvkiIhkzZd`0S1Czt-O+()%sUv`9Ko&I-q$}8@yWc@ zA*^eG1Ipth+=$ZkdxQG^qG+7GvCvMR%pVp9A9FydUFI*veOB^7gL!E|Wxjc+HOr%P#))3}D#<(cb8suFXR} zROtN~P?*!bH8vcLnLguhq(gySY0vBKi*#@5itaG?@Hqk09{fHhWbyS*$Phm8Xs^HT zgnadN&Tu3Y>+TaL^6)=8AzSNPk^!>zdQk>ssjp}C#`?qBD3{b$Z!m>#d!mCQ!|gJE zITK1t6ea2DAE4dsiwtb*3O3B?3itSAZkNEPX*REuNZW6bpu{fog;`K!ryk|bIU^(3 z`-lWg@BORYCq6>TK4V`M|u){&9upl@<|-*Zh)uRL2>scM~&Axolz z(b(qDKysp;>I`@|P?p+)qtStp{vmxhSt(7hJ^ec(;pQE!hGdI#Zi@~^7e@!ehWrY$ zT6M*i`MzwZoaU2xARFA0Mw33C!3VNILhF#wRHL3l<vderZ$F!ab8LBfk^hLKtVm_IlmZ8u*TXoh3f!e1z zCTjb#p@e^!15RW#Zep!1qgI`qB&su}oaKg~-y1Z{0f~oZ%(Kfx%&kdIYO7Z@-zD?e zxv(G&U-aZcfKG*Stog>@0Pw%hg*s~eRW4j(m-%=eczJCeOt;JYY!LvDcF^G+^3W+b zRscxp8gCiSyLfs?Ln9+vL zOlmU=FHitgc3D|tPXQEL2vS>BU!=dcZv>|c`eeSpl+5kE4uE3S$63u^Sp~&%cPu(E z&=rd5@+jpuyMZCCYN)8uSkbvwe7H$O=uvp(JY5?y)j?hSbQQ3qx{(1Gq#%1$dUuHF4$N?(jrxOt_-B4bb}1$O_{v}TIFh#hV&}Jq1YxZIJ1&;OCgaAGAQMH z3!y-G+uk1@=_Ach z9KR%q+Jalxc@4PHd6ojGUP~GZg~R=Wy*Mo35BMAW*zasP6ip;m;?2*8egn>{!CpnPL7lxM7D*yCE#;YIP{;FXbpF6{12tud*GTQKnhq20EGyKMN0Owj z$hHXWJf7s^Z2oa=kyPvD3aGZrys-l2*kvB7pwqX% zi7qcsHPO3QnxKd;x)Cz;@bFg^un@&o1Z;MwOLGl|5opprG89Fyuv@+0ovm4dI1giG zd!F$^ja}xy@C0^p%epflQwN+L@5x;Q;#qq0VD0VTKRFpd?x>em5p;Xi;U;z~Ia1~uZ(D@`v-ooFZQBldRZ-cS|bu;1*Ze>!fnLm>PD+)fg&7=$9l!&X& z=>3^cp#(xxaz^WCK~M`T9-l?`n#-e(LCWNA_iNQqsgZr5noh{2kX_)Tp%ndnaHA5G zV)XZj5Ay18CoIl@8vWfI9*B5{qC;DUlwc>P7UuJu61q!>OEFHO?okY#L#w>BfuyE} zJo{?ETb6JjmHRs_M)%J9=P6El9u#ScraLby@2q(*O~ZB$MJXmFul-7kqB!LM2j@aY zmd^X-)0?hxqtR>f$pX0|6RBi2uROW$$f*U))Lm_nCpZQrJ7bC$kW-8o;w3Q8C-dC- zQxW*(P{UOi)KZjpP*(u+H9}SX>(o1M+yU_LcY<(@7}HosDN7rm1T>{J9T3eLjW2_l ziXoV(2dcVz(sNE)p!)yzQK;a@v+0g(!~heY2igX@=n78ABxdzWsr$YwWQQ(CO+N>R zBAE+5*i0@zrxrXs&Xdnj5$T-48TFwRml`v@?G_0GHwwk{tjg z^#1t*%G{q_px@&#!|4G%uVgYo6z$H_HX_cpn*wt^XihyQrmMh(HfX`?)I$A4CaOY` zdw}46?21pC$|b3~og(t3%Kt2~oRj6!-fXAai)#^G1g))jl9j8`yqST2Uj!u!6GW}# z5tB!_tCTFf65jedl(i(LY%ybY-IOO^7K;oIN5WpEETPcYcLNl0M<=EJLKVO=^_s>0 zCMYuA8GbPCE^IBSByl%trlt@U`e)=+@nTrEn1XG*C}aiv-iGF8D(%rtrA+uhu`BV4 z3e8;up(MFeuTm_-@+Dm<5(M)8VoIR?FkMYzIPhLks9)x%05etI2ca})W`QADNJORkJsq#^~=9aXzeC2Tr>s}^z^P$iO| zF+Gru+jh)^C34LFcmowFzmv$sEaS7=YP00PNIl{Fm%UShM(RJ zGn4xHGfOB*ba4q4;u6G%8<%u*KD`}^+ENgeek3qlh>GWenCiGaZtD`%WU^HxXOF^$ z+m}*8b?;L6TMXner&qI#1ZnMFEF7_Vohrdftl){YypBcPuEi)tVg_g`(FUz>$8x3Q zrMYL_j_X%E^kP$6%W_rS4PTHfVrZ*3Bbp4{+PQ`9&VO73|8#Vl&31Gf-@SsOh|dTp zD@y=n@xIkiC1ZZ0cOcpo8bDOXuHQg4kFJ4qyUbr$4YgFq{$Mq+@WE;*)PFSdnUI#_|;JYy084M7lhCcY|y8s!%fc{An^*y%^3i;l3 zq{a05AJ;*%UB=f+b-w&AnCrkugg+mKB0h8%Ri(Dw1=Z7J7tisrLaM{!68Q36P{2>! zMF563?t)oZ^QRWs#XH{uPn8-~4fo$erR`v#)$eUk>Jatql0;;1l9>bjVMIDm(4_Zn zXhnRLZcq6U4h4Ml3*ab^>sg0*1s#Ot;#-!n0{+AIArs*W@53zJMVx-0tQLM|aA6O! z0^arkak1nJpwyX(KaC(_fcBbNUfT@?{P{0Hj$P)bOf5*eHaa*Q9nkQ3LeX=s-`lMG zXK+PAY+DbKS|rukR{~2FSo7hBP*%K3FY9QX<~IAi4Rd`m4{m^h{D9xvDgw=98pdB; zPl}6O_?ZvEnWd-Alpa17C-`PX?l_`^+SIT`KM*dNcWz__{KMZ<#yWo|lyUDyDt){# zhq;@VM0=y^9vP|VhE|KrXscR1#`5EqQSkDim!X)yxRGi%XMcqFi;W;r?1cY}ytZy^ z#j>+TfQp$v0#-^`FKs2l=w>v6_AkOLg4K}HD%3tWPe?Ir!aM7|Fq0C_YUCzKQrqxQ zs5|0C3d=VENUI~U;eLWsRd!LmTOB%D#N7P&Iml>Mgi9hcjVEO{ro*>w^#h@u(XAux zGPj?ExptW!DPoM@{}H&-WS7MAcR-CMobg^@eA6EYxV9?{g>?8-R;ca4Zpg(uYkXUn z;sWaOyKd6pgf2L>KWsf5+lIm?3I2mj3|8>;FiW z7MoOcS5-#j#`MO-+WtORW0&zL*6>%mNU+ES5A@UPEu&D&&pZsdd58$o^)UIG>_4Pb z=4C9Wf|u=sEMEU7%DUw5gd0%wqp;deN5|{DE;8Khllg1+tLQc57%tyL1-EY_V+;r-@BP2`~(>Q9~z{4TpSCiP1uw*DIbPBfj<@nS8}0JJ9@GSD++NK}QChyoz zL5nrv?U0+4O&o3P6f&#%e=BB1i>mCv=-&YFc{}Lx7;0sh_FeP|crJOnf4PHlI2LC= z`yP<_D?5qLI9((IkbMt%=akRullM^66z5!MsweLOr|!_qVUOO<7u`#)LWq5mo+L7# z&)!S6%WNb?15%&OT>MxT!)ucjgV>(*#L>Fyvss>o0W-q4#s)QdyW~xoEi+84S!~L4 z_mh3MTJCfAL++$Tk0i7D0XorU$zV&)=I$F;UaLOLfJ{uujePQeg&TS88dk*5j8YU5 zxAoYRWY=j_>uiRI?o>8h+}1^3B0Y&CM*FqsOOT#~qTbF=eM!+9BgRguXCm3WT9$4^ zlvdQS2OqCx4%{UxHP<>!k?SpQ0OQ$vtWFm-E!ji94Jlr-M*j1jWM;xz+!!-TTVt#G zBAXG&i?k<`;;u2PaB0_A3gMEv59bRRJ{lKyIL;Tcnn}-vI>V77HY-Vta-@hM=y5`G zXMd0`oaGOKC#C3X)q`-0UFMHI2n!UK*S5Vs+#4C8N3Rz8I=e@dyW81fHbbe4X+gKC z-;(~}5z4xtzc97j78)ArAM8aKb1|D?+#t5@C0QTZ3*|*>z#TshL*KpNHfxM}A=F)! zY0$Ny!EOvdXq*rw&H1Vls3puMx^>eV)72rrj+OC;OIZ#-_EiduluN{HR*cab8ML@t zME3M13Q_*@_lW#DR?f4_8P;!AYfTICRxx~V#B5StwK0Wdes6!u)A_4bH8!NS$e({*bR-iq&yS_C z%<@!Nf2NAzW3{1gP)$HE-Hw~_!P7XaarPvzjOgZ2KR${!;i1=N`Z+Lu`~W3F^UR@v$>&`>mFXjmq0=-d-fYTWA*NMUn^ znoHqRC0NY&eH+rX^nev<>Vf9Lim)==M4cY)RAr>-w8=D!++$7!lkk4N;yaYcrQFQG z4#=fl>G*_duRr(>EJ!`)@HiAWTgh#sqto(JQ%abr-%21HMy&fO%;3gbX3aAH5G<24DXCU0YCl& zGCS7U_{+?EAAb&3Dm8{6Z~KXoELLhc=n^wl^^ag>(Bi^og#Jq8@Vg&FmRgw;DN#G`)VduOSLI7N*4*S-# zdOo_5VGv9%Xy5G=;38`!ss<~jazfrJ8<=N?lC)NO8_$zBXxcy(D4~#sL34U6vMn*P zrTG8M8H)cg7>(y$*lfm^zDa3S>~|?)cjrcSlLha%QCWpL40%H4Ne!@n8f7aJs)(+Y zBcZ`?D26eQy^Dbn6yZqMvRJ5lq`#X?MM7)l^*e?lF^p2MEv2a9t^ZE-x60SydIYgx zfO604peI*#R+`WpSn23nuS33;ik-ao!hZ#c9vu=b0L`7C30RBA4^>U5hNecQ`D`zoFOpllml* z@0~p?D?wr{WWT*{g4@{e{XKMj#l2L%Drth-wj}d+dKiAw0A*=@`z9H`_)>NPStU9@ zd=840V}lgPLD$MEBE|1*#t;)dMah~ds-<|s=}G^2s2Ui+ySz#xZW)?iGdb)_mJYin z6%o1ZW+m`B{|gh6J4C26>?*3F{P6?!D4YO$;5 z3mJ2S(aZ@G#2asCLjyT3^>_~U7apZK#Iza8c?idQV7$D9&Di}<~FFRX^@%z%;?lo5wm zo2kGAT{7YhXQwNX^X9eSS6?1p3%(5c)J2d(!XRR{_2h3=6k+Y1Fi-DMeisBYn$(kA zeiu~ojhk2ocf1RE3Xl+?fL_&{=)g646o5V?Q>1_iEpdRY2uCv>8juxulAYJoE-&ajK z^7A=`AgWK;YMV4q023xdAU5IdpbaE=;iDfyrjE8m%wjzFP!e0?frpK#6s_W1Q$Zh= z@N3VJ9-#9TaCzp?i$I3&oFjN2LIf)aBB(%-6o+R-H#$ntE+d|=STIFb3SFo^D62Sp z?Fy(C8U$;m1QcKVi0X_KE-(5$r1KwtM0IAmiS1ReeCIy`pRi6ezWm*8DqY4k(Vb95 z`NcKu8iPXqZ~v1f$EM`weiRlv8nn1UgX_i3Mr(NaV_JJ$6Iyis>U-cc3cGPV2m?Cs z2Yv9R1bwF>;Nz#CqXbvw>o6xH;P+wyD8;BQKkr-TJmCr2z`vGvVsg601e zCWtw8Jz!VfT}_yv3Y<>JCzh5bcVCoZJ0!}dH$)^Ao zOnJ_!BL-8Rdg`!2wR$ReZ4bf*<8v~Nn2xtLY^fs(H6j|KFww+KKqe)y;p0${PB})6 zmWC_LOk(lyXET!Ur45}FlBlvpjtwD+iEK?kg+^ULw4zoG|0`NiK6E9}ikdwCzd$S2 z@Vo%S?-xc(o&<3aRFpX(IhPcnXfbsO_dTu@|QxOJ|7od6yQ@Qr*eC+T_}|~o@YinW_@Da;eG7|-@X~REDuQ4Ao(66WC?yJDauC!`Y9LI2*WSDn-b73 z5qWKVX^`0v2@ZjTjj+E2=wqV}$QK17f?x#@U?p*@Fl)>%ezAyv0i`f8wXCDX8!M|b2-Pe!%=xHR8emvw)n?q28c-UXE zE&}vV>ymV%5|$uf0JXvKOAv5?$+`$wK&?yaOAtVT+Fypv}&?=iy zg4$^F3QR~ri!K&uL9JKvWwbGOx*j+NHN{Lo4QdmaBFN|aSRt3UC_r`PyOmVvpw?Yr zARa!bby+%9<1zt&@PD_sJ5Q9{Td+3GK`)OyS+FeaTKhD&&Ei*nZ8lqmP5EcHdDT~| zZo2-q>sMU6>{eqC{vt207-z+4ciU{dZJd>5;_v*b% zzxVz6ft@ol>^A(%R4293^gntq-KPEarZ_*2;x8Rx#hHKF@1!35m)~Tp27f5nf8YLZ zR6A@o{L3@V*7mPXoB9KP*t-&cOt>cZn{PSOY&QG@Q}2iW&B-6#${7FdA(mZuB=(b+l2ew3MKv*8~z(S-vNKlxBWA^+eI%cdVL=4Fqu;HO@Sn4gbu-U-+DxA0KB`irh<%uwrTZDYuP& zJ(YV8v$9$E3(rps@115XyxMo%ws2&C=Re8{?l%7fw9S^I@~a%Pubgq)Hb=ve*x-iV zo*i?BM|OzIG=Ed6&6aN(d#aUhc$`&uEs(;@r-+Gm~Ehcctp{ebSF+%Vk@zYI&HQ$YEfAK2M6rC*8l(j diff --git a/gui/themes/modern/THEMERC b/gui/themes/modern/THEMERC index 366b4eec3e6..cbb16cf0292 100644 --- a/gui/themes/modern/THEMERC +++ b/gui/themes/modern/THEMERC @@ -1 +1 @@ -[SCUMMVM_STX0.8.16:ResidualVM Modern Theme:No Author] +[SCUMMVM_STX0.8.20:ResidualVM Modern Theme:No Author] diff --git a/gui/themes/modern/editbtn.bmp b/gui/themes/modern/editbtn.bmp new file mode 100644 index 0000000000000000000000000000000000000000..49eb4035b56c615ee6524a5e6f284ea9f46f2e13 GIT binary patch literal 3126 zcmeH_FHizO6vo#;5{eicDGo^lj&>#tjwHrJk|c?SGt5XMVZww-5++HQBuROau#t$g zYqV?k&si3pJHm5j7lSkM?e5&0+xLEZ`@TEBG{bBzlQDM87&}_59d;l#!i??hinUb0 zg6;ed9RPsp?`gGT0I>Y0HdVE{o*xQ@R?k&^!x+imk49B#tNGzbg!=w?JTCI}yb!gA zu^7eAWHQJ%48utC!Dl+1ny=JS$y8G4gbyxcCK3sZ<=X*3_AHD1KvBp%HBF4a@$>n- z{6;Un$mJ-0@v5lDl&cDb0>v+v%OZ0Ve@mrniho{2O#&Q4vj6;TKTx=JClqnijyG(m{=5%NN)s5nS|KpKew191qb zNDzvUKcI-a_^(*-YKMl{sUO}QU+%u#yT^Ne$!E}+JVNLZA$0d0H|PNx8HDa{AugQI z>A%l-JSK#^{e>h+ut^j}*=)9$PN7hsD5}@%fq%VTGYm5v4l%|`0##K-QDj+G(=_le z7K>7;?t-U$D=(uFLa0$8n0HfIkewa=8pTEmtZPj4^cQ^Lf2qU#(WTT<$3W zP16|TX0!S8Uf=gI#{GVOx7&Rt*zfl|&#S81Znt5+=Xs;iC~Xs{>$=`*wLniMlj(FC z1c7OqP6E&KL{Xg0W{pMz{I+c`mrGfeyWQ?s{S6@`?dt3GFW!&8PXGV_ literal 0 HcmV?d00001 diff --git a/gui/themes/modern/fastreplay.bmp b/gui/themes/modern/fastreplay.bmp new file mode 100644 index 0000000000000000000000000000000000000000..35ad2b444412c97abc71c870c502128ed203bfcb GIT binary patch literal 3126 zcmeH@Pj1>k9EGPMVCa~uxyBwEj9ZB?jErBvB?1d=Kur_5aS#G(gTEM@wNmX z0Fnz3I6#_6arK*VMvgsL-A3Y@KOWiNd!VWOJlSWQ6UJDPF;&FMXWbM z7VhTm20qFS04Q>@|L}im=@I~YoMJKSCjflrWNXTPEwAM@P1EujSpWyQ10t8p(3Jh( zVr9|B3m6TZkN8jI})Rg@dj;`yK3Qb+t(^&vN1Z5gD%jLAD z>^F#ChGEqCscsncdOeW^P>ZP6s^CjTG%d?AEt6>R-89X37C@`jYBrmE zBN}2f8gWh8e*(Qco$IgZQ%a5%PO+m7wf?%2fXsclP5*?&&B?ztP$6QkF2-7t(I z0J_~CpPWv&+v#*@5mHn34+VqaU^pDah^GKtT=e^WzP%@|Df_Q{pU*2lVl?voL<+#j z8+o30=@FL%?FmiUfBjoP&udOP1z<9sOvaOOjI^fgzm52F%co4evRCN?m0DTr3xhWrn8g uUvD<+^(H$7V6}Q&tsb*AWq%lk&o3|OPw_VZX@8%d;xBjS-3@%W8~6u(K90En literal 0 HcmV?d00001 diff --git a/gui/themes/modern/fastreplay_small.bmp b/gui/themes/modern/fastreplay_small.bmp new file mode 100644 index 0000000000000000000000000000000000000000..8ef004c3bff3b4403300d217310115cc984bf3db GIT binary patch literal 822 zcmd6hA&Z1i6ov0H(Xz0`XxuP^C^$je20>x53nmf7Vzd|>m{G((AeaV3VX8%}TI^!9 z3WDH2cnjmKJp2W7co)w3?z!-Oe$NMZ>I?wi0Py*m4R=U)iWdp=2$b|1T3 zE<1up0HA3aCzfRq1c4LWlO%aMomiILpzFHF$;8LW*CNfo^jIm V{jw}L!F}7dctn7TqS%o)jDLg0;%EQ> literal 0 HcmV?d00001 diff --git a/gui/themes/modern/modern_gfx.stx b/gui/themes/modern/modern_gfx.stx index 5e2f66c0b10..b28974d5a21 100644 --- a/gui/themes/modern/modern_gfx.stx +++ b/gui/themes/modern/modern_gfx.stx @@ -103,6 +103,14 @@ + + + + + + + + diff --git a/gui/themes/modern/modern_layout.stx b/gui/themes/modern/modern_layout.stx index 145b9b7df0b..6a822b76535 100644 --- a/gui/themes/modern/modern_layout.stx +++ b/gui/themes/modern/modern_layout.stx @@ -43,6 +43,9 @@ + + + @@ -106,6 +109,13 @@ size = '15, 18' padding = '0, 3, 4, 0' /> + + + @@ -180,17 +190,22 @@ height = 'Globals.Line.Height' /> - - - - - + + + + + + + @@ -376,6 +391,10 @@ height = 'Globals.Line.Height' /> + @@ -811,6 +830,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -875,6 +1045,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -97,12 +100,19 @@ size = '32, 18' padding = '0, 0, 2, 0' /> + + - - - - - - + + + + + + + @@ -322,7 +336,7 @@ - + + @@ -807,6 +825,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -855,6 +1024,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3sGf*=K))L|n)DlV0mK?%df@&4~lXQd>wxI`7 z@gUya|3e4j3x0eacR)A-z&ijm`8>fs4R!!;ujv#lu+*<^pl;y*H(;&pAH%l0S!-Wb zcqX5?SgiVfd;Z}Mn&!}8iztJWFQk-0N+ATwN~NGS3Maq4hf~{f^1Dtc)hS{m)n!qr zX~4-p55svN6o(~w>LYRT#{@l&8tDqHOQlCnzL}`VXv literal 0 HcmV?d00001 diff --git a/gui/themes/modern/switchbtn.bmp b/gui/themes/modern/switchbtn.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6bafa4a9981e13d570b6383b271f427cf13d49dc GIT binary patch literal 3126 zcmeHII}U>|40Y!+b?Vr$N+2T#=-$h4L!T^FFnMwvi2_moXAkJo^iQ_dbhe=4f*sS*B4OKY5orm*Ao{Xp zD?CzM+ENP!lYpwyfIL%MZsCERlFfJkqeoL|Huz1xq19;!X{T;V+gcB7Fu1ru literal 0 HcmV?d00001 diff --git a/gui/themes/modern/switchbtn_small.bmp b/gui/themes/modern/switchbtn_small.bmp new file mode 100644 index 0000000000000000000000000000000000000000..929b1288843101e652e87aecd9be564821cbd202 GIT binary patch literal 822 zcmbV|K@Ni;5Ji9EvYV!>9-)E2(gSqg%W%VgGMaIeOh8A|kC2gh|D$}q^Mpk+fH#20 zI&XMsV*;LcotrAE|Bx)D_>Aq6j3HQyCLBq5Yay9)&PhhfDJ}{!L~V^B)G9}ll;S`w zU;1Z1t7$FyBJU?}H@(WwR7e?H@47mmdmSwn(Aw_!{Ma3L{Irp;=q

{WQxTXYGE# E59h?MBme*a literal 0 HcmV?d00001 diff --git a/gui/widget.cpp b/gui/widget.cpp index 4ffb63e9453..c3f10a861f0 100644 --- a/gui/widget.cpp +++ b/gui/widget.cpp @@ -593,8 +593,8 @@ void SliderWidget::handleMouseUp(int x, int y, int button, int clickCount) { void SliderWidget::handleMouseWheel(int x, int y, int direction) { if (isEnabled() && !_isDragging) { - // Increment or decrement one position - int newValue = posToValue(valueToPos(_value) - 1 * direction); + // Increment or decrement by one + int newValue = _value - direction; if (newValue < _valueMin) newValue = _valueMin; diff --git a/gui/widgets/edittext.cpp b/gui/widgets/edittext.cpp index 4b266e81944..3677f02e471 100644 --- a/gui/widgets/edittext.cpp +++ b/gui/widgets/edittext.cpp @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "common/system.h" #include "gui/widgets/edittext.h" #include "gui/gui-manager.h" @@ -79,8 +80,13 @@ void EditTextWidget::handleMouseDown(int x, int y, int button, int clickCount) { } if (setCaretPos(i)) draw(); -} +#ifdef TIZEN + // Display the virtual keypad to allow text entry. Samsung app-store testers expected + // the keypad to be displayed when clicking the filter edit control in the laucher gui. + g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true); +#endif +} void EditTextWidget::drawWidget() { g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x+_w, _y+_h), 0, ThemeEngine::kWidgetBackgroundEditText); diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp index 13784ddf7fd..473d5f04df9 100644 --- a/gui/widgets/list.cpp +++ b/gui/widgets/list.cpp @@ -206,6 +206,7 @@ void ListWidget::scrollTo(int item) { if (_currentPos != item) { _currentPos = item; + checkBounds(); scrollBarRecalc(); } } @@ -284,7 +285,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 && state.keycode <= Common::KEYCODE_z && Common::isPrint(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. @@ -467,6 +468,7 @@ void ListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { case kSetPositionCmd: if (_currentPos != (int)data) { _currentPos = data; + checkBounds(); draw(); // Scrollbar actions cause list focus (which triggers a redraw) @@ -550,6 +552,13 @@ Common::Rect ListWidget::getEditRect() const { return r; } +void ListWidget::checkBounds() { + if (_currentPos < 0 || _entriesPerPage > (int)_list.size()) + _currentPos = 0; + else if (_currentPos + _entriesPerPage > (int)_list.size()) + _currentPos = _list.size() - _entriesPerPage; +} + void ListWidget::scrollToCurrent() { // Only do something if the current item is not in our view port if (_selectedItem < _currentPos) { @@ -560,11 +569,7 @@ void ListWidget::scrollToCurrent() { _currentPos = _selectedItem - _entriesPerPage + 1; } - if (_currentPos < 0 || _entriesPerPage > (int)_list.size()) - _currentPos = 0; - else if (_currentPos + _entriesPerPage > (int)_list.size()) - _currentPos = _list.size() - _entriesPerPage; - + checkBounds(); _scrollBar->_currentPos = _currentPos; _scrollBar->recalc(); } diff --git a/gui/widgets/list.h b/gui/widgets/list.h index 47613b79f39..d18a82dd3f0 100644 --- a/gui/widgets/list.h +++ b/gui/widgets/list.h @@ -145,6 +145,7 @@ protected: void receivedFocusWidget(); void lostFocusWidget(); + void checkBounds(); void scrollToCurrent(); int *_textWidth; diff --git a/gui/widgets/popup.cpp b/gui/widgets/popup.cpp index 1a552e97c03..829a49c53eb 100644 --- a/gui/widgets/popup.cpp +++ b/gui/widgets/popup.cpp @@ -388,24 +388,28 @@ void PopUpWidget::handleMouseDown(int x, int y, int button, int clickCount) { if (newSel != -1 && _selectedItem != newSel) { _selectedItem = newSel; sendCommand(kPopUpItemSelectedCmd, _entries[_selectedItem].tag); + draw(); } } } void PopUpWidget::handleMouseWheel(int x, int y, int direction) { - int newSelection = _selectedItem + direction; + if (isEnabled()) { + int newSelection = _selectedItem + direction; - // Skip separator entries - while ((newSelection >= 0) && (newSelection < (int)_entries.size()) && - _entries[newSelection].name.equals("")) { - newSelection += direction; - } + // Skip separator entries + while ((newSelection >= 0) && (newSelection < (int)_entries.size()) && + _entries[newSelection].name.equals("")) { + newSelection += direction; + } - // Just update the selected item when we're in range - if ((newSelection >= 0) && (newSelection < (int)_entries.size()) && - (newSelection != _selectedItem)) { - _selectedItem = newSelection; - draw(); + // Just update the selected item when we're in range + if ((newSelection >= 0) && (newSelection < (int)_entries.size()) && + (newSelection != _selectedItem)) { + _selectedItem = newSelection; + sendCommand(kPopUpItemSelectedCmd, _entries[_selectedItem].tag); + draw(); + } } } diff --git a/po/POTFILES b/po/POTFILES index e792c0a34f5..c9e7f28d4aa 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -14,6 +14,7 @@ gui/saveload-dialog.cpp gui/themebrowser.cpp gui/ThemeEngine.cpp gui/widget.cpp +gui/fluidsynth-dialog.cpp base/main.cpp diff --git a/ports.mk b/ports.mk index 304fd67325c..f5719feca43 100644 --- a/ports.mk +++ b/ports.mk @@ -48,7 +48,7 @@ ifdef DYNAMIC_MODULES rm -rf "$(DESTDIR)$(libdir)/residualvm/" endif -#ResidualVM only: +#ResidualVM specific: deb: ln -sf dists/debian; debian/prepare @@ -93,6 +93,12 @@ endif cp $(srcdir)/dists/iphone/icon.png $(bundle_name)/ cp $(srcdir)/dists/iphone/icon-72.png $(bundle_name)/ cp $(srcdir)/dists/iphone/Default.png $(bundle_name)/ + # Binary patch workaround for Iphone 5/IPad 4 "Illegal instruction: 4" toolchain issue (http://code.google.com/p/iphone-gcc-full/issues/detail?id=6) + cp residualvm residualvm-iph5 + sed -i'' 's/\x00\x30\x93\xe4/\x00\x30\x93\xe5/g;s/\x00\x30\xd3\xe4/\x00\x30\xd3\xe5/g;' residualvm-iph5 + ldid -S residualvm-iph5 + chmod 755 residualvm-iph5 + cp residualvm-iph5 $(bundle_name)/ResidualVM-iph5 # Location of static libs for the iPhone ifneq ($(BACKEND), iphone) @@ -128,11 +134,6 @@ ifdef USE_MAD OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libmad.a endif -#ResidualVM use it: -ifdef USE_MPEG2 -OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libmpeg2.a -endif - ifdef USE_PNG OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libpng.a endif @@ -145,6 +146,10 @@ ifdef USE_FAAD OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libfaad.a endif +ifdef USE_MPEG2 +OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libmpeg2.a +endif + ifdef USE_ZLIB OSX_ZLIB ?= $(STATICLIBPATH)/lib/libz.a endif @@ -365,7 +370,6 @@ endif cp $(srcdir)/dists/ps3/ICON0.PNG ps3pkg/ sfo.py -f $(srcdir)/dists/ps3/sfo.xml ps3pkg/PARAM.SFO pkg.py --contentid UP0001-RESI12000_00-0000000000000000 ps3pkg/ residualvm-ps3.pkg - package_finalize residualvm-ps3.pkg ps3run: $(EXECUTABLE) $(STRIP) $(EXECUTABLE) @@ -374,4 +378,4 @@ ps3run: $(EXECUTABLE) ps3load $(EXECUTABLE).self # Mark special targets as phony -.PHONY: deb bundle osxsnap win32dist install uninstall ps3pkg +.PHONY: deb bundle osxsnap win32dist install uninstall ps3pkg ps3run diff --git a/test/common/bitstream.h b/test/common/bitstream.h new file mode 100644 index 00000000000..3f6a15fcd87 --- /dev/null +++ b/test/common/bitstream.h @@ -0,0 +1,152 @@ +#include + +#include "common/bitstream.h" +#include "common/memstream.h" + +class BitStreamTestSuite : public CxxTest::TestSuite +{ + public: + void test_get_bit() { + byte contents[] = { 'a' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.getBit(), 0u); + TS_ASSERT_EQUALS(bs.getBit(), 1u); + TS_ASSERT_EQUALS(bs.getBit(), 1u); + TS_ASSERT_EQUALS(bs.pos(), 3u); + TS_ASSERT(!bs.eos()); + } + + void test_get_bits() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.getBits(3), 3u); + TS_ASSERT_EQUALS(bs.pos(), 3u); + TS_ASSERT_EQUALS(bs.getBits(8), 11u); + TS_ASSERT_EQUALS(bs.pos(), 11u); + TS_ASSERT(!bs.eos()); + } + + void test_skip() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + bs.skip(5); + TS_ASSERT_EQUALS(bs.pos(), 5u); + bs.skip(4); + TS_ASSERT_EQUALS(bs.pos(), 9u); + TS_ASSERT_EQUALS(bs.getBits(3), 6u); + TS_ASSERT(!bs.eos()); + } + + void test_rewind() { + byte contents[] = { 'a' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + bs.skip(5); + TS_ASSERT_EQUALS(bs.pos(), 5u); + bs.rewind(); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.getBits(3), 3u); + TS_ASSERT(!bs.eos()); + + TS_ASSERT_EQUALS(bs.size(), 8u); + } + + void test_peek_bit() { + byte contents[] = { 'a' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.peekBit(), 0u); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.getBit(), 0u); + TS_ASSERT_EQUALS(bs.pos(), 1u); + TS_ASSERT_EQUALS(bs.peekBit(), 1u); + TS_ASSERT_EQUALS(bs.pos(), 1u); + TS_ASSERT(!bs.eos()); + } + + void test_peek_bits() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.peekBits(3), 3u); + TS_ASSERT_EQUALS(bs.pos(), 0u); + bs.skip(3); + TS_ASSERT_EQUALS(bs.pos(), 3u); + TS_ASSERT_EQUALS(bs.peekBits(8), 11u); + TS_ASSERT_EQUALS(bs.pos(), 3u); + bs.skip(8); + TS_ASSERT_EQUALS(bs.pos(), 11u); + TS_ASSERT_EQUALS(bs.peekBits(5), 2u); + TS_ASSERT(!bs.eos()); + } + + void test_eos() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8MSB bs(ms); + bs.skip(11); + TS_ASSERT_EQUALS(bs.pos(), 11u); + TS_ASSERT_EQUALS(bs.getBits(5), 2u); + TS_ASSERT(bs.eos()); + + bs.rewind(); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT(!bs.eos()); + } + + void test_get_bits_lsb() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8LSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.getBits(3), 1u); + TS_ASSERT_EQUALS(bs.pos(), 3u); + TS_ASSERT_EQUALS(bs.getBits(8), 76u); + TS_ASSERT_EQUALS(bs.pos(), 11u); + TS_ASSERT(!bs.eos()); + } + + void test_peek_bits_lsb() { + byte contents[] = { 'a', 'b' }; + + Common::MemoryReadStream ms(contents, sizeof(contents)); + + Common::BitStream8LSB bs(ms); + TS_ASSERT_EQUALS(bs.pos(), 0u); + TS_ASSERT_EQUALS(bs.peekBits(3), 1u); + TS_ASSERT_EQUALS(bs.pos(), 0u); + bs.skip(3); + TS_ASSERT_EQUALS(bs.pos(), 3u); + TS_ASSERT_EQUALS(bs.peekBits(8), 76u); + TS_ASSERT_EQUALS(bs.pos(), 3u); + bs.skip(8); + TS_ASSERT_EQUALS(bs.pos(), 11u); + TS_ASSERT_EQUALS(bs.peekBits(5), 12u); + TS_ASSERT(!bs.eos()); + } +}; diff --git a/test/common/bufferedseekablereadstream.h b/test/common/bufferedseekablereadstream.h index 11eb58f3d02..bf21f22f77a 100644 --- a/test/common/bufferedseekablereadstream.h +++ b/test/common/bufferedseekablereadstream.h @@ -72,6 +72,13 @@ class BufferedSeekableReadStreamTestSuite : public CxxTest::TestSuite { b = ssrs.readByte(); TS_ASSERT_EQUALS(b, 2); + ssrs.seek(5, SEEK_CUR); + TS_ASSERT_EQUALS(ssrs.pos(), 8); + ssrs.seek(-1, SEEK_CUR); + TS_ASSERT_EQUALS(ssrs.pos(), 7); + b = ssrs.readByte(); + TS_ASSERT_EQUALS(b, 7); + delete &ssrs; } }; diff --git a/test/common/memorywritestream.h b/test/common/memorywritestream.h new file mode 100644 index 00000000000..43a137a9f3b --- /dev/null +++ b/test/common/memorywritestream.h @@ -0,0 +1,31 @@ +#include + +#include "common/memstream.h" + +class MemoryWriteStreamTestSuite : public CxxTest::TestSuite { + public: + void test_err() { + byte temp = 0; + + Common::MemoryWriteStream stream(&temp, 0); + TS_ASSERT(!stream.err()); + + // Make sure the error indicator gets set + stream.write(&temp, 1); + TS_ASSERT(stream.err()); + + // Test whether the error indicator can be cleared + stream.clearErr(); + TS_ASSERT(!stream.err()); + } + + void test_write() { + byte buffer[7] = {}; + Common::MemoryWriteStream stream(buffer, sizeof(buffer)); + + const byte data[7] = { 7, 4, 3, 0, 10, 12, 1 }; + stream.write(data, sizeof(data)); + TS_ASSERT(memcmp(buffer, data, sizeof(data)) == 0); + TS_ASSERT(!stream.err()); + } +}; diff --git a/test/common/str.h b/test/common/str.h index 2c563f31324..adc6a099e43 100644 --- a/test/common/str.h +++ b/test/common/str.h @@ -243,6 +243,14 @@ class StringTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(str, "012345678923456789012345678901"); } + void test_erase() { + Common::String str("01234567890123456789012345678901"); + str.erase(18); + TS_ASSERT_EQUALS(str, "012345678901234567"); + str.erase(7, 5); + TS_ASSERT_EQUALS(str, "0123456234567"); + } + void test_sharing() { Common::String str("01234567890123456789012345678901"); Common::String str2(str); diff --git a/test/cxxtest/cxxtestgen.pl b/test/cxxtest/cxxtestgen.pl old mode 100644 new mode 100755 diff --git a/test/cxxtest/sample/Makefile.PL b/test/cxxtest/sample/Makefile.PL old mode 100644 new mode 100755 diff --git a/test/cxxtest/sample/msvc/CxxTest_1_Run.dsp b/test/cxxtest/sample/msvc/CxxTest_1_Run.dsp index 6bc00e7f41a..9ab9114d53b 100644 --- a/test/cxxtest/sample/msvc/CxxTest_1_Run.dsp +++ b/test/cxxtest/sample/msvc/CxxTest_1_Run.dsp @@ -1,93 +1,93 @@ -# Microsoft Developer Studio Project File - Name="CxxTest_1_Run" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) External Target" 0x0106 - -CFG=CxxTest_1_Run - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "CxxTest_1_Run.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "CxxTest_1_Run.mak" CFG="CxxTest_1_Run - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "CxxTest_1_Run - Win32 Release" (based on "Win32 (x86) External Target") -!MESSAGE "CxxTest_1_Run - Win32 Debug" (based on "Win32 (x86) External Target") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" - -!IF "$(CFG)" == "CxxTest_1_Run - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Cmd_Line "NMAKE /f CxxTest_1_Run.mak" -# PROP BASE Rebuild_Opt "/a" -# PROP BASE Target_File "CxxTest_1_Run.exe" -# PROP BASE Bsc_Name "CxxTest_1_Run.bsc" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Cmd_Line "nmake DIR=Release run" -# PROP Rebuild_Opt "/a" -# PROP Target_File "Release\run.log" -# PROP Bsc_Name "" -# PROP Target_Dir "" - -!ELSEIF "$(CFG)" == "CxxTest_1_Run - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Cmd_Line "NMAKE /f CxxTest_1_Run.mak" -# PROP BASE Rebuild_Opt "/a" -# PROP BASE Target_File "CxxTest_1_Run.exe" -# PROP BASE Bsc_Name "CxxTest_1_Run.bsc" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Cmd_Line "nmake DIR=Debug run" -# PROP Rebuild_Opt "/a" -# PROP Target_File "Debug\run.log" -# PROP Bsc_Name "" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "CxxTest_1_Run - Win32 Release" -# Name "CxxTest_1_Run - Win32 Debug" - -!IF "$(CFG)" == "CxxTest_1_Run - Win32 Release" - -!ELSEIF "$(CFG)" == "CxxTest_1_Run - Win32 Debug" - -!ENDIF - -# Begin Source File - -SOURCE=.\Makefile -# End Source File -# Begin Source File - -SOURCE=.\ReadMe.txt -# End Source File -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="CxxTest_1_Run" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=CxxTest_1_Run - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "CxxTest_1_Run.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "CxxTest_1_Run.mak" CFG="CxxTest_1_Run - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "CxxTest_1_Run - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "CxxTest_1_Run - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "CxxTest_1_Run - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Cmd_Line "NMAKE /f CxxTest_1_Run.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "CxxTest_1_Run.exe" +# PROP BASE Bsc_Name "CxxTest_1_Run.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Cmd_Line "nmake DIR=Release run" +# PROP Rebuild_Opt "/a" +# PROP Target_File "Release\run.log" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "CxxTest_1_Run - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Cmd_Line "NMAKE /f CxxTest_1_Run.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "CxxTest_1_Run.exe" +# PROP BASE Bsc_Name "CxxTest_1_Run.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Cmd_Line "nmake DIR=Debug run" +# PROP Rebuild_Opt "/a" +# PROP Target_File "Debug\run.log" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "CxxTest_1_Run - Win32 Release" +# Name "CxxTest_1_Run - Win32 Debug" + +!IF "$(CFG)" == "CxxTest_1_Run - Win32 Release" + +!ELSEIF "$(CFG)" == "CxxTest_1_Run - Win32 Debug" + +!ENDIF + +# Begin Source File + +SOURCE=.\Makefile +# End Source File +# Begin Source File + +SOURCE=.\ReadMe.txt +# End Source File +# End Target +# End Project diff --git a/test/cxxtest/sample/msvc/CxxTest_2_Build.dsp b/test/cxxtest/sample/msvc/CxxTest_2_Build.dsp index 04727fd3322..301320d84b2 100644 --- a/test/cxxtest/sample/msvc/CxxTest_2_Build.dsp +++ b/test/cxxtest/sample/msvc/CxxTest_2_Build.dsp @@ -1,94 +1,94 @@ -# Microsoft Developer Studio Project File - Name="CxxTest_2_Build" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=CxxTest_2_Build - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "CxxTest_2_Build.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "CxxTest_2_Build.mak" CFG="CxxTest_2_Build - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "CxxTest_2_Build - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "CxxTest_2_Build - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "CxxTest_2_Build - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I "..\.." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x40d /d "NDEBUG" -# ADD RSC /l 0x40d /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"Release/runner.exe" - -!ELSEIF "$(CFG)" == "CxxTest_2_Build - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\.." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD BASE RSC /l 0x40d /d "_DEBUG" -# ADD RSC /l 0x40d /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/runner.exe" /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "CxxTest_2_Build - Win32 Release" -# Name "CxxTest_2_Build - Win32 Debug" -# Begin Source File - -SOURCE=.\ReadMe.txt -# End Source File -# Begin Source File - -SOURCE=.\runner.cpp -# End Source File -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="CxxTest_2_Build" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=CxxTest_2_Build - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "CxxTest_2_Build.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "CxxTest_2_Build.mak" CFG="CxxTest_2_Build - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "CxxTest_2_Build - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "CxxTest_2_Build - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "CxxTest_2_Build - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\.." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x40d /d "NDEBUG" +# ADD RSC /l 0x40d /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"Release/runner.exe" + +!ELSEIF "$(CFG)" == "CxxTest_2_Build - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\.." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x40d /d "_DEBUG" +# ADD RSC /l 0x40d /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/runner.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "CxxTest_2_Build - Win32 Release" +# Name "CxxTest_2_Build - Win32 Debug" +# Begin Source File + +SOURCE=.\ReadMe.txt +# End Source File +# Begin Source File + +SOURCE=.\runner.cpp +# End Source File +# End Target +# End Project diff --git a/test/cxxtest/sample/msvc/CxxTest_3_Generate.dsp b/test/cxxtest/sample/msvc/CxxTest_3_Generate.dsp index 5bbad94ce00..1dd6d4dc5d8 100644 --- a/test/cxxtest/sample/msvc/CxxTest_3_Generate.dsp +++ b/test/cxxtest/sample/msvc/CxxTest_3_Generate.dsp @@ -1,93 +1,93 @@ -# Microsoft Developer Studio Project File - Name="CxxTest_3_Generate" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) External Target" 0x0106 - -CFG=CxxTest_3_Generate - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "CxxTest_3_Generate.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "CxxTest_3_Generate.mak" CFG="CxxTest_3_Generate - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "CxxTest_3_Generate - Win32 Release" (based on "Win32 (x86) External Target") -!MESSAGE "CxxTest_3_Generate - Win32 Debug" (based on "Win32 (x86) External Target") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" - -!IF "$(CFG)" == "CxxTest_3_Generate - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Cmd_Line "NMAKE /f CxxTest_3_Generate.mak" -# PROP BASE Rebuild_Opt "/a" -# PROP BASE Target_File "CxxTest_3_Generate.exe" -# PROP BASE Bsc_Name "CxxTest_3_Generate.bsc" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Cmd_Line "nmake runner.cpp" -# PROP Rebuild_Opt "/a" -# PROP Target_File "runner.cpp" -# PROP Bsc_Name "" -# PROP Target_Dir "" - -!ELSEIF "$(CFG)" == "CxxTest_3_Generate - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Cmd_Line "NMAKE /f CxxTest_3_Generate.mak" -# PROP BASE Rebuild_Opt "/a" -# PROP BASE Target_File "CxxTest_3_Generate.exe" -# PROP BASE Bsc_Name "CxxTest_3_Generate.bsc" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Cmd_Line "nmake runner.cpp" -# PROP Rebuild_Opt "/a" -# PROP Target_File "runner.cpp" -# PROP Bsc_Name "" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "CxxTest_3_Generate - Win32 Release" -# Name "CxxTest_3_Generate - Win32 Debug" - -!IF "$(CFG)" == "CxxTest_3_Generate - Win32 Release" - -!ELSEIF "$(CFG)" == "CxxTest_3_Generate - Win32 Debug" - -!ENDIF - -# Begin Source File - -SOURCE=.\Makefile -# End Source File -# Begin Source File - -SOURCE=.\ReadMe.txt -# End Source File -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="CxxTest_3_Generate" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=CxxTest_3_Generate - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "CxxTest_3_Generate.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "CxxTest_3_Generate.mak" CFG="CxxTest_3_Generate - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "CxxTest_3_Generate - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "CxxTest_3_Generate - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "CxxTest_3_Generate - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Cmd_Line "NMAKE /f CxxTest_3_Generate.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "CxxTest_3_Generate.exe" +# PROP BASE Bsc_Name "CxxTest_3_Generate.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Cmd_Line "nmake runner.cpp" +# PROP Rebuild_Opt "/a" +# PROP Target_File "runner.cpp" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "CxxTest_3_Generate - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Cmd_Line "NMAKE /f CxxTest_3_Generate.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "CxxTest_3_Generate.exe" +# PROP BASE Bsc_Name "CxxTest_3_Generate.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Cmd_Line "nmake runner.cpp" +# PROP Rebuild_Opt "/a" +# PROP Target_File "runner.cpp" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "CxxTest_3_Generate - Win32 Release" +# Name "CxxTest_3_Generate - Win32 Debug" + +!IF "$(CFG)" == "CxxTest_3_Generate - Win32 Release" + +!ELSEIF "$(CFG)" == "CxxTest_3_Generate - Win32 Debug" + +!ENDIF + +# Begin Source File + +SOURCE=.\Makefile +# End Source File +# Begin Source File + +SOURCE=.\ReadMe.txt +# End Source File +# End Target +# End Project diff --git a/test/cxxtest/sample/msvc/CxxTest_Workspace.dsw b/test/cxxtest/sample/msvc/CxxTest_Workspace.dsw index 481a1c0b514..e59b75c11c7 100644 --- a/test/cxxtest/sample/msvc/CxxTest_Workspace.dsw +++ b/test/cxxtest/sample/msvc/CxxTest_Workspace.dsw @@ -1,58 +1,58 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "CxxTest_1_Run"=.\CxxTest_1_Run.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name CxxTest_2_Build - End Project Dependency -}}} - -############################################################################### - -Project: "CxxTest_2_Build"=.\CxxTest_2_Build.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name CxxTest_3_Generate - End Project Dependency -}}} - -############################################################################### - -Project: "CxxTest_3_Generate"=.\CxxTest_3_Generate.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "CxxTest_1_Run"=.\CxxTest_1_Run.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name CxxTest_2_Build + End Project Dependency +}}} + +############################################################################### + +Project: "CxxTest_2_Build"=.\CxxTest_2_Build.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name CxxTest_3_Generate + End Project Dependency +}}} + +############################################################################### + +Project: "CxxTest_3_Generate"=.\CxxTest_3_Generate.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### diff --git a/video/bink_decoder.cpp b/video/bink_decoder.cpp index 6e6842cf407..847a8e2da71 100644 --- a/video/bink_decoder.cpp +++ b/video/bink_decoder.cpp @@ -40,7 +40,7 @@ #include "common/dct.h" #include "common/system.h" -#include "graphics/yuva_to_rgba.h" +#include "graphics/yuva_to_rgba.h" // ResidualVM specific #include "graphics/surface.h" #include "video/binkdata.h" @@ -186,7 +186,7 @@ void BinkDecoder::readNextPacket() { uint32 audioPacketStart = _bink->pos(); uint32 audioPacketEnd = _bink->pos() + audioPacketLength; - // ResidualVM specific + // ResidualVM specific if: if (i == _selectedAudioTrack) { // Number of samples in bytes audio.sampleCount = _bink->readUint32LE() / (2 * audio.channels); @@ -419,6 +419,7 @@ void BinkDecoder::BinkVideoTrack::decodePacket(VideoFrame &frame) { // Convert the YUV data we have to our format // The width used here is the surface-width, and not the video-width // to allow for odd-sized videos. + // ResidualVM: added support for Alpha version: YUVAToRGBAMan, _curPlanes[3] assert(_curPlanes[0] && _curPlanes[1] && _curPlanes[2] && _curPlanes[3]); YUVAToRGBAMan.convert420(&_surface, Graphics::YUVAToRGBAManager::kScaleITU, _curPlanes[0], _curPlanes[1], _curPlanes[2], _curPlanes[3], _surfaceWidth, _surfaceHeight, _surfaceWidth, _surfaceWidth >> 1); @@ -629,8 +630,8 @@ void BinkDecoder::BinkVideoTrack::initBundles() { _bundles[i].dataEnd = _bundles[i].data + blocks * 64; } - uint32 cbw[2] = { (_surface.w + 7) >> 3, (_surface.w + 15) >> 4 }; - uint32 cw [2] = { _surface.w , _surface.w >> 1 }; + uint32 cbw[2] = { (uint32)((_surface.w + 7) >> 3), (uint32)((_surface.w + 15) >> 4) }; + uint32 cw [2] = { (uint32)( _surface.w ), (uint32)( _surface.w >> 1) }; // Calculate the lengths of an element count in bits for (int i = 0; i < 2; i++) { diff --git a/video/bink_decoder.h b/video/bink_decoder.h index f0c5a7cfba5..f1c3c891667 100644 --- a/video/bink_decoder.h +++ b/video/bink_decoder.h @@ -33,10 +33,12 @@ #include "common/array.h" #include "common/rational.h" -#include "graphics/surface.h" +#include "graphics/surface.h" // ResidualVM specific #include "video/video_decoder.h" +#include "graphics/surface.h" + namespace Audio { class AudioStream; class QueuingAudioStream; diff --git a/video/video_decoder.cpp b/video/video_decoder.cpp index b560143e93d..5df811008c6 100644 --- a/video/video_decoder.cpp +++ b/video/video_decoder.cpp @@ -188,6 +188,25 @@ const Graphics::Surface *VideoDecoder::decodeNextFrame() { return frame; } +bool VideoDecoder::setReverse(bool reverse) { + // Can only reverse video-only videos + if (reverse && hasAudio()) + return false; + + // Attempt to make sure all the tracks are in the requested direction + for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) { + if ((*it)->getTrackType() == Track::kTrackTypeVideo && ((VideoTrack *)*it)->isReversed() != reverse) { + if (!((VideoTrack *)*it)->setReverse(reverse)) + return false; + + _needsUpdate = true; // force an update + } + } + + findNextVideoTrack(); + return true; +} + const byte *VideoDecoder::getPalette() { _dirtyPalette = false; return _palette; @@ -218,7 +237,7 @@ uint32 VideoDecoder::getTime() const { return _lastTimeChange.msecs(); if (isPaused()) - return (_playbackRate * (_pauseStartTime - _startTime)).toInt(); + return MAX((_playbackRate * (_pauseStartTime - _startTime)).toInt(), 0); if (useAudioSync()) { for (TrackList::const_iterator it = _tracks.begin(); it != _tracks.end(); it++) { @@ -231,20 +250,29 @@ uint32 VideoDecoder::getTime() const { } } - return (_playbackRate * (g_system->getMillis() - _startTime)).toInt(); + return MAX((_playbackRate * (g_system->getMillis() - _startTime)).toInt(), 0); } uint32 VideoDecoder::getTimeToNextFrame() const { if (endOfVideo() || _needsUpdate || !_nextVideoTrack) return 0; - uint32 elapsedTime = getTime(); + uint32 currentTime = getTime(); uint32 nextFrameStartTime = _nextVideoTrack->getNextFrameStartTime(); - if (nextFrameStartTime <= elapsedTime) + if (_nextVideoTrack->isReversed()) { + // For reversed videos, we need to handle the time difference the opposite way. + if (nextFrameStartTime >= currentTime) + return 0; + + return currentTime - nextFrameStartTime; + } + + // Otherwise, handle it normally. + if (nextFrameStartTime <= currentTime) return 0; - return nextFrameStartTime - elapsedTime; + return nextFrameStartTime - currentTime; } bool VideoDecoder::endOfVideo() const { @@ -318,7 +346,7 @@ bool VideoDecoder::seek(const Audio::Timestamp &time) { // Also reset our start time if (isPlaying()) { startAudio(); - _startTime = g_system->getMillis() - time.msecs(); + _startTime = g_system->getMillis() - (time.msecs() / _playbackRate).toInt(); } resetPauseStartTime(); @@ -402,9 +430,11 @@ void VideoDecoder::setRate(const Common::Rational &rate) { Common::Rational targetRate = rate; - if (rate < 0) { - // TODO: Implement support for this + // Attempt to set the reverse + if (!setReverse(rate < 0)) { + assert(rate < 0); // We shouldn't fail for forward. warning("Cannot set custom rate to backwards"); + setReverse(false); targetRate = 1; if (_playbackRate == targetRate) @@ -418,7 +448,7 @@ void VideoDecoder::setRate(const Common::Rational &rate) { _startTime = g_system->getMillis(); // Adjust start time if we've seeked to something besides zero time - if (_lastTimeChange.totalNumberOfFrames() != 0) + if (_lastTimeChange != 0) _startTime -= (_lastTimeChange.msecs() / _playbackRate).toInt(); startAudio(); @@ -644,6 +674,8 @@ bool VideoDecoder::addStreamFileTrack(const Common::String &baseName) { if (result) addTrack(track); + else + delete track; return result; } diff --git a/video/video_decoder.h b/video/video_decoder.h index 5fec52cf424..d0a6e08005e 100644 --- a/video/video_decoder.h +++ b/video/video_decoder.h @@ -353,6 +353,17 @@ public: */ void setDefaultHighColorFormat(const Graphics::PixelFormat &format) { _defaultHighColorFormat = format; } + /** + * Set the video to decode frames in reverse. + * + * By default, VideoDecoder will decode forward. + * + * @note This is used by setRate() + * @note This will not work if an audio track is present + * @param reverse true for reverse, false for forward + * @return true on success, false otherwise + */ + bool setReverse(bool reverse); ///////////////////////////////////////// // Audio Control @@ -551,6 +562,21 @@ protected: * should only be used by VideoDecoder::seekToFrame(). */ virtual Audio::Timestamp getFrameTime(uint frame) const; + + /** + * Set the video track to play in reverse or forward. + * + * By default, a VideoTrack must decode forward. + * + * @param reverse true for reverse, false for forward + * @return true for success, false for failure + */ + virtual bool setReverse(bool reverse) { return !reverse; } + + /** + * Is the video track set to play in reverse? + */ + virtual bool isReversed() const { return false; } }; /**