aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/imuse_digi
diff options
context:
space:
mode:
authorMax Horn2006-02-11 22:45:04 +0000
committerMax Horn2006-02-11 22:45:04 +0000
commit26ee630756ebdd7c96bccede0881a8c8b98e8f2b (patch)
tree26e378d5cf990a2b81c2c96e9e683a7f333b62e8 /engines/scumm/imuse_digi
parent2a9a0d4211b1ea5723f1409d91cb95de8984429e (diff)
downloadscummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.gz
scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.bz2
scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.zip
Moved engines to the new engines/ directory
svn-id: r20582
Diffstat (limited to 'engines/scumm/imuse_digi')
-rw-r--r--engines/scumm/imuse_digi/dimuse.cpp412
-rw-r--r--engines/scumm/imuse_digi/dimuse.h239
-rw-r--r--engines/scumm/imuse_digi/dimuse_bndmgr.cpp344
-rw-r--r--engines/scumm/imuse_digi/dimuse_bndmgr.h115
-rw-r--r--engines/scumm/imuse_digi/dimuse_codecs.cpp655
-rw-r--r--engines/scumm/imuse_digi/dimuse_music.cpp444
-rw-r--r--engines/scumm/imuse_digi/dimuse_script.cpp409
-rw-r--r--engines/scumm/imuse_digi/dimuse_sndmgr.cpp625
-rw-r--r--engines/scumm/imuse_digi/dimuse_sndmgr.h139
-rw-r--r--engines/scumm/imuse_digi/dimuse_tables.cpp881
-rw-r--r--engines/scumm/imuse_digi/dimuse_track.cpp368
11 files changed, 4631 insertions, 0 deletions
diff --git a/engines/scumm/imuse_digi/dimuse.cpp b/engines/scumm/imuse_digi/dimuse.cpp
new file mode 100644
index 0000000000..c057cc8d85
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse.cpp
@@ -0,0 +1,412 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "common/timer.h"
+
+#include "scumm/actor.h"
+#include "scumm/saveload.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+IMuseDigital::Track::Track()
+ : soundId(-1), used(false), stream(NULL), stream2(NULL) {
+}
+
+void IMuseDigital::timer_handler(void *refCon) {
+ IMuseDigital *imuseDigital = (IMuseDigital *)refCon;
+ imuseDigital->callback();
+}
+
+IMuseDigital::IMuseDigital(ScummEngine *scumm, int fps)
+ : _vm(scumm) {
+ _pause = false;
+ _sound = new ImuseDigiSndMgr(_vm);
+ _callbackFps = fps;
+ resetState();
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ _track[l] = new Track;
+ _track[l]->trackId = l;
+ _track[l]->used = false;
+ }
+ _vm->_timer->installTimerProc(timer_handler, 1000000 / _callbackFps, this);
+
+ _audioNames = NULL;
+ _numAudioNames = 0;
+}
+
+IMuseDigital::~IMuseDigital() {
+ stopAllSounds();
+ _vm->_timer->removeTimerProc(timer_handler);
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ delete _track[l];
+ }
+ delete _sound;
+ free(_audioNames);
+}
+
+void IMuseDigital::resetState() {
+ _curMusicState = 0;
+ _curMusicSeq = 0;
+ _curMusicCue = 0;
+ memset(_attributes, 0, sizeof(_attributes));
+ _nextSeqToPlay = 0;
+}
+
+void IMuseDigital::saveOrLoad(Serializer *ser) {
+ Common::StackLock lock(_mutex, "IMuseDigital::saveOrLoad()");
+
+ const SaveLoadEntry mainEntries[] = {
+ MK_OBSOLETE(IMuseDigital, _volVoice, sleInt32, VER(31), VER(42)),
+ MK_OBSOLETE(IMuseDigital, _volSfx, sleInt32, VER(31), VER(42)),
+ MK_OBSOLETE(IMuseDigital, _volMusic, sleInt32, VER(31), VER(42)),
+ MKLINE(IMuseDigital, _curMusicState, sleInt32, VER(31)),
+ MKLINE(IMuseDigital, _curMusicSeq, sleInt32, VER(31)),
+ MKLINE(IMuseDigital, _curMusicCue, sleInt32, VER(31)),
+ MKLINE(IMuseDigital, _nextSeqToPlay, sleInt32, VER(31)),
+ MKARRAY(IMuseDigital, _attributes[0], sleInt32, 188, VER(31)),
+ MKEND()
+ };
+
+ const SaveLoadEntry trackEntries[] = {
+ MKLINE(Track, pan, sleInt8, VER(31)),
+ MKLINE(Track, vol, sleInt32, VER(31)),
+ MKLINE(Track, volFadeDest, sleInt32, VER(31)),
+ MKLINE(Track, volFadeStep, sleInt32, VER(31)),
+ MKLINE(Track, volFadeDelay, sleInt32, VER(31)),
+ MKLINE(Track, volFadeUsed, sleByte, VER(31)),
+ MKLINE(Track, soundId, sleInt32, VER(31)),
+ MKARRAY(Track, soundName[0], sleByte, 15, VER(31)),
+ MKLINE(Track, used, sleByte, VER(31)),
+ MKLINE(Track, toBeRemoved, sleByte, VER(31)),
+ MKLINE(Track, souStream, sleByte, VER(31)),
+ MKLINE(Track, started, sleByte, VER(31)),
+ MKLINE(Track, priority, sleInt32, VER(31)),
+ MKLINE(Track, regionOffset, sleInt32, VER(31)),
+ MK_OBSOLETE(Track, trackOffset, sleInt32, VER(31), VER(31)),
+ MKLINE(Track, dataOffset, sleInt32, VER(31)),
+ MKLINE(Track, curRegion, sleInt32, VER(31)),
+ MKLINE(Track, curHookId, sleInt32, VER(31)),
+ MKLINE(Track, volGroupId, sleInt32, VER(31)),
+ MKLINE(Track, soundType, sleInt32, VER(31)),
+ MKLINE(Track, iteration, sleInt32, VER(31)),
+ MKLINE(Track, mod, sleInt32, VER(31)),
+ MKLINE(Track, mixerFlags, sleInt32, VER(31)),
+ MK_OBSOLETE(Track, mixerVol, sleInt32, VER(31), VER(42)),
+ MK_OBSOLETE(Track, mixerPan, sleInt32, VER(31), VER(42)),
+ MKLINE(Track, compressed, sleByte, VER(45)),
+ MKEND()
+ };
+
+ ser->saveLoadEntries(this, mainEntries);
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ Track *track = _track[l];
+ if (!ser->isSaving()) {
+ track->compressed = false;
+ }
+ ser->saveLoadEntries(track, trackEntries);
+ if (!ser->isSaving()) {
+ if (!track->used)
+ continue;
+ track->readyToRemove = false;
+ if ((track->toBeRemoved) || (track->souStream) || (track->curRegion == -1)) {
+ track->stream2 = NULL;
+ track->stream = NULL;
+ track->used = false;
+ continue;
+ }
+
+ track->soundHandle = _sound->openSound(track->soundId,
+ track->soundName, track->soundType,
+ track->volGroupId, -1);
+ if (!track->soundHandle) {
+ warning("IMuseDigital::saveOrLoad: Can't open sound so will not be resumed, propably on diffrent CD");
+ track->stream2 = NULL;
+ track->stream = NULL;
+ track->used = false;
+ continue;
+ }
+
+ if (track->compressed) {
+ track->regionOffset = 0;
+ }
+ track->compressed = _sound->isCompressed(track->soundHandle);
+ if (track->compressed) {
+ track->regionOffset = 0;
+ }
+ track->dataOffset = _sound->getRegionOffset(track->soundHandle, track->curRegion);
+ int bits = _sound->getBits(track->soundHandle);
+ int channels = _sound->getChannels(track->soundHandle);
+ int freq = _sound->getFreq(track->soundHandle);
+ track->iteration = freq * channels;
+ track->mixerFlags = 0;
+ if (channels == 2)
+ track->mixerFlags = Audio::Mixer::FLAG_STEREO | Audio::Mixer::FLAG_REVERSE_STEREO;
+
+ if ((bits == 12) || (bits == 16)) {
+ track->mixerFlags |= Audio::Mixer::FLAG_16BITS;
+ track->iteration *= 2;
+ } else if (bits == 8) {
+ track->mixerFlags |= Audio::Mixer::FLAG_UNSIGNED;
+ } else
+ error("IMuseDigital::saveOrLoad(): Can't handle %d bit samples", bits);
+
+#ifdef SCUMM_LITTLE_ENDIAN
+ if (track->compressed)
+ track->mixerFlags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+
+ int32 streamBufferSize = track->iteration;
+ track->stream2 = NULL;
+ track->stream = makeAppendableAudioStream(freq, track->mixerFlags, streamBufferSize);
+
+ const int pan = (track->pan != 64) ? 2 * track->pan - 127 : 0;
+ const int vol = track->vol / 1000;
+ Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType;
+
+ if (track->volGroupId == 1)
+ type = Audio::Mixer::kSpeechSoundType;
+ if (track->volGroupId == 2)
+ type = Audio::Mixer::kSFXSoundType;
+ if (track->volGroupId == 3)
+ type = Audio::Mixer::kMusicSoundType;
+
+ _vm->_mixer->playInputStream(type, &track->handle, track->stream, -1, vol, pan, false);
+ }
+ }
+}
+
+void IMuseDigital::callback() {
+ Common::StackLock lock(_mutex, "IMuseDigital::callback()");
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->readyToRemove) {
+ if (track->toBeRemoved) {
+ track->readyToRemove = true;
+ continue;
+ }
+
+ if (_pause || !_vm)
+ return;
+
+ if (track->volFadeUsed) {
+ if (track->volFadeStep < 0) {
+ if (track->vol > track->volFadeDest) {
+ track->vol += track->volFadeStep;
+ if (track->vol < track->volFadeDest) {
+ track->vol = track->volFadeDest;
+ track->volFadeUsed = false;
+ }
+ if (track->vol == 0) {
+ track->toBeRemoved = true;
+ }
+ }
+ } else if (track->volFadeStep > 0) {
+ if (track->vol < track->volFadeDest) {
+ track->vol += track->volFadeStep;
+ if (track->vol > track->volFadeDest) {
+ track->vol = track->volFadeDest;
+ track->volFadeUsed = false;
+ }
+ }
+ }
+ debug(5, "Fade: sound(%d), Vol(%d)", track->soundId, track->vol / 1000);
+ }
+
+ const int pan = (track->pan != 64) ? 2 * track->pan - 127 : 0;
+ const int vol = track->vol / 1000;
+ Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType;
+
+ if (track->volGroupId == 1)
+ type = Audio::Mixer::kSpeechSoundType;
+ if (track->volGroupId == 2)
+ type = Audio::Mixer::kSFXSoundType;
+ if (track->volGroupId == 3)
+ type = Audio::Mixer::kMusicSoundType;
+
+ if (track->stream) {
+ byte *data = NULL;
+ int32 result = 0;
+
+ if (track->curRegion == -1) {
+ switchToNextRegion(track);
+ if (track->toBeRemoved)
+ continue;
+ }
+
+ int bits = _sound->getBits(track->soundHandle);
+ int channels = _sound->getChannels(track->soundHandle);
+
+ int32 mixer_size = track->iteration / _callbackFps;
+
+ if (track->stream->endOfData()) {
+ mixer_size *= 2;
+ }
+
+ if ((bits == 12) || (bits == 16)) {
+ if (channels == 1)
+ mixer_size &= ~1;
+ if (channels == 2)
+ mixer_size &= ~3;
+ } else {
+ if (channels == 2)
+ mixer_size &= ~1;
+ }
+
+ if (mixer_size == 0)
+ continue;
+
+ do {
+ if (bits == 12) {
+ byte *ptr = NULL;
+
+ mixer_size += track->mod;
+ int mixer_size_12 = (mixer_size * 3) / 4;
+ int length = (mixer_size_12 / 3) * 4;
+ track->mod = mixer_size - length;
+
+ int32 offset = (track->regionOffset * 3) / 4;
+ int result2 = _sound->getDataFromRegion(track->soundHandle, track->curRegion, &ptr, offset, mixer_size_12);
+ result = BundleCodecs::decode12BitsSample(ptr, &data, result2);
+
+ free(ptr);
+ } else if (bits == 16) {
+ result = _sound->getDataFromRegion(track->soundHandle, track->curRegion, &data, track->regionOffset, mixer_size);
+ if (channels == 1) {
+ result &= ~1;
+ }
+ if (channels == 2) {
+ result &= ~3;
+ }
+ } else if (bits == 8) {
+ result = _sound->getDataFromRegion(track->soundHandle, track->curRegion, &data, track->regionOffset, mixer_size);
+ if (channels == 2) {
+ result &= ~1;
+ }
+ }
+
+ if (result > mixer_size)
+ result = mixer_size;
+
+ if (_vm->_mixer->isReady()) {
+ _vm->_mixer->setChannelVolume(track->handle, vol);
+ _vm->_mixer->setChannelBalance(track->handle, pan);
+ track->stream->append(data, result);
+ track->regionOffset += result;
+ }
+ free(data);
+
+ if (_sound->isEndOfRegion(track->soundHandle, track->curRegion)) {
+ switchToNextRegion(track);
+ if (track->toBeRemoved)
+ break;
+ }
+ mixer_size -= result;
+ assert(mixer_size >= 0);
+ } while (mixer_size != 0);
+ } else if (track->stream2) {
+ if (_vm->_mixer->isReady()) {
+ if (!track->started) {
+ track->started = true;
+ _vm->_mixer->playInputStream(type, &track->handle, track->stream2, -1, vol, pan, false);
+ } else {
+ _vm->_mixer->setChannelVolume(track->handle, vol);
+ _vm->_mixer->setChannelBalance(track->handle, pan);
+ }
+ }
+ }
+ }
+ }
+}
+
+void IMuseDigital::switchToNextRegion(Track *track) {
+ assert(track);
+ debug(5, "switchToNextRegion(track:%d)", track->trackId);
+
+ if (track->trackId >= MAX_DIGITAL_TRACKS) {
+ track->toBeRemoved = true;
+ debug(5, "exit (fadetrack can't go next region) switchToNextRegion(trackId:%d)", track->trackId);
+ return;
+ }
+
+ int num_regions = _sound->getNumRegions(track->soundHandle);
+
+ if (++track->curRegion == num_regions) {
+ track->toBeRemoved = true;
+ debug(5, "exit (end of regions) switchToNextRegion(track:%d)", track->trackId);
+ return;
+ }
+
+ ImuseDigiSndMgr::soundStruct *soundHandle = track->soundHandle;
+ int jumpId = _sound->getJumpIdByRegionAndHookId(soundHandle, track->curRegion, track->curHookId);
+ if (jumpId == -1)
+ jumpId = _sound->getJumpIdByRegionAndHookId(soundHandle, track->curRegion, 0);
+ if (jumpId != -1) {
+ int region = _sound->getRegionIdByJumpId(soundHandle, jumpId);
+ assert(region != -1);
+ int sampleHookId = _sound->getJumpHookId(soundHandle, jumpId);
+ assert(sampleHookId != -1);
+ int fadeDelay = (60 * _sound->getJumpFade(soundHandle, jumpId)) / 1000;
+ if (sampleHookId != 0) {
+ if (track->curHookId == sampleHookId) {
+ if (fadeDelay != 0) {
+ Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay);
+ if (fadeTrack) {
+ fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundHandle, fadeTrack->curRegion);
+ fadeTrack->regionOffset = 0;
+ debug(5, "switchToNextRegion-sound(%d) select region %d, curHookId: %d", fadeTrack->soundId, fadeTrack->curRegion, fadeTrack->curHookId);
+ fadeTrack->curHookId = 0;
+ }
+ }
+ track->curRegion = region;
+ debug(5, "switchToNextRegion-sound(%d) jump to region %d, curHookId: %d", track->soundId, track->curRegion, track->curHookId);
+ track->curHookId = 0;
+ }
+ } else {
+ if (fadeDelay != 0) {
+ Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay);
+ if (fadeTrack) {
+ fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundHandle, fadeTrack->curRegion);
+ fadeTrack->regionOffset = 0;
+ debug(5, "switchToNextRegion-sound(%d) select region %d, curHookId: %d", fadeTrack->soundId, fadeTrack->curRegion, fadeTrack->curHookId);
+ }
+ }
+ track->curRegion = region;
+ debug(5, "switchToNextRegion-sound(%d) jump to region %d, curHookId: %d", track->soundId, track->curRegion, track->curHookId);
+ }
+ }
+
+ debug(5, "switchToNextRegion-sound(%d) select region %d, curHookId: %d", track->soundId, track->curRegion, track->curHookId);
+ track->dataOffset = _sound->getRegionOffset(soundHandle, track->curRegion);
+ track->regionOffset = 0;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse.h b/engines/scumm/imuse_digi/dimuse.h
new file mode 100644
index 0000000000..fed2e48457
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse.h
@@ -0,0 +1,239 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#if !defined(IMUSE_DIGI_H) && !defined(DISABLE_SCUMM_7_8)
+#define IMUSE_DIGI_H
+
+#include "common/scummsys.h"
+#include "common/util.h"
+
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+#include "scumm/imuse_digi/dimuse_sndmgr.h"
+#include "scumm/music.h"
+#include "scumm/sound.h"
+
+#include "sound/mixer.h"
+#include "sound/audiostream.h"
+
+namespace Scumm {
+
+#define MAX_DIGITAL_TRACKS 8
+#define MAX_DIGITAL_FADETRACKS 8
+
+struct imuseDigTable;
+struct imuseComiTable;
+class Serializer;
+
+class IMuseDigital : public MusicEngine {
+private:
+
+ int _callbackFps;
+
+ struct Track {
+ int trackId;
+
+ int8 pan; // pan
+ int32 vol; // volume
+ int32 volFadeDest; //
+ int32 volFadeStep; //
+ int32 volFadeDelay; //
+ bool volFadeUsed; //
+
+ int32 soundId;
+ char soundName[15];
+ bool used;
+ bool toBeRemoved;
+ bool readyToRemove;
+ bool started;
+ bool souStream;
+ bool compressed;
+ int32 priority;
+ int32 regionOffset;
+ int32 dataOffset;
+ int32 curRegion;
+ int32 curHookId;
+ int32 volGroupId;
+ int32 soundType;
+ int32 iteration;
+ int32 mod;
+ int32 mixerFlags;
+
+ ImuseDigiSndMgr::soundStruct *soundHandle;
+ Audio::SoundHandle handle;
+ AppendableAudioStream *stream;
+ AudioStream *stream2;
+
+ Track();
+ };
+
+ Track *_track[MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS];
+
+ Common::Mutex _mutex;
+ ScummEngine *_vm;
+ ImuseDigiSndMgr *_sound;
+
+ char *_audioNames;
+ int32 _numAudioNames;
+
+ bool _pause;
+
+ int32 _attributes[188];
+ int32 _nextSeqToPlay;
+ int32 _curMusicState;
+ int32 _curMusicSeq;
+ int32 _curMusicCue;
+
+ static void timer_handler(void *refConf);
+ void callback();
+ void switchToNextRegion(Track *track);
+ int allocSlot(int priority);
+ void startSound(int soundId, const char *soundName, int soundType, int volGroupId, AudioStream *input, int hookId, int volume, int priority);
+ void selectVolumeGroup(int soundId, int volGroupId);
+
+ int32 getPosInMs(int soundId);
+ void getLipSync(int soundId, int syncId, int32 msPos, int32 &width, int32 &height);
+
+ int getSoundIdByName(const char *soundName);
+ void fadeOutMusic(int fadeDelay);
+ Track *cloneToFadeOutTrack(Track *track, int fadeDelay);
+
+ void setFtMusicState(int stateId);
+ void setFtMusicSequence(int seqId);
+ void setFtMusicCuePoint(int cueId);
+ void playFtMusic(const char *songName, int opcode, int volume);
+
+ void setComiMusicState(int stateId);
+ void setComiMusicSequence(int seqId);
+ void playComiMusic(const char *songName, const imuseComiTable *table, int atribPos, bool sequence);
+
+ void setDigMusicState(int stateId);
+ void setDigMusicSequence(int seqId);
+ void playDigMusic(const char *songName, const imuseDigTable *table, int atribPos, bool sequence);
+
+public:
+ IMuseDigital(ScummEngine *scumm, int fps);
+ virtual ~IMuseDigital();
+
+ void setAudioNames(int32 num, char *names);
+
+ void startVoice(int soundId, AudioStream *input);
+ void startVoice(int soundId, const char *soundName);
+ void startMusic(int soundId, int volume);
+ void startMusic(const char *soundName, int soundId, int hookId, int volume);
+ void startSfx(int soundId, int priority);
+ void startSound(int sound)
+ { error("MusicEngine::startSound() Should be never called"); }
+
+ void saveOrLoad(Serializer *ser);
+ void resetState();
+
+ void setPriority(int soundId, int priority);
+ void setVolume(int soundId, int volume);
+ void setPan(int soundId, int pan);
+ void setFade(int soundId, int destVolume, int delay60HzTicks);
+ int getCurMusicSoundId();
+ char *getCurMusicSoundName();
+ void setHookId(int soundId, int hookId);
+ void setMusicVolume(int vol) {}
+ void stopSound(int sound);
+ void stopAllSounds();
+ void pause(bool pause);
+ void parseScriptCmds(int cmd, int soundId, int sub_cmd, int d, int e, int f, int g, int h);
+ void refreshScripts();
+ void flushTracks();
+ int getSoundStatus(int sound) const;
+ int32 getCurMusicPosInMs();
+ int32 getCurVoiceLipSyncWidth();
+ int32 getCurVoiceLipSyncHeight();
+ int32 getCurMusicLipSyncWidth(int syncId);
+ int32 getCurMusicLipSyncHeight(int syncId);
+};
+
+struct imuseRoomMap {
+ int8 roomId;
+ byte stateIndex1;
+ byte offset;
+ byte stateIndex2;
+ byte atribPos;
+ byte stateIndex3;
+};
+
+struct imuseDigTable {
+ byte opcode;
+ int16 soundId;
+ char name[20];
+ byte atribPos;
+ byte hookId;
+ char filename[13];
+};
+
+struct imuseComiTable {
+ byte opcode;
+ int16 soundId;
+ char name[20];
+ byte atribPos;
+ byte hookId;
+ int16 fadeOut60TicksDelay;
+ char filename[13];
+};
+
+
+struct imuseFtNames {
+ char name[20];
+};
+
+struct imuseFtStateTable {
+ char audioName[9];
+ byte opcode;
+ byte volume;
+ char name[21];
+};
+
+struct imuseFtSeqTable {
+ char audioName[9];
+ byte opcode;
+ byte volume;
+};
+
+#ifdef PALMOS_68K
+extern const imuseRoomMap *_digStateMusicMap;
+extern const imuseDigTable *_digStateMusicTable;
+extern const imuseDigTable *_digSeqMusicTable;
+extern const imuseComiTable *_comiStateMusicTable;
+extern const imuseComiTable *_comiSeqMusicTable;
+extern const imuseFtStateTable *_ftStateMusicTable;
+extern const imuseFtSeqTable *_ftSeqMusicTable;
+extern const imuseFtNames *_ftSeqNames;
+#else
+extern const imuseRoomMap _digStateMusicMap[];
+extern const imuseDigTable _digStateMusicTable[];
+extern const imuseDigTable _digSeqMusicTable[];
+extern const imuseComiTable _comiStateMusicTable[];
+extern const imuseComiTable _comiSeqMusicTable[];
+extern const imuseFtStateTable _ftStateMusicTable[];
+extern const imuseFtSeqTable _ftSeqMusicTable[];
+extern const imuseFtNames _ftSeqNames[];
+#endif
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_bndmgr.cpp b/engines/scumm/imuse_digi/dimuse_bndmgr.cpp
new file mode 100644
index 0000000000..d36d733ef2
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_bndmgr.cpp
@@ -0,0 +1,344 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "scumm/scumm.h"
+#include "scumm/util.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+namespace Scumm {
+
+BundleDirCache::BundleDirCache() {
+ for (int fileId = 0; fileId < ARRAYSIZE(_budleDirCache); fileId++) {
+ _budleDirCache[fileId].bundleTable = NULL;
+ _budleDirCache[fileId].fileName[0] = 0;
+ _budleDirCache[fileId].numFiles = 0;
+ _budleDirCache[fileId].compressedBun = false;
+ _budleDirCache[fileId].indexTable = NULL;
+ }
+}
+
+BundleDirCache::~BundleDirCache() {
+ for (int fileId = 0; fileId < ARRAYSIZE(_budleDirCache); fileId++) {
+ free(_budleDirCache[fileId].bundleTable);
+ free(_budleDirCache[fileId].indexTable);
+ }
+}
+
+BundleDirCache::AudioTable *BundleDirCache::getTable(int slot) {
+ return _budleDirCache[slot].bundleTable;
+}
+
+int32 BundleDirCache::getNumFiles(int slot) {
+ return _budleDirCache[slot].numFiles;
+}
+
+BundleDirCache::IndexNode *BundleDirCache::getIndexTable(int slot) {
+ return _budleDirCache[slot].indexTable;
+}
+
+bool BundleDirCache::isCompressed(int slot) {
+ return _budleDirCache[slot].compressedBun;
+}
+
+int BundleDirCache::matchFile(const char *filename) {
+ int32 tag, offset;
+ bool found = false;
+ int freeSlot = -1;
+ int fileId;
+
+ for (fileId = 0; fileId < ARRAYSIZE(_budleDirCache); fileId++) {
+ if ((_budleDirCache[fileId].bundleTable == NULL) && (freeSlot == -1)) {
+ freeSlot = fileId;
+ }
+ if (scumm_stricmp(filename, _budleDirCache[fileId].fileName) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ ScummFile file;
+
+ if (g_scumm->openFile(file, filename) == false) {
+ error("BundleDirCache::matchFile() Can't open bundle file: %s", filename);
+ return false;
+ }
+
+ if (freeSlot == -1)
+ error("BundleDirCache::matchFileFile() Can't find free slot for file bundle dir cache");
+
+ tag = file.readUint32BE();
+ if (tag == 'LB23')
+ _budleDirCache[freeSlot].compressedBun = true;
+ offset = file.readUint32BE();
+
+ strcpy(_budleDirCache[freeSlot].fileName, filename);
+ _budleDirCache[freeSlot].numFiles = file.readUint32BE();
+ _budleDirCache[freeSlot].bundleTable = (AudioTable *) malloc(_budleDirCache[freeSlot].numFiles * sizeof(AudioTable));
+
+ file.seek(offset, SEEK_SET);
+
+ _budleDirCache[freeSlot].indexTable =
+ (IndexNode *)calloc(_budleDirCache[freeSlot].numFiles, sizeof(IndexNode));
+
+ for (int32 i = 0; i < _budleDirCache[freeSlot].numFiles; i++) {
+ char name[24], c;
+ int32 z = 0;
+ int32 z2;
+
+ if (tag == 'LB23') {
+ file.read(_budleDirCache[freeSlot].bundleTable[i].filename, 24);
+ } else {
+ for (z2 = 0; z2 < 8; z2++)
+ if ((c = file.readByte()) != 0)
+ name[z++] = c;
+ name[z++] = '.';
+ for (z2 = 0; z2 < 4; z2++)
+ if ((c = file.readByte()) != 0)
+ name[z++] = c;
+
+ name[z] = '\0';
+ strcpy(_budleDirCache[freeSlot].bundleTable[i].filename, name);
+ }
+ _budleDirCache[freeSlot].bundleTable[i].offset = file.readUint32BE();
+ _budleDirCache[freeSlot].bundleTable[i].size = file.readUint32BE();
+ strcpy(_budleDirCache[freeSlot].indexTable[i].filename,
+ _budleDirCache[freeSlot].bundleTable[i].filename);
+ _budleDirCache[freeSlot].indexTable[i].index = i;
+ }
+ qsort(_budleDirCache[freeSlot].indexTable, _budleDirCache[freeSlot].numFiles,
+ sizeof(IndexNode), (int (*)(const void*, const void*))scumm_stricmp);
+ return freeSlot;
+ } else {
+ return fileId;
+ }
+}
+
+BundleMgr::BundleMgr(BundleDirCache *cache) {
+ _cache = cache;
+ _bundleTable = NULL;
+ _compTable = NULL;
+ _numFiles = 0;
+ _numCompItems = 0;
+ _curSample = -1;
+ _fileBundleId = -1;
+ _compInput = NULL;
+}
+
+BundleMgr::~BundleMgr() {
+ close();
+}
+
+Common::File *BundleMgr::getFile(const char *filename, int32 &offset, int32 &size) {
+ BundleDirCache::IndexNode target;
+ strcpy(target.filename, filename);
+ BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles,
+ sizeof(BundleDirCache::IndexNode), (int (*)(const void*, const void*))scumm_stricmp);
+ if (found) {
+ _file.seek(_bundleTable[found->index].offset, SEEK_SET);
+ offset = _bundleTable[found->index].offset;
+ size = _bundleTable[found->index].size;
+ return &_file;
+ }
+
+ return NULL;
+}
+
+bool BundleMgr::open(const char *filename, bool &compressed, bool errorFlag) {
+ if (_file.isOpen())
+ return true;
+
+ if (g_scumm->openFile(_file, filename) == false) {
+ if (errorFlag) {
+ error("BundleMgr::open() Can't open bundle file: %s", filename);
+ } else {
+ warning("BundleMgr::open() Can't open bundle file: %s", filename);
+ }
+ return false;
+ }
+
+ int slot = _cache->matchFile(filename);
+ assert(slot != -1);
+ compressed = _cache->isCompressed(slot);
+ _numFiles = _cache->getNumFiles(slot);
+ assert(_numFiles);
+ _bundleTable = _cache->getTable(slot);
+ _indexTable = _cache->getIndexTable(slot);
+ assert(_bundleTable);
+ _compTableLoaded = false;
+ _outputSize = 0;
+ _lastBlock = -1;
+
+ return true;
+}
+
+void BundleMgr::close() {
+ if (_file.isOpen()) {
+ _file.close();
+ _bundleTable = NULL;
+ _numFiles = 0;
+ _numCompItems = 0;
+ _compTableLoaded = false;
+ _lastBlock = -1;
+ _outputSize = 0;
+ _curSample = -1;
+ free(_compTable);
+ _compTable = NULL;
+ free(_compInput);
+ _compInput = NULL;
+ }
+}
+
+bool BundleMgr::loadCompTable(int32 index) {
+ _file.seek(_bundleTable[index].offset, SEEK_SET);
+ uint32 tag = _file.readUint32BE();
+ _numCompItems = _file.readUint32BE();
+ assert(_numCompItems > 0);
+ _file.seek(8, SEEK_CUR);
+
+ if (tag != MKID_BE('COMP')) {
+ error("BundleMgr::loadCompTable() Compressed sound %d invalid (%s)", index, tag2str(tag));
+ return false;
+ }
+
+ _compTable = (CompTable *)malloc(sizeof(CompTable) * _numCompItems);
+ int32 maxSize = 0;
+ for (int i = 0; i < _numCompItems; i++) {
+ _compTable[i].offset = _file.readUint32BE();
+ _compTable[i].size = _file.readUint32BE();
+ _compTable[i].codec = _file.readUint32BE();
+ _file.seek(4, SEEK_CUR);
+ if (_compTable[i].size > maxSize)
+ maxSize = _compTable[i].size;
+ }
+ // CMI hack: one more byte at the end of input buffer
+ _compInput = (byte *)malloc(maxSize + 1);
+
+ return true;
+}
+
+int32 BundleMgr::decompressSampleByCurIndex(int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside) {
+ return decompressSampleByIndex(_curSample, offset, size, comp_final, header_size, header_outside);
+}
+
+int32 BundleMgr::decompressSampleByIndex(int32 index, int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside) {
+ int32 i, final_size, output_size;
+ int skip, first_block, last_block;
+
+ assert(0 <= index && index < _numFiles);
+
+ if (_file.isOpen() == false) {
+ error("BundleMgr::decompressSampleByIndex() File is not open!");
+ return 0;
+ }
+
+ if (_curSample == -1)
+ _curSample = index;
+
+ assert(_curSample == index);
+
+ if (!_compTableLoaded) {
+ _compTableLoaded = loadCompTable(index);
+ if (!_compTableLoaded)
+ return 0;
+ }
+
+ first_block = (offset + header_size) / 0x2000;
+ last_block = (offset + header_size + size - 1) / 0x2000;
+
+ // Clip last_block by the total number of blocks (= "comp items")
+ if ((last_block >= _numCompItems) && (_numCompItems > 0))
+ last_block = _numCompItems - 1;
+
+ int32 blocks_final_size = 0x2000 * (1 + last_block - first_block);
+ *comp_final = (byte *)malloc(blocks_final_size);
+ final_size = 0;
+
+ skip = (offset + header_size) % 0x2000;
+
+ for (i = first_block; i <= last_block; i++) {
+ if (_lastBlock != i) {
+ // CMI hack: one more zero byte at the end of input buffer
+ _compInput[_compTable[i].size] = 0;
+ _file.seek(_bundleTable[index].offset + _compTable[i].offset, SEEK_SET);
+ _file.read(_compInput, _compTable[i].size);
+ _outputSize = BundleCodecs::decompressCodec(_compTable[i].codec, _compInput, _compOutput, _compTable[i].size);
+ if (_outputSize > 0x2000) {
+ error("_outputSize: %d", _outputSize);
+ }
+ _lastBlock = i;
+ }
+
+ output_size = _outputSize;
+
+ if (header_outside) {
+ output_size -= skip;
+ } else {
+ if ((header_size != 0) && (skip >= header_size))
+ output_size -= skip;
+ }
+
+ if ((output_size + skip) > 0x2000) // workaround
+ output_size -= (output_size + skip) - 0x2000;
+
+ if (output_size > size)
+ output_size = size;
+
+ assert(final_size + output_size <= blocks_final_size);
+
+ memcpy(*comp_final + final_size, _compOutput + skip, output_size);
+ final_size += output_size;
+
+ size -= output_size;
+ assert(size >= 0);
+ if (size == 0)
+ break;
+
+ skip = 0;
+ }
+
+ return final_size;
+}
+
+int32 BundleMgr::decompressSampleByName(const char *name, int32 offset, int32 size, byte **comp_final, bool header_outside) {
+ int32 final_size = 0;
+
+ if (!_file.isOpen()) {
+ error("BundleMgr::decompressSampleByName() File is not open!");
+ return 0;
+ }
+
+ BundleDirCache::IndexNode target;
+ strcpy(target.filename, name);
+ BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles,
+ sizeof(BundleDirCache::IndexNode), (int (*)(const void*, const void*))scumm_stricmp);
+ if (found) {
+ final_size = decompressSampleByIndex(found->index, offset, size, comp_final, 0, header_outside);
+ return final_size;
+ }
+
+ debug(2, "BundleMgr::decompressSampleByName() Failed finding voice %s", name);
+ return final_size;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_bndmgr.h b/engines/scumm/imuse_digi/dimuse_bndmgr.h
new file mode 100644
index 0000000000..36d4f5683e
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_bndmgr.h
@@ -0,0 +1,115 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef BUNDLE_MGR_H
+#define BUNDLE_MGR_H
+
+#include "common/scummsys.h"
+#include "common/file.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+class BundleDirCache {
+public:
+ struct AudioTable {
+ char filename[24];
+ int32 offset;
+ int32 size;
+ };
+
+ struct IndexNode {
+ char filename[24];
+ int32 index;
+ };
+
+private:
+
+ struct FileDirCache {
+ char fileName[20];
+ AudioTable *bundleTable;
+ int32 numFiles;
+ bool compressedBun;
+ IndexNode *indexTable;
+ } _budleDirCache[4];
+
+public:
+ BundleDirCache();
+ ~BundleDirCache();
+
+ int matchFile(const char *filename);
+ AudioTable *getTable(int slot);
+ IndexNode *getIndexTable(int slot);
+ int32 getNumFiles(int slot);
+ bool isCompressed(int slot);
+};
+
+class BundleMgr {
+
+private:
+
+ struct CompTable {
+ int32 offset;
+ int32 size;
+ int32 codec;
+ };
+
+ BundleDirCache *_cache;
+ BundleDirCache::AudioTable *_bundleTable;
+ BundleDirCache::IndexNode *_indexTable;
+ CompTable *_compTable;
+ int _numFiles;
+ int _numCompItems;
+ int _curSample;
+ ScummFile _file;
+ bool _compTableLoaded;
+ int _fileBundleId;
+ byte _compOutput[0x2000];
+ byte *_compInput;
+ int _outputSize;
+ int _lastBlock;
+
+ bool loadCompTable(int32 index);
+
+public:
+
+ BundleMgr(BundleDirCache *_cache);
+ ~BundleMgr();
+
+ bool open(const char *filename, bool &compressed, bool errorFlag=false);
+ void close();
+ Common::File *getFile(const char *filename, int32 &offset, int32 &size);
+ int32 decompressSampleByName(const char *name, int32 offset, int32 size, byte **comp_final, bool header_outside);
+ int32 decompressSampleByIndex(int32 index, int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside);
+ int32 decompressSampleByCurIndex(int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside);
+};
+
+namespace BundleCodecs {
+
+uint32 decode12BitsSample(const byte *src, byte **dst, uint32 size);
+void initializeImcTables();
+int32 decompressCodec(int32 codec, byte *comp_input, byte *comp_output, int32 input_size);
+
+} // End of namespace BundleCodecs
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_codecs.cpp b/engines/scumm/imuse_digi/dimuse_codecs.cpp
new file mode 100644
index 0000000000..dccae928b0
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_codecs.cpp
@@ -0,0 +1,655 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/util.h"
+
+namespace Scumm {
+
+namespace BundleCodecs {
+
+uint32 decode12BitsSample(const byte *src, byte **dst, uint32 size) {
+ uint32 loop_size = size / 3;
+ uint32 s_size = loop_size * 4;
+ byte *ptr = *dst = (byte *)malloc(s_size);
+
+ uint32 tmp;
+ while (loop_size--) {
+ byte v1 = *src++;
+ byte v2 = *src++;
+ byte v3 = *src++;
+ tmp = ((((v2 & 0x0f) << 8) | v1) << 4) - 0x8000;
+ WRITE_BE_UINT16(ptr, tmp); ptr += 2;
+ tmp = ((((v2 & 0xf0) << 4) | v3) << 4) - 0x8000;
+ WRITE_BE_UINT16(ptr, tmp); ptr += 2;
+ }
+ return s_size;
+}
+
+/*
+ * The "IMC" codec below (see cases 13 & 15 in decompressCodec) is actually a
+ * variant of the IMA codec, see also
+ * <http://www.multimedia.cx/simpleaudio.html>
+ *
+ * It is somewhat different, though: the standard ADPCM codecs use a fixed
+ * size for their data packets (4 bits), while the codec implemented here
+ * varies the size of each "packet" between 2 and 7 bits.
+ */
+
+static byte _imcTableEntryBitCount[89];
+
+#ifdef PALMOS_68K
+static const int16 *imcTable;
+#else
+static const int16 imcTable[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 16, 17, 19, 21, 23, 25, 28, 31,
+ 34, 37, 41, 45, 50, 55, 60, 66,
+ 73, 80, 88, 97, 107, 118, 130, 143,
+ 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658,
+ 724, 796, 876, 963, 1060, 1166, 1282, 1411,
+ 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
+ 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
+ 7132, 7845, 8630, 9493,10442,11487,12635,13899,
+ 15289,16818,18500,20350,22385,24623,27086,29794,
+ 32767
+};
+#endif
+
+static const byte imxOtherTable[6][64] = {
+ {
+ 0xFF,
+ 4
+ },
+
+ {
+ 0xFF, 0xFF,
+ 2, 8
+ },
+
+ {
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 1, 2, 4, 6
+ },
+
+ {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 1, 2, 4, 6, 8, 12, 16, 32
+ },
+
+ {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 1, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 32
+ },
+
+ {
+ 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,
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32
+ }
+};
+
+void initializeImcTables() {
+ int pos;
+
+ for (pos = 0; pos < ARRAYSIZE(imcTable); ++pos) {
+ byte put = 0;
+ int32 tableValue = ((imcTable[pos] * 4) / 7) / 2;
+ while (tableValue != 0) {
+ tableValue /= 2;
+ put++;
+ }
+ if (put < 2) {
+ put = 2;
+ }
+ if (put > 7) {
+ put = 7;
+ }
+ _imcTableEntryBitCount[pos] = put;
+ }
+}
+
+#define NextBit \
+ do { \
+ bit = mask & 1; \
+ mask >>= 1; \
+ if (!--bitsleft) { \
+ mask = READ_LE_UINT16(srcptr); \
+ srcptr += 2; \
+ bitsleft = 16; \
+ } \
+ } while (0)
+
+static int32 compDecode(byte *src, byte *dst) {
+ byte *result, *srcptr = src, *dstptr = dst;
+ int data, size, bit, bitsleft = 16, mask = READ_LE_UINT16(srcptr);
+ srcptr += 2;
+
+ for (;;) {
+ NextBit;
+ if (bit) {
+ *dstptr++ = *srcptr++;
+ } else {
+ NextBit;
+ if (!bit) {
+ NextBit;
+ size = bit << 1;
+ NextBit;
+ size = (size | bit) + 3;
+ data = *srcptr++ | 0xffffff00;
+ } else {
+ data = *srcptr++;
+ size = *srcptr++;
+
+ data |= 0xfffff000 + ((size & 0xf0) << 4);
+ size = (size & 0x0f) + 3;
+
+ if (size == 3)
+ if (((*srcptr++) + 1) == 1)
+ return dstptr - dst;
+ }
+ result = dstptr + data;
+ while (size--)
+ *dstptr++ = *result++;
+ }
+ }
+}
+#undef NextBit
+
+int32 decompressCodec(int32 codec, byte *comp_input, byte *comp_output, int32 input_size) {
+ int32 output_size, channels;
+ int32 offset1, offset2, offset3, length, k, c, s, j, r, t, z;
+ byte *src, *t_table, *p, *ptr;
+ byte t_tmp1, t_tmp2;
+
+ switch (codec) {
+ case 0:
+ memcpy(comp_output, comp_input, input_size);
+ output_size = input_size;
+ break;
+
+ case 1:
+ output_size = compDecode(comp_input, comp_output);
+ break;
+
+ case 2:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+ break;
+
+ case 3:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+ break;
+
+ case 4:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 0;
+ if (length > 0) {
+ c = -12;
+ s = 0;
+ j = 0;
+ do {
+ ptr = src + length + (k >> 1);
+ t_tmp2 = src[j];
+ if (k & 1) {
+ r = c >> 3;
+ t_table[r + 2] = ((t_tmp2 & 0x0f) << 4) | (ptr[1] >> 4);
+ t_table[r + 1] = (t_tmp2 & 0xf0) | (t_table[r + 1]);
+ } else {
+ r = s >> 3;
+ t_table[r + 0] = ((t_tmp2 & 0x0f) << 4) | (ptr[0] & 0x0f);
+ t_table[r + 1] = t_tmp2 >> 4;
+ }
+ s += 12;
+ c += 12;
+ k++;
+ j++;
+ } while (k < length);
+ }
+ offset1 = ((length - 1) * 3) >> 1;
+ t_table[offset1 + 1] = (t_table[offset1 + 1]) | (src[length - 1] & 0xf0);
+ memcpy(src, t_table, output_size);
+ free(t_table);
+ break;
+
+ case 5:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 1;
+ c = 0;
+ s = 12;
+ t_table[0] = src[length] >> 4;
+ t = length + k;
+ j = 1;
+ if (t > k) {
+ do {
+ t_tmp1 = *(src + length + (k >> 1));
+ t_tmp2 = src[j - 1];
+ if (k & 1) {
+ r = c >> 3;
+ t_table[r + 0] = (t_tmp2 & 0xf0) | t_table[r];
+ t_table[r + 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 & 0x0f);
+ } else {
+ r = s >> 3;
+ t_table[r + 0] = t_tmp2 >> 4;
+ t_table[r - 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 >> 4);
+ }
+ s += 12;
+ c += 12;
+ k++;
+ j++;
+ } while (k < t);
+ }
+ memcpy(src, t_table, output_size);
+ free(t_table);
+ break;
+
+ case 6:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 0;
+ c = 0;
+ j = 0;
+ s = -12;
+ t_table[0] = src[output_size - 1];
+ t_table[output_size - 1] = src[length - 1];
+ t = length - 1;
+ if (t > 0) {
+ do {
+ t_tmp1 = *(src + length + (k >> 1));
+ t_tmp2 = src[j];
+ if (k & 1) {
+ r = s >> 3;
+ t_table[r + 2] = (t_tmp2 & 0xf0) | t_table[r + 2];
+ t_table[r + 3] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 >> 4);
+ } else {
+ r = c >> 3;
+ t_table[r + 2] = t_tmp2 >> 4;
+ t_table[r + 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 & 0x0f);
+ }
+ s += 12;
+ c += 12;
+ k++;
+ j++;
+ } while (k < t);
+ }
+ memcpy(src, t_table, output_size);
+ free(t_table);
+ break;
+
+ case 10:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+ memcpy(t_table, p, output_size);
+
+ offset1 = output_size / 3;
+ offset2 = offset1 << 1;
+ offset3 = offset2;
+ src = comp_output;
+
+ while (offset1--) {
+ offset2 -= 2;
+ offset3--;
+ t_table[offset2 + 0] = src[offset1];
+ t_table[offset2 + 1] = src[offset3];
+ }
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 0;
+ if (length > 0) {
+ c = -12;
+ s = 0;
+ do {
+ j = length + (k >> 1);
+ t_tmp1 = t_table[k];
+ if (k & 1) {
+ r = c >> 3;
+ t_tmp2 = t_table[j + 1];
+ src[r + 2] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4);
+ src[r + 1] = (src[r + 1]) | (t_tmp1 & 0xf0);
+ } else {
+ r = s >> 3;
+ t_tmp2 = t_table[j];
+ src[r + 0] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f);
+ src[r + 1] = t_tmp1 >> 4;
+ }
+ s += 12;
+ c += 12;
+ k++;
+ } while (k < length);
+ }
+ offset1 = ((length - 1) * 3) >> 1;
+ src[offset1 + 1] = (t_table[length] & 0xf0) | src[offset1 + 1];
+ free(t_table);
+ break;
+
+ case 11:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+ memcpy(t_table, p, output_size);
+
+ offset1 = output_size / 3;
+ offset2 = offset1 << 1;
+ offset3 = offset2;
+ src = comp_output;
+
+ while (offset1--) {
+ offset2 -= 2;
+ offset3--;
+ t_table[offset2 + 0] = src[offset1];
+ t_table[offset2 + 1] = src[offset3];
+ }
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 1;
+ c = 0;
+ s = 12;
+ t_tmp1 = t_table[length] >> 4;
+ src[0] = t_tmp1;
+ t = length + k;
+ if (t > k) {
+ do {
+ j = length + (k >> 1);
+ t_tmp1 = t_table[k - 1];
+ t_tmp2 = t_table[j];
+ if (k & 1) {
+ r = c >> 3;
+ src[r + 0] = (src[r]) | (t_tmp1 & 0xf0);
+ src[r + 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f);
+ } else {
+ r = s >> 3;
+ src[r + 0] = t_tmp1 >> 4;
+ src[r - 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4);
+ }
+ s += 12;
+ c += 12;
+ k++;
+ } while (k < t);
+ }
+ free(t_table);
+ break;
+
+ case 12:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+ memcpy(t_table, p, output_size);
+
+ offset1 = output_size / 3;
+ offset2 = offset1 << 1;
+ offset3 = offset2;
+ src = comp_output;
+
+ while (offset1--) {
+ offset2 -= 2;
+ offset3--;
+ t_table[offset2 + 0] = src[offset1];
+ t_table[offset2 + 1] = src[offset3];
+ }
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 0;
+ c = 0;
+ s = -12;
+ src[0] = t_table[output_size - 1];
+ src[output_size - 1] = t_table[length - 1];
+ t = length - 1;
+ if (t > 0) {
+ do {
+ j = length + (k >> 1);
+ t_tmp1 = t_table[k];
+ t_tmp2 = t_table[j];
+ if (k & 1) {
+ r = s >> 3;
+ src[r + 2] = (src[r + 2]) | (t_tmp1 & 0xf0);
+ src[r + 3] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4);
+ } else {
+ r = c >> 3;
+ src[r + 2] = t_tmp1 >> 4;
+ src[r + 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f);
+ }
+ s += 12;
+ c += 12;
+ k++;
+ } while (k < t);
+ }
+ free(t_table);
+ break;
+
+ case 13:
+ case 15:
+ if (codec == 13) {
+ channels = 1;
+ } else {
+ channels = 2;
+ }
+
+ {
+ // Decoder for the the IMA ADPCM variants used in COMI.
+ // Contrary to regular IMA ADPCM, this codec uses a variable
+ // bitsize for the encoded data.
+
+ const int MAX_CHANNELS = 2;
+ int32 outputSamplesLeft;
+ int32 destPos;
+ int16 firstWord;
+ byte initialTablePos[MAX_CHANNELS] = {0, 0};
+ int32 initialimcTableEntry[MAX_CHANNELS] = {7, 7};
+ int32 initialOutputWord[MAX_CHANNELS] = {0, 0};
+ int32 totalBitOffset, curTablePos, outputWord;
+ byte *dst;
+ int i;
+
+ // We only support mono and stereo
+ assert(channels == 1 || channels == 2);
+
+ src = comp_input;
+ dst = comp_output;
+ output_size = 0x2000;
+ outputSamplesLeft = 0x1000;
+
+ // Every data packet contains 0x2000 bytes of audio data
+ // when extracted. In order to encode bigger data sets,
+ // one has to split the data into multiple blocks.
+ //
+ // Every block starts with a 2 byte word. If that word is
+ // non-zero, it indicates the size of a block of raw audio
+ // data (not encoded) following it. That data we simply copy
+ // to the output buffer and the proceed by decoding the
+ // remaining data.
+ //
+ // If on the other hand the word is zero, then what follows
+ // are 7*channels bytes containing seed data for the decoder.
+ firstWord = READ_BE_UINT16(src);
+ src += 2;
+ if (firstWord != 0) {
+ // Copy raw data
+ memcpy(dst, src, firstWord);
+ dst += firstWord;
+ src += firstWord;
+ assert((firstWord & 1) == 0);
+ outputSamplesLeft -= firstWord / 2;
+ } else {
+ // Read the seed values for the decoder.
+ for (i = 0; i < channels; i++) {
+ initialTablePos[i] = *src;
+ src += 1;
+ initialimcTableEntry[i] = READ_BE_UINT32(src);
+ src += 4;
+ initialOutputWord[i] = READ_BE_UINT32(src);
+ src += 4;
+ }
+ }
+
+ totalBitOffset = 0;
+ // The channels are encoded separately.
+ for (int chan = 0; chan < channels; chan++) {
+ // Read initial state (this makes it possible for the data stream
+ // to be split & spread across multiple data chunks.
+ curTablePos = initialTablePos[chan];
+ //imcTableEntry = initialimcTableEntry[chan];
+ outputWord = initialOutputWord[chan];
+
+ // We need to interleave the channels in the output; we achieve
+ // that by using a variables dest offset:
+ destPos = chan * 2;
+
+ const int bound = (channels == 1)
+ ? outputSamplesLeft
+ : ((chan == 0)
+ ? (outputSamplesLeft+1) / 2
+ : outputSamplesLeft / 2);
+ for (i = 0; i < bound; ++i) {
+ // Determine the size (in bits) of the next data packet
+ const int32 curTableEntryBitCount = _imcTableEntryBitCount[curTablePos];
+ assert(2 <= curTableEntryBitCount && curTableEntryBitCount <= 7);
+
+ // Read the next data packet
+ const byte *readPos = src + (totalBitOffset >> 3);
+ const uint16 readWord = (uint16)(READ_BE_UINT16(readPos) << (totalBitOffset & 7));
+ const byte packet = (byte)(readWord >> (16 - curTableEntryBitCount));
+
+ // Advance read position to the next data packet
+ totalBitOffset += curTableEntryBitCount;
+
+ // Decode the data packet into a delta value for the output signal.
+ const byte signBitMask = (1 << (curTableEntryBitCount - 1));
+ const byte dataBitMask = (signBitMask - 1);
+ const byte data = (packet & dataBitMask);
+
+ int32 delta = imcTable[curTablePos] * (2 * data + 1) >> (curTableEntryBitCount - 1);
+
+ // The topmost bit in the data packet tells is a sign bit
+ if ((packet & signBitMask) != 0) {
+ delta = -delta;
+ }
+
+ // Accumulate the delta onto the output data
+ outputWord += delta;
+
+ // Clip outputWord to 16 bit signed, and write it into the destination stream
+ if (outputWord > 0x7fff)
+ outputWord = 0x7fff;
+ if (outputWord < -0x8000)
+ outputWord = -0x8000;
+ WRITE_BE_UINT16(dst + destPos, outputWord);
+ destPos += channels << 1;
+
+ // Adjust the curTablePos
+ curTablePos += (int8)imxOtherTable[curTableEntryBitCount - 2][data];
+ if (curTablePos < 0)
+ curTablePos = 0;
+ else if (curTablePos >= ARRAYSIZE(imcTable))
+ curTablePos = ARRAYSIZE(imcTable) - 1;
+ }
+ }
+ }
+ break;
+
+ default:
+ error("BundleCodecs::decompressCodec() Unknown codec %d!", (int)codec);
+ output_size = 0;
+ break;
+ }
+
+ return output_size;
+}
+
+} // End of namespace BundleCodecs
+
+} // End of namespace Scumm
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(DimuseCodecs)
+_GSETPTR(Scumm::BundleCodecs::imcTable, GBVARS_IMCTABLE_INDEX, int16, GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(DimuseCodecs)
+_GRELEASEPTR(GBVARS_IMCTABLE_INDEX, GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_music.cpp b/engines/scumm/imuse_digi/dimuse_music.cpp
new file mode 100644
index 0000000000..4c0cedc57c
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_music.cpp
@@ -0,0 +1,444 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "scumm/scumm.h"
+#include "scumm/imuse_digi/dimuse.h"
+
+namespace Scumm {
+
+#define DIG_STATE_OFFSET 11
+#define DIG_SEQ_OFFSET (DIG_STATE_OFFSET + 65)
+#define COMI_STATE_OFFSET 3
+
+void IMuseDigital::setDigMusicState(int stateId) {
+ int l, num = -1;
+
+ for (l = 0; _digStateMusicTable[l].soundId != -1; l++) {
+ if ((_digStateMusicTable[l].soundId == stateId)) {
+ debug(5, "Set music state: %s, %s", _digStateMusicTable[l].name, _digStateMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+
+ if (num == -1) {
+ for (l = 0; _digStateMusicMap[l].roomId != -1; l++) {
+ if ((_digStateMusicMap[l].roomId == stateId)) {
+ break;
+ }
+ }
+ num = l;
+
+ int offset = _attributes[_digStateMusicMap[num].offset];
+ if (offset == 0) {
+ if (_attributes[_digStateMusicMap[num].atribPos] != 0) {
+ num = _digStateMusicMap[num].stateIndex3;
+ } else {
+ num = _digStateMusicMap[num].stateIndex1;
+ }
+ } else {
+ int stateIndex2 = _digStateMusicMap[num].stateIndex2;
+ if (stateIndex2 == 0) {
+ num = _digStateMusicMap[num].stateIndex1 + offset;
+ } else {
+ num = stateIndex2;
+ }
+ }
+ }
+
+ debug(5, "Set music state: %s, %s", _digStateMusicTable[num].name, _digStateMusicTable[num].filename);
+
+ if (_curMusicState == num)
+ return;
+
+ if (_curMusicSeq == 0) {
+ if (num == 0)
+ playDigMusic(NULL, &_digStateMusicTable[0], num, false);
+ else
+ playDigMusic(_digStateMusicTable[num].name, &_digStateMusicTable[num], num, false);
+ }
+
+ _curMusicState = num;
+}
+
+void IMuseDigital::setDigMusicSequence(int seqId) {
+ int l, num = -1;
+
+ if (seqId == 0)
+ seqId = 2000;
+
+ for (l = 0; _digSeqMusicTable[l].soundId != -1; l++) {
+ if ((_digSeqMusicTable[l].soundId == seqId)) {
+ debug(5, "Set music sequence: %s, %s", _digSeqMusicTable[l].name, _digSeqMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+
+ if (num == -1)
+ return;
+
+ if (_curMusicSeq == num)
+ return;
+
+ if (num != 0) {
+ if (_curMusicSeq == 0) {
+ playDigMusic(_digSeqMusicTable[num].name, &_digSeqMusicTable[num], 0, true);
+ _nextSeqToPlay = 0;
+ _attributes[DIG_SEQ_OFFSET + num] = 1;
+ } else {
+ if ((_digSeqMusicTable[_curMusicSeq].opcode == 4) || (_digSeqMusicTable[_curMusicSeq].opcode == 6)) {
+ _nextSeqToPlay = num;
+ return;
+ } else {
+ playDigMusic(_digSeqMusicTable[num].name, &_digSeqMusicTable[num], 0, true);
+ _nextSeqToPlay = 0;
+ _attributes[DIG_SEQ_OFFSET + num] = 1;
+ }
+ }
+ } else {
+ if (_nextSeqToPlay != 0) {
+ playDigMusic(_digSeqMusicTable[_nextSeqToPlay].name, &_digSeqMusicTable[_nextSeqToPlay], 0, true);
+ _attributes[DIG_SEQ_OFFSET + _nextSeqToPlay] = 1;
+ num = _nextSeqToPlay;
+ _nextSeqToPlay = 0;
+ } else {
+ if (_curMusicState != 0) {
+ playDigMusic(_digStateMusicTable[_curMusicState].name, &_digStateMusicTable[_curMusicState], _curMusicState, true);
+ } else
+ playDigMusic(NULL, &_digStateMusicTable[0], _curMusicState, true);
+ num = 0;
+ }
+ }
+
+ _curMusicSeq = num;
+}
+
+void IMuseDigital::playDigMusic(const char *songName, const imuseDigTable *table, int atribPos, bool sequence) {
+ int hookId = 0;
+
+ if (songName != NULL) {
+ if ((_attributes[DIG_SEQ_OFFSET + 38] != 0) && (_attributes[DIG_SEQ_OFFSET + 41] == _attributes[DIG_SEQ_OFFSET + 38])) {
+ if ((atribPos == 43) || (atribPos == 44))
+ hookId = 3;
+ }
+
+ if ((_attributes[DIG_SEQ_OFFSET + 46] != 0) && (_attributes[DIG_SEQ_OFFSET + 48] == 0)) {
+ if ((atribPos == 38) || (atribPos == 39))
+ hookId = 3;
+ }
+
+ if ((_attributes[DIG_SEQ_OFFSET + 53] != 0)) {
+ if ((atribPos == 50) || (atribPos == 51))
+ hookId = 3;
+ }
+
+ if ((atribPos != 0) && (hookId == 0)) {
+ if (table->atribPos != 0)
+ atribPos = table->atribPos;
+ hookId = _attributes[DIG_STATE_OFFSET + atribPos];
+ if (table->hookId != 0) {
+ if ((hookId != 0) && (table->hookId > 1)) {
+ _attributes[DIG_STATE_OFFSET + atribPos] = 2;
+ } else {
+ _attributes[DIG_STATE_OFFSET + atribPos] = hookId + 1;
+ if (table->hookId < hookId + 1)
+ _attributes[DIG_STATE_OFFSET + atribPos] = 1;
+ }
+ }
+ }
+ }
+
+ fadeOutMusic(120);
+
+ switch(table->opcode) {
+ case 0:
+ case 5:
+ case 6:
+ break;
+ case 3:
+ case 4:
+ if (table->filename[0] == 0) {
+ return;
+ }
+ if ((!sequence) && (table->atribPos != 0) &&
+ (table->atribPos == _digStateMusicTable[_curMusicState].atribPos)) {
+ startMusic(table->filename, table->soundId, 0, 127);
+ return;
+ }
+ startMusic(table->filename, table->soundId, hookId, 127);
+ break;
+ }
+}
+
+void IMuseDigital::setComiMusicState(int stateId) {
+ int l, num = -1;
+
+ // This happens at the beginning of Part II, but should apparently not
+ // do anything since the correct music is already playing. A left-over
+ // of some kind?
+
+ if (stateId == 4)
+ return;
+
+ if (stateId == 0)
+ stateId = 1000;
+
+ for (l = 0; _comiStateMusicTable[l].soundId != -1; l++) {
+ if ((_comiStateMusicTable[l].soundId == stateId)) {
+ debug(5, "Set music state: %s, %s", _comiStateMusicTable[l].name, _comiStateMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+ assert(num != -1);
+
+ if (_curMusicState == num)
+ return;
+
+ if (_curMusicSeq == 0) {
+ if (num == 0)
+ playComiMusic(NULL, &_comiStateMusicTable[0], num, false);
+ else
+ playComiMusic(_comiStateMusicTable[num].name, &_comiStateMusicTable[num], num, false);
+ }
+
+ _curMusicState = num;
+}
+
+void IMuseDigital::setComiMusicSequence(int seqId) {
+ int l, num = -1;
+
+ if (seqId == 0)
+ seqId = 2000;
+
+ for (l = 0; _comiSeqMusicTable[l].soundId != -1; l++) {
+ if ((_comiSeqMusicTable[l].soundId == seqId)) {
+ debug(5, "Set music sequence: %s, %s", _comiSeqMusicTable[l].name, _comiSeqMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+ assert(num != -1);
+
+ if (_curMusicSeq == num)
+ return;
+
+ if (num != 0) {
+ if (_curMusicSeq == 0) {
+ playComiMusic(_comiSeqMusicTable[num].name, &_comiSeqMusicTable[num], 0, true);
+ _nextSeqToPlay = 0;
+ } else {
+ if ((_comiSeqMusicTable[_curMusicSeq].opcode == 4) || (_comiSeqMusicTable[_curMusicSeq].opcode == 6)) {
+ _nextSeqToPlay = num;
+ return;
+ } else {
+ playComiMusic(_comiSeqMusicTable[num].name, &_comiSeqMusicTable[num], 0, true);
+ _nextSeqToPlay = 0;
+ }
+ }
+ } else {
+ if (_nextSeqToPlay != 0) {
+ playComiMusic(_comiSeqMusicTable[_nextSeqToPlay].name, &_comiSeqMusicTable[_nextSeqToPlay], 0, true);
+ num = _nextSeqToPlay;
+ _nextSeqToPlay = 0;
+ } else {
+ if (_curMusicState != 0) {
+ playComiMusic(_comiStateMusicTable[_curMusicState].name, &_comiStateMusicTable[_curMusicState], _curMusicState, true);
+ } else
+ playComiMusic(NULL, &_comiStateMusicTable[0], _curMusicState, true);
+ num = 0;
+ }
+ }
+
+ _curMusicSeq = num;
+}
+
+void IMuseDigital::playComiMusic(const char *songName, const imuseComiTable *table, int atribPos, bool sequence) {
+ int hookId = 0;
+
+ if ((songName != NULL) && (atribPos != 0)) {
+ if (table->atribPos != 0)
+ atribPos = table->atribPos;
+ hookId = _attributes[COMI_STATE_OFFSET + atribPos];
+ if (table->hookId != 0) {
+ if ((hookId != 0) && (table->hookId > 1)) {
+ _attributes[COMI_STATE_OFFSET + atribPos] = 2;
+ } else {
+ _attributes[COMI_STATE_OFFSET + atribPos] = hookId + 1;
+ if (table->hookId < hookId + 1)
+ _attributes[COMI_STATE_OFFSET + atribPos] = 1;
+ }
+ }
+ }
+
+ switch(table->opcode) {
+ case 0:
+ case 8:
+ case 9:
+ fadeOutMusic(120);
+ break;
+ case 1:
+ if (table->filename[0] == 0) {
+ fadeOutMusic(120);
+ return;
+ }
+ fadeOutMusic(120);
+ startMusic(table->filename, table->soundId, 0, 1);
+ setFade(table->soundId, 127, 120);
+ break;
+ case 2:
+ if (table->filename[0] == 0) {
+ fadeOutMusic(60);
+ return;
+ }
+ fadeOutMusic(table->fadeOut60TicksDelay);
+ startMusic(table->filename, table->soundId, table->hookId, 127);
+ break;
+ case 3:
+ case 4:
+ case 12:
+ if (table->filename[0] == 0) {
+ fadeOutMusic(60);
+ return;
+ }
+ fadeOutMusic(table->fadeOut60TicksDelay);
+ if ((!sequence) && (table->atribPos != 0) &&
+ (table->atribPos == _comiStateMusicTable[_curMusicState].atribPos)) {
+ startMusic(table->filename, table->soundId, 0, 127);
+ return;
+ }
+ if (table->opcode == 12) {
+ startMusic(table->filename, table->soundId, table->hookId, 127);
+ } else {
+ startMusic(table->filename, table->soundId, hookId, 127);
+ }
+ break;
+ }
+}
+
+void IMuseDigital::setFtMusicState(int stateId) {
+ if (stateId > 48)
+ return;
+
+ debug(5, "State music: %s, %s", _ftStateMusicTable[stateId].name, _ftStateMusicTable[stateId].audioName);
+
+ if (_curMusicState == stateId)
+ return;
+
+ if (_curMusicSeq == 0) {
+ if (stateId == 0)
+ playFtMusic(NULL, 0, 0);
+ else
+ playFtMusic(_ftStateMusicTable[stateId].audioName, _ftStateMusicTable[stateId].opcode, _ftStateMusicTable[stateId].volume);
+ }
+
+ _curMusicState = stateId;
+}
+
+void IMuseDigital::setFtMusicSequence(int seqId) {
+ if (seqId > 52)
+ return;
+
+ debug(5, "Sequence music: %s", _ftSeqNames[seqId].name);
+
+ if (_curMusicSeq == seqId)
+ return;
+
+ if (seqId == 0) {
+ if (_curMusicState == 0)
+ playFtMusic(NULL, 0, 0);
+ else {
+ playFtMusic(_ftStateMusicTable[_curMusicState].audioName, _ftStateMusicTable[_curMusicState].opcode, _ftStateMusicTable[_curMusicState].volume);
+ }
+ } else {
+ int seq = (seqId - 1) * 4;
+ playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].opcode, _ftSeqMusicTable[seq].volume);
+ }
+
+ _curMusicSeq = seqId;
+ _curMusicCue = 0;
+}
+
+void IMuseDigital::setFtMusicCuePoint(int cueId) {
+ if (cueId > 3)
+ return;
+
+ debug(5, "Cue point sequence: %d", cueId);
+
+ if (_curMusicSeq == 0)
+ return;
+
+ if (_curMusicCue == cueId)
+ return;
+
+ if (cueId == 0)
+ playFtMusic(NULL, 0, 0);
+ else {
+ int seq = ((_curMusicSeq - 1) * 4) + cueId;
+ playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].opcode, _ftSeqMusicTable[seq].volume);
+ }
+
+ _curMusicCue = cueId;
+}
+
+void IMuseDigital::setAudioNames(int32 num, char *names) {
+ free(_audioNames);
+ _numAudioNames = num;
+ _audioNames = names;
+}
+
+int IMuseDigital::getSoundIdByName(const char *soundName) {
+ if (soundName && soundName[0] != 0) {
+ for (int r = 0; r < _numAudioNames; r++) {
+ if (strcmp(soundName, &_audioNames[r * 9]) == 0) {
+ return r;
+ }
+ }
+ }
+
+ return -1;
+}
+
+void IMuseDigital::playFtMusic(const char *songName, int opcode, int volume) {
+ fadeOutMusic(200);
+
+ switch(opcode) {
+ case 0:
+ case 4:
+ break;
+ case 1:
+ case 2:
+ case 3:
+ {
+ int soundId = getSoundIdByName(songName);
+ if (soundId != -1) {
+ startMusic(soundId, volume);
+ }
+ }
+ break;
+ }
+}
+
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_script.cpp b/engines/scumm/imuse_digi/dimuse_script.cpp
new file mode 100644
index 0000000000..83deb91e54
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_script.cpp
@@ -0,0 +1,409 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "common/timer.h"
+
+#include "scumm/actor.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+void IMuseDigital::parseScriptCmds(int cmd, int b, int c, int d, int e, int f, int g, int h) {
+ int soundId = b;
+ int sub_cmd = c;
+
+ if (!cmd)
+ return;
+
+ switch (cmd) {
+ case 10: // ImuseStopAllSounds
+ stopAllSounds();
+ break;
+ case 12: // ImuseSetParam
+ switch (sub_cmd) {
+ case 0x400: // select group volume
+ selectVolumeGroup(soundId, d);
+ break;
+ case 0x500: // set priority
+ setPriority(soundId, d);
+ break;
+ case 0x600: // set volume
+ setVolume(soundId, d);
+ break;
+ case 0x700: // set pan
+ setPan(soundId, d);
+ break;
+ default:
+ warning("IMuseDigital::doCommand SetParam DEFAULT command %d", sub_cmd);
+ break;
+ }
+ break;
+ case 14: // ImuseFadeParam
+ switch (sub_cmd) {
+ case 0x600: // set volume fading
+ if ((d != 0) && (e == 0))
+ setVolume(soundId, d);
+ else if ((d == 0) && (e == 0))
+ stopSound(soundId);
+ else
+ setFade(soundId, d, e);
+ break;
+ default:
+ warning("IMuseDigital::doCommand FadeParam DEFAULT sub command %d", sub_cmd);
+ break;
+ }
+ break;
+ case 25: // ImuseStartStream
+ debug(5, "ImuseStartStream (%d, %d, %d)", soundId, c, d);
+ break;
+ case 26: // ImuseSwitchStream
+ debug(5, "ImuseSwitchStream (%d, %d, %d, %d, %d)", soundId, c, d, e, f);
+ break;
+ case 0x1000: // ImuseSetState
+ debug(5, "ImuseSetState (%d)", b);
+ if ((_vm->_gameId == GID_DIG) && (_vm->_features & GF_DEMO)) {
+ if (b == 1) {
+ fadeOutMusic(200);
+ startMusic(1, 127);
+ } else {
+ if (getSoundStatus(2) == 0) {
+ fadeOutMusic(200);
+ startMusic(2, 127);
+ }
+ }
+ } else if ((_vm->_gameId == GID_CMI) && (_vm->_features & GF_DEMO)) {
+ fadeOutMusic(120);
+ if (b == 2) {
+ startMusic("in1.imx", 1100, 0, 127);
+ } else if (b == 4) {
+ startMusic("in2.imx", 1120, 0, 127);
+ } else if (b == 8) {
+ startMusic("out1.imx", 1140, 0, 127);
+ } else if (b == 9) {
+ startMusic("out2.imx", 1150, 0, 127);
+ } else if (b == 16) {
+ startMusic("gun.imx", 1210, 0, 127);
+ } else {
+ warning("imuse digital: set state unknown for cmi demo: %d, room: %d", b, _vm->_currentRoom);
+ }
+ } else if (_vm->_gameId == GID_DIG) {
+ setDigMusicState(b);
+ } else if (_vm->_gameId == GID_CMI) {
+ setComiMusicState(b);
+ } else if (_vm->_gameId == GID_FT) {
+ setFtMusicState(b);
+ }
+ break;
+ case 0x1001: // ImuseSetSequence
+ debug(5, "ImuseSetSequence (%d)", b);
+ if (_vm->_gameId == GID_DIG) {
+ setDigMusicSequence(b);
+ } else if (_vm->_gameId == GID_CMI) {
+ setComiMusicSequence(b);
+ } else if (_vm->_gameId == GID_FT) {
+ setFtMusicSequence(b);
+ }
+ break;
+ case 0x1002: // ImuseSetCuePoint
+ debug(5, "ImuseSetCuePoint (%d)", b);
+ if (_vm->_gameId == GID_FT) {
+ setFtMusicCuePoint(b);
+ }
+ break;
+ case 0x1003: // ImuseSetAttribute
+ debug(5, "ImuseSetAttribute (%d, %d)", b, c);
+ assert((_vm->_gameId == GID_DIG) || (_vm->_gameId == GID_FT));
+ if (_vm->_gameId == GID_DIG) {
+ _attributes[b] = c;
+ }
+ break;
+ case 0x2000: // ImuseSetGroupSfxVolume
+ debug(5, "ImuseSetGroupSFXVolume (%d)", b);
+// setGroupSfxVolume(b);
+ break;
+ case 0x2001: // ImuseSetGroupVoiceVolume
+ debug(5, "ImuseSetGroupVoiceVolume (%d)", b);
+// setGroupVoiceVolume(b);
+ break;
+ case 0x2002: // ImuseSetGroupMusicVolume
+ debug(5, "ImuseSetGroupMusicVolume (%d)", b);
+// setGroupMusicVolume(b);
+ break;
+ default:
+ error("IMuseDigital::doCommand DEFAULT command %d", cmd);
+ }
+}
+
+void IMuseDigital::flushTracks() {
+ Common::StackLock lock(_mutex, "IMuseDigital::flushTracks()");
+ debug(5, "flushTracks()");
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && (track->readyToRemove ||
+ (_vm->_insaneRunning && track->toBeRemoved))) { // INSANE hack for sync timer mode
+ if (track->stream) {
+ if (!track->stream->endOfStream()) {
+ track->stream->finish();
+ }
+ if (track->stream->endOfStream()
+ || _vm->_mixer->isPaused() // hack for paused Mixer
+ || _vm->_insaneRunning) { // INSANE hack for sync timer mode
+ _vm->_mixer->stopHandle(track->handle);
+ delete track->stream;
+ track->stream = NULL;
+ _sound->closeSound(track->soundHandle);
+ track->soundHandle = NULL;
+ track->used = false;
+ }
+ } else if (track->stream2) {
+ _vm->_mixer->stopHandle(track->handle);
+ delete track->stream2;
+ track->stream2 = NULL;
+ track->used = false;
+ }
+ }
+ }
+}
+
+void IMuseDigital::refreshScripts() {
+ Common::StackLock lock(_mutex, "IMuseDigital::refreshScripts()");
+ debug(5, "refreshScripts()");
+ bool found = false;
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ found = true;
+ }
+ }
+
+ if (!found && (_curMusicSeq != 0)) {
+ debug(5, "refreshScripts() Start Sequence");
+ parseScriptCmds(0x1001, 0, 0, 0, 0, 0, 0, 0);
+ }
+}
+
+void IMuseDigital::startVoice(int soundId, AudioStream *input) {
+ debug(5, "startVoiceStream(%d)", soundId);
+ startSound(soundId, "", 0, IMUSE_VOLGRP_VOICE, input, 0, 127, 127);
+}
+
+void IMuseDigital::startVoice(int soundId, const char *soundName) {
+ debug(5, "startVoiceBundle(%s)", soundName);
+ startSound(soundId, soundName, IMUSE_BUNDLE, IMUSE_VOLGRP_VOICE, NULL, 0, 127, 127);
+}
+
+void IMuseDigital::startMusic(int soundId, int volume) {
+ debug(5, "startMusicResource(%d)", soundId);
+ startSound(soundId, "", IMUSE_RESOURCE, IMUSE_VOLGRP_MUSIC, NULL, 0, volume, 126);
+}
+
+void IMuseDigital::startMusic(const char *soundName, int soundId, int hookId, int volume) {
+ debug(5, "startMusicBundle(%s)", soundName);
+ startSound(soundId, soundName, IMUSE_BUNDLE, IMUSE_VOLGRP_MUSIC, NULL, hookId, volume, 126);
+}
+
+void IMuseDigital::startSfx(int soundId, int priority) {
+ debug(5, "startSfx(%d)", soundId);
+ startSound(soundId, "", IMUSE_RESOURCE, IMUSE_VOLGRP_SFX, NULL, 0, 127, priority);
+}
+
+void IMuseDigital::getLipSync(int soundId, int syncId, int32 msPos, int32 &width, int32 &height) {
+ int32 sync_size;
+ byte *sync_ptr;
+
+ msPos /= 16;
+ if (msPos < 65536) {
+ Common::StackLock lock(_mutex, "IMuseDigital::getLipSync()");
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ _sound->getSyncSizeAndPtrById(track->soundHandle, syncId, sync_size, &sync_ptr);
+ if ((sync_size != 0) && (sync_ptr != NULL)) {
+ sync_size /= 4;
+ while (sync_size--) {
+ if (READ_BE_UINT16(sync_ptr) >= msPos)
+ break;
+ sync_ptr += 4;
+ }
+ if (sync_size < 0)
+ sync_ptr -= 4;
+ else
+ if (READ_BE_UINT16(sync_ptr) > msPos)
+ sync_ptr -= 4;
+
+ width = sync_ptr[2];
+ height = sync_ptr[3];
+ return;
+ }
+ }
+ }
+ }
+}
+
+int32 IMuseDigital::getPosInMs(int soundId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::getPosInMs()");
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ int32 pos = (5 * (track->dataOffset + track->regionOffset)) / (track->iteration / 200);
+ return pos;
+ }
+ }
+
+ return 0;
+}
+
+int IMuseDigital::getSoundStatus(int sound) const {
+ Common::StackLock lock(_mutex, "IMuseDigital::getSoundStatus()");
+ debug(5, "IMuseDigital::getSoundStatus(%d)", sound);
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->soundId == sound) {
+ if ((track->stream2 && _vm->_mixer->isSoundHandleActive(track->handle)) ||
+ (track->stream && track->used && !track->readyToRemove)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void IMuseDigital::stopSound(int soundId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::stopSound()");
+ debug(5, "IMuseDigital::stopSound(%d)", soundId);
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->toBeRemoved = true;
+ }
+ }
+}
+
+int32 IMuseDigital::getCurMusicPosInMs() {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicPosInMs()");
+ int soundId = -1;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ soundId = track->soundId;
+ }
+ }
+
+ int32 msPos = getPosInMs(soundId);
+ debug(5, "IMuseDigital::getCurMusicPosInMs(%d) = %d", soundId, msPos);
+ return msPos;
+}
+
+int32 IMuseDigital::getCurVoiceLipSyncWidth() {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurVoiceLipSyncWidth()");
+ int32 msPos = getPosInMs(kTalkSoundID) + 50;
+ int32 width = 0, height = 0;
+
+ debug(5, "IMuseDigital::getCurVoiceLipSyncWidth(%d)", kTalkSoundID);
+ getLipSync(kTalkSoundID, 0, msPos, width, height);
+ return width;
+}
+
+int32 IMuseDigital::getCurVoiceLipSyncHeight() {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurVoiceLipSyncHeight()");
+ int32 msPos = getPosInMs(kTalkSoundID) + 50;
+ int32 width = 0, height = 0;
+
+ debug(5, "IMuseDigital::getCurVoiceLipSyncHeight(%d)", kTalkSoundID);
+ getLipSync(kTalkSoundID, 0, msPos, width, height);
+ return height;
+}
+
+int32 IMuseDigital::getCurMusicLipSyncWidth(int syncId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicLipSyncWidth()");
+ int soundId = -1;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ soundId = track->soundId;
+ }
+ }
+
+ int32 msPos = getPosInMs(soundId) + 50;
+ int32 width = 0, height = 0;
+
+ debug(5, "IMuseDigital::getCurVoiceLipSyncWidth(%d, %d)", soundId, msPos);
+ getLipSync(soundId, syncId, msPos, width, height);
+ return width;
+}
+
+int32 IMuseDigital::getCurMusicLipSyncHeight(int syncId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicLipSyncHeight()");
+ int soundId = -1;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ soundId = track->soundId;
+ }
+ }
+
+ int32 msPos = getPosInMs(soundId) + 50;
+ int32 width = 0, height = 0;
+
+ debug(5, "IMuseDigital::getCurVoiceLipSyncHeight(%d, %d)", soundId, msPos);
+ getLipSync(soundId, syncId, msPos, width, height);
+ return height;
+}
+
+void IMuseDigital::stopAllSounds() {
+ debug(5, "IMuseDigital::stopAllSounds");
+
+ for (;;) {
+ bool foundNotRemoved = false;
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used) {
+ track->toBeRemoved = true;
+ foundNotRemoved = true;
+ }
+ }
+ if (!foundNotRemoved)
+ break;
+ flushTracks();
+ _vm->_system->delayMillis(50);
+#if defined(_WIN32_WCE) || defined (PALMOS_MODE) || defined(__SYMBIAN32__)
+ _vm->parseEvents(); // timers are events, we need to consume them
+#endif
+ }
+}
+
+void IMuseDigital::pause(bool p) {
+ _pause = p;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.cpp b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
new file mode 100644
index 0000000000..7d3d06f16e
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
@@ -0,0 +1,625 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/util.h"
+
+#include "sound/voc.h"
+#include "sound/vorbis.h"
+#include "sound/mp3.h"
+
+#include "scumm/scumm.h"
+#include "scumm/util.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse_digi/dimuse_sndmgr.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+namespace Scumm {
+
+ImuseDigiSndMgr::ImuseDigiSndMgr(ScummEngine *scumm) {
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ memset(&_sounds[l], 0, sizeof(soundStruct));
+ }
+ _vm = scumm;
+ _disk = 0;
+ _cacheBundleDir = new BundleDirCache();
+ BundleCodecs::initializeImcTables();
+}
+
+ImuseDigiSndMgr::~ImuseDigiSndMgr() {
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ closeSound(&_sounds[l]);
+ }
+
+ delete _cacheBundleDir;
+}
+
+void ImuseDigiSndMgr::countElements(byte *ptr, int &numRegions, int &numJumps, int &numSyncs) {
+ uint32 tag;
+ int32 size = 0;
+
+ do {
+ tag = READ_BE_UINT32(ptr); ptr += 4;
+ switch(tag) {
+ case MKID_BE('TEXT'):
+ case MKID_BE('STOP'):
+ case MKID_BE('FRMT'):
+ case MKID_BE('DATA'):
+ size = READ_BE_UINT32(ptr); ptr += size + 4;
+ break;
+ case MKID_BE('REGN'):
+ numRegions++;
+ size = READ_BE_UINT32(ptr); ptr += size + 4;
+ break;
+ case MKID_BE('JUMP'):
+ numJumps++;
+ size = READ_BE_UINT32(ptr); ptr += size + 4;
+ break;
+ case MKID_BE('SYNC'):
+ numSyncs++;
+ size = READ_BE_UINT32(ptr); ptr += size + 4;
+ break;
+ default:
+ error("ImuseDigiSndMgr::countElements() Unknown sfx header '%s'", tag2str(tag));
+ }
+ } while (tag != MKID_BE('DATA'));
+}
+
+void ImuseDigiSndMgr::prepareSoundFromRMAP(Common::File *file, soundStruct *sound, int32 offset, int32 size) {
+ int l;
+
+ file->seek(offset, SEEK_SET);
+ uint32 tag = file->readUint32BE();
+ assert(tag == 'RMAP');
+ int32 version = file->readUint32BE();
+ if (version != 2) {
+ error("ImuseDigiSndMgr::prepareSoundFromRMAP: Wrong version number, expected 2, but it's: %d.", version);
+ }
+ sound->bits = file->readUint32BE();
+ sound->freq = file->readUint32BE();
+ sound->channels = file->readUint32BE();
+ sound->numRegions = file->readUint32BE();
+ sound->numJumps = file->readUint32BE();
+ sound->numSyncs = file->readUint32BE();
+ sound->region = (_region *)malloc(sizeof(_region) * sound->numRegions);
+ sound->jump = (_jump *)malloc(sizeof(_jump) * sound->numJumps);
+ sound->sync = (_sync *)malloc(sizeof(_sync) * sound->numSyncs);
+ for (l = 0; l < sound->numRegions; l++) {
+ sound->region[l].offset = file->readUint32BE();
+ sound->region[l].length = file->readUint32BE();
+ }
+ for (l = 0; l < sound->numJumps; l++) {
+ sound->jump[l].offset = file->readUint32BE();
+ sound->jump[l].dest = file->readUint32BE();
+ sound->jump[l].hookId = file->readUint32BE();
+ sound->jump[l].fadeDelay = file->readUint32BE();
+ }
+ for (l = 0; l < sound->numSyncs; l++) {
+ sound->sync[l].size = file->readUint32BE();
+ sound->sync[l].ptr = (byte *)malloc(sound->sync[l].size);
+ file->read(sound->sync[l].ptr, sound->sync[l].size);
+ }
+}
+
+void ImuseDigiSndMgr::prepareSound(byte *ptr, soundStruct *sound) {
+ if (READ_UINT32(ptr) == MKID('Crea')) {
+ bool quit = false;
+ int len;
+
+ int32 offset = READ_LE_UINT16(ptr + 20);
+ int16 code = READ_LE_UINT16(ptr + 24);
+
+ sound->region = (_region *)malloc(sizeof(_region) * 70);
+ sound->jump = (_jump *)malloc(sizeof(_jump));
+ sound->resPtr = ptr;
+ sound->bits = 8;
+ sound->channels = 1;
+
+ while (!quit) {
+ len = READ_LE_UINT32(ptr + offset);
+ code = len & 0xFF;
+ if ((code != 0) && (code != 1) && (code != 6) && (code != 7)) {
+ // try again with 2 bytes forward (workaround for some FT sounds (ex.362, 363)
+ offset += 2;
+ len = READ_LE_UINT32(ptr + offset);
+ code = len & 0xFF;
+ if ((code != 0) && (code != 1) && (code != 6) && (code != 7)) {
+ error("Invalid code in VOC file : %d", code);
+ }
+ }
+ offset += 4;
+ len >>= 8;
+ switch(code) {
+ case 0:
+ quit = true;
+ break;
+ case 1:
+ {
+ int time_constant = ptr[offset];
+ offset += 2;
+ len -= 2;
+ sound->freq = getSampleRateFromVOCRate(time_constant);
+ sound->region[sound->numRegions].offset = offset;
+ sound->region[sound->numRegions].length = len;
+ sound->numRegions++;
+ }
+ break;
+ case 6: // begin of loop
+ sound->jump[0].dest = offset + 8;
+ sound->jump[0].hookId = 0;
+ sound->jump[0].fadeDelay = 0;
+ break;
+ case 7: // end of loop
+ sound->jump[0].offset = offset - 4;
+ sound->numJumps++;
+ sound->region[sound->numRegions].offset = offset - 4;
+ sound->region[sound->numRegions].length = 0;
+ sound->numRegions++;
+ break;
+ default:
+ error("Invalid code in VOC file : %d", code);
+ quit = true;
+ break;
+ }
+ offset += len;
+ }
+ } else if (READ_UINT32(ptr) == MKID('iMUS')) {
+ uint32 tag;
+ int32 size = 0;
+ byte *s_ptr = ptr;
+ ptr += 16;
+
+ int curIndexRegion = 0;
+ int curIndexJump = 0;
+ int curIndexSync = 0;
+
+ sound->numRegions = 0;
+ sound->numJumps = 0;
+ sound->numSyncs = 0;
+ countElements(ptr, sound->numRegions, sound->numJumps, sound->numSyncs);
+ sound->region = (_region *)malloc(sizeof(_region) * sound->numRegions);
+ sound->jump = (_jump *)malloc(sizeof(_jump) * sound->numJumps);
+ sound->sync = (_sync *)malloc(sizeof(_sync) * sound->numSyncs);
+
+ do {
+ tag = READ_BE_UINT32(ptr); ptr += 4;
+ switch(tag) {
+ case MKID_BE('FRMT'):
+ ptr += 12;
+ sound->bits = READ_BE_UINT32(ptr); ptr += 4;
+ sound->freq = READ_BE_UINT32(ptr); ptr += 4;
+ sound->channels = READ_BE_UINT32(ptr); ptr += 4;
+ break;
+ case MKID_BE('TEXT'):
+ case MKID_BE('STOP'):
+ size = READ_BE_UINT32(ptr); ptr += size + 4;
+ break;
+ case MKID_BE('REGN'):
+ ptr += 4;
+ sound->region[curIndexRegion].offset = READ_BE_UINT32(ptr); ptr += 4;
+ sound->region[curIndexRegion].length = READ_BE_UINT32(ptr); ptr += 4;
+ curIndexRegion++;
+ break;
+ case MKID_BE('JUMP'):
+ ptr += 4;
+ sound->jump[curIndexJump].offset = READ_BE_UINT32(ptr); ptr += 4;
+ sound->jump[curIndexJump].dest = READ_BE_UINT32(ptr); ptr += 4;
+ sound->jump[curIndexJump].hookId = READ_BE_UINT32(ptr); ptr += 4;
+ sound->jump[curIndexJump].fadeDelay = READ_BE_UINT32(ptr); ptr += 4;
+ curIndexJump++;
+ break;
+ case MKID_BE('SYNC'):
+ size = READ_BE_UINT32(ptr); ptr += 4;
+ sound->sync[curIndexSync].size = size;
+ sound->sync[curIndexSync].ptr = (byte *)malloc(size);
+ memcpy(sound->sync[curIndexSync].ptr, ptr, size);
+ curIndexSync++;
+ ptr += size;
+ break;
+ case MKID_BE('DATA'):
+ ptr += 4;
+ break;
+ default:
+ error("ImuseDigiSndMgr::prepareSound(%d/%s) Unknown sfx header '%s'", sound->soundId, sound->name, tag2str(tag));
+ }
+ } while (tag != MKID_BE('DATA'));
+ sound->offsetData = ptr - s_ptr;
+ } else {
+ error("ImuseDigiSndMgr::prepareSound(): Unknown sound format");
+ }
+}
+
+ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::allocSlot() {
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ if (!_sounds[l].inUse) {
+ _sounds[l].inUse = true;
+ return &_sounds[l];
+ }
+ }
+
+ return NULL;
+}
+
+bool ImuseDigiSndMgr::openMusicBundle(soundStruct *sound, int disk) {
+ bool result = false;
+
+ sound->bundle = new BundleMgr(_cacheBundleDir);
+ if (_vm->_gameId == GID_CMI) {
+ if (_vm->_features & GF_DEMO) {
+ result = sound->bundle->open("music.bun", sound->compressed);
+ } else {
+ char musicfile[20];
+ if (disk == -1)
+ disk = _vm->VAR(_vm->VAR_CURRENTDISK);
+ sprintf(musicfile, "musdisk%d.bun", disk);
+// if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) {
+// _vm->_imuseDigital->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_imuseDigital->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_imuseDigital->stopAllSounds();
+// sound->bundle->closeFile();
+// }
+
+ result = sound->bundle->open(musicfile, sound->compressed, true);
+
+ // FIXME: Shouldn't we only set _disk if result == true?
+ _disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK);
+ }
+ } else if (_vm->_gameId == GID_DIG)
+ result = sound->bundle->open("digmusic.bun", sound->compressed, true);
+ else
+ error("ImuseDigiSndMgr::openMusicBundle() Don't know which bundle file to load");
+
+ _vm->VAR(_vm->VAR_MUSIC_BUNDLE_LOADED) = result ? 1 : 0;
+
+ return result;
+}
+
+bool ImuseDigiSndMgr::openVoiceBundle(soundStruct *sound, int disk) {
+ bool result = false;
+
+ sound->bundle = new BundleMgr(_cacheBundleDir);
+ if (_vm->_gameId == GID_CMI) {
+ if (_vm->_features & GF_DEMO) {
+ result = sound->bundle->open("voice.bun", sound->compressed);
+ } else {
+ char voxfile[20];
+ if (disk == -1)
+ disk = _vm->VAR(_vm->VAR_CURRENTDISK);
+ sprintf(voxfile, "voxdisk%d.bun", disk);
+// if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) {
+// _vm->_imuseDigital->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_imuseDigital->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_imuseDigital->stopAllSounds();
+// sound->bundle->closeFile();
+// }
+
+ result = sound->bundle->open(voxfile, sound->compressed);
+
+ // FIXME: Shouldn't we only set _disk if result == true?
+ _disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK);
+ }
+ } else if (_vm->_gameId == GID_DIG)
+ result = sound->bundle->open("digvoice.bun", sound->compressed);
+ else
+ error("ImuseDigiSndMgr::openVoiceBundle() Don't know which bundle file to load");
+
+ _vm->VAR(_vm->VAR_VOICE_BUNDLE_LOADED) = result ? 1 : 0;
+
+ return result;
+}
+
+ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::openSound(int32 soundId, const char *soundName, int soundType, int volGroupId, int disk) {
+ assert(soundId >= 0);
+ assert(soundType);
+
+ soundStruct *sound = allocSlot();
+ if (!sound) {
+ error("ImuseDigiSndMgr::openSound() can't alloc free sound slot");
+ }
+
+ const bool header_outside = ((_vm->_gameId == GID_CMI) && !(_vm->_features & GF_DEMO));
+ bool result = false;
+ byte *ptr = NULL;
+
+ switch (soundType) {
+ case IMUSE_RESOURCE:
+ assert(soundName[0] == 0); // Paranoia check
+
+ _vm->ensureResourceLoaded(rtSound, soundId);
+ _vm->res.lock(rtSound, soundId);
+ ptr = _vm->getResourceAddress(rtSound, soundId);
+ if (ptr == NULL) {
+ closeSound(sound);
+ return NULL;
+ }
+ sound->resPtr = ptr;
+ break;
+ case IMUSE_BUNDLE:
+ if (volGroupId == IMUSE_VOLGRP_VOICE)
+ result = openVoiceBundle(sound, disk);
+ else if (volGroupId == IMUSE_VOLGRP_MUSIC)
+ result = openMusicBundle(sound, disk);
+ else
+ error("ImuseDigiSndMgr::openSound() Don't know how load sound: %d", soundId);
+ if (!result) {
+ closeSound(sound);
+ return NULL;
+ }
+ if (sound->compressed) {
+ char fileName[24];
+ int32 offset = 0, size = 0;
+ sprintf(fileName, "%s.map", soundName);
+ Common::File *rmapFile = sound->bundle->getFile(fileName, offset, size);
+ if (!rmapFile) {
+ closeSound(sound);
+ return NULL;
+ }
+ prepareSoundFromRMAP(rmapFile, sound, offset, size);
+ strcpy(sound->name, soundName);
+ sound->soundId = soundId;
+ sound->type = soundType;
+ sound->volGroupId = volGroupId;
+ sound->disk = _disk;
+ return sound;
+ } else if (soundName[0] == 0) {
+ if (sound->bundle->decompressSampleByIndex(soundId, 0, 0x2000, &ptr, 0, header_outside) == 0 || ptr == NULL) {
+ closeSound(sound);
+ return NULL;
+ }
+ } else {
+ if (sound->bundle->decompressSampleByName(soundName, 0, 0x2000, &ptr, header_outside) == 0 || ptr == NULL) {
+ closeSound(sound);
+ return NULL;
+ }
+ }
+ sound->resPtr = 0;
+ break;
+ default:
+ error("ImuseDigiSndMgr::openSound() Unknown soundType %d (trying to load sound %d)", soundType, soundId);
+ }
+
+ strcpy(sound->name, soundName);
+ sound->soundId = soundId;
+ sound->type = soundType;
+ sound->volGroupId = volGroupId;
+ sound->disk = _disk;
+ prepareSound(ptr, sound);
+ if ((soundType == IMUSE_BUNDLE) && !sound->compressed) {
+ free(ptr);
+ }
+ return sound;
+}
+
+void ImuseDigiSndMgr::closeSound(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+
+ if (soundHandle->resPtr) {
+ bool found = false;
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ if ((_sounds[l].soundId == soundHandle->soundId) && (&_sounds[l] != soundHandle))
+ found = true;
+ }
+ if (!found)
+ _vm->res.unlock(rtSound, soundHandle->soundId);
+ }
+
+ if (soundHandle->compressedStream)
+ delete soundHandle->compressedStream;
+
+ delete soundHandle->bundle;
+
+ for (int r = 0; r < soundHandle->numSyncs; r++)
+ free(soundHandle->sync[r].ptr);
+ free(soundHandle->region);
+ free(soundHandle->jump);
+ free(soundHandle->sync);
+ memset(soundHandle, 0, sizeof(soundStruct));
+}
+
+ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::cloneSound(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+
+ return openSound(soundHandle->soundId, soundHandle->name, soundHandle->type, soundHandle->volGroupId, soundHandle->disk);
+}
+
+bool ImuseDigiSndMgr::checkForProperHandle(soundStruct *soundHandle) {
+ if (!soundHandle)
+ return false;
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ if (soundHandle == &_sounds[l])
+ return true;
+ }
+ return false;
+}
+
+bool ImuseDigiSndMgr::isCompressed(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->compressed;
+}
+
+int ImuseDigiSndMgr::getFreq(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->freq;
+}
+
+int ImuseDigiSndMgr::getBits(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->bits;
+}
+
+int ImuseDigiSndMgr::getChannels(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->channels;
+}
+
+bool ImuseDigiSndMgr::isEndOfRegion(soundStruct *soundHandle, int region) {
+ assert(checkForProperHandle(soundHandle));
+ assert(region >= 0 && region < soundHandle->numRegions);
+ return soundHandle->endFlag;
+}
+
+int ImuseDigiSndMgr::getNumRegions(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->numRegions;
+}
+
+int ImuseDigiSndMgr::getNumJumps(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->numJumps;
+}
+
+int ImuseDigiSndMgr::getRegionOffset(soundStruct *soundHandle, int region) {
+ debug(5, "getRegionOffset() region:%d", region);
+ assert(checkForProperHandle(soundHandle));
+ assert(region >= 0 && region < soundHandle->numRegions);
+ return soundHandle->region[region].offset;
+}
+
+int ImuseDigiSndMgr::getJumpIdByRegionAndHookId(soundStruct *soundHandle, int region, int hookId) {
+ debug(5, "getJumpIdByRegionAndHookId() region:%d, hookId:%d", region, hookId);
+ assert(checkForProperHandle(soundHandle));
+ assert(region >= 0 && region < soundHandle->numRegions);
+ int32 offset = soundHandle->region[region].offset;
+ for (int l = 0; l < soundHandle->numJumps; l++) {
+ if (offset == soundHandle->jump[l].offset) {
+ if (soundHandle->jump[l].hookId == hookId)
+ return l;
+ }
+ }
+
+ return -1;
+}
+
+void ImuseDigiSndMgr::getSyncSizeAndPtrById(soundStruct *soundHandle, int number, int32 &sync_size, byte **sync_ptr) {
+ assert(checkForProperHandle(soundHandle));
+ assert(number >= 0);
+ if (number < soundHandle->numSyncs) {
+ sync_size = soundHandle->sync[number].size;
+ *sync_ptr = soundHandle->sync[number].ptr;
+ } else {
+ sync_size = 0;
+ *sync_ptr = NULL;
+ }
+}
+
+int ImuseDigiSndMgr::getRegionIdByJumpId(soundStruct *soundHandle, int jumpId) {
+ debug(5, "getRegionIdByJumpId() jumpId:%d", jumpId);
+ assert(checkForProperHandle(soundHandle));
+ assert(jumpId >= 0 && jumpId < soundHandle->numJumps);
+ int32 dest = soundHandle->jump[jumpId].dest;
+ for (int l = 0; l < soundHandle->numRegions; l++) {
+ if (dest == soundHandle->region[l].offset) {
+ return l;
+ }
+ }
+
+ return -1;
+}
+
+int ImuseDigiSndMgr::getJumpHookId(soundStruct *soundHandle, int number) {
+ debug(5, "getJumpHookId() number:%d", number);
+ assert(checkForProperHandle(soundHandle));
+ assert(number >= 0 && number < soundHandle->numJumps);
+ return soundHandle->jump[number].hookId;
+}
+
+int ImuseDigiSndMgr::getJumpFade(soundStruct *soundHandle, int number) {
+ debug(5, "getJumpFade() number:%d", number);
+ assert(checkForProperHandle(soundHandle));
+ assert(number >= 0 && number < soundHandle->numJumps);
+ return soundHandle->jump[number].fadeDelay;
+}
+
+int32 ImuseDigiSndMgr::getDataFromRegion(soundStruct *soundHandle, int region, byte **buf, int32 offset, int32 size) {
+ debug(5, "getDataFromRegion() region:%d, offset:%d, size:%d, numRegions:%d", region, offset, size, soundHandle->numRegions);
+ assert(checkForProperHandle(soundHandle));
+ assert(buf && offset >= 0 && size >= 0);
+ assert(region >= 0 && region < soundHandle->numRegions);
+
+ int32 region_offset = soundHandle->region[region].offset;
+ int32 region_length = soundHandle->region[region].length;
+ int32 offset_data = soundHandle->offsetData;
+ int32 start = region_offset - offset_data;
+
+ if (offset + size + offset_data > region_length) {
+ size = region_length - offset;
+ soundHandle->endFlag = true;
+ } else {
+ soundHandle->endFlag = false;
+ }
+
+ int header_size = soundHandle->offsetData;
+ bool header_outside = ((_vm->_gameId == GID_CMI) && !(_vm->_features & GF_DEMO));
+ if ((soundHandle->bundle) && (!soundHandle->compressed)) {
+ size = soundHandle->bundle->decompressSampleByCurIndex(start + offset, size, buf, header_size, header_outside);
+ } else if (soundHandle->resPtr) {
+ *buf = (byte *)malloc(size);
+ memcpy(*buf, soundHandle->resPtr + start + offset + header_size, size);
+ } else if ((soundHandle->bundle) && (soundHandle->compressed)) {
+ *buf = (byte *)malloc(size);
+ char fileName[24];
+ sprintf(fileName, "%s_reg%03d", soundHandle->name, region);
+ if (scumm_stricmp(fileName, soundHandle->lastFileName) != 0) {
+ int32 offs = 0, len = 0;
+ Common::File *cmpFile;
+ bool oggMode = false;
+ sprintf(fileName, "%s_reg%03d.mp3", soundHandle->name, region);
+ cmpFile = soundHandle->bundle->getFile(fileName, offs, len);
+#ifndef USE_MAD
+ if (len)
+ error("Mad library compiled support needed!");
+#endif
+ if (!len) {
+ sprintf(fileName, "%s_reg%03d.ogg", soundHandle->name, region);
+ cmpFile = soundHandle->bundle->getFile(fileName, offs, len);
+#ifndef USE_VORBIS
+ if (len)
+ error("Vorbis library compiled support needed!");
+#endif
+ assert(len);
+ oggMode = true;
+ }
+ if (!soundHandle->compressedStream) {
+#ifdef USE_VORBIS
+ if (oggMode)
+ soundHandle->compressedStream = makeVorbisStream(cmpFile, len);
+#endif
+#ifdef USE_MAD
+ if (!oggMode)
+ soundHandle->compressedStream = makeMP3Stream(cmpFile, len);
+#endif
+ assert(soundHandle->compressedStream);
+ }
+ strcpy(soundHandle->lastFileName, fileName);
+ }
+ size = soundHandle->compressedStream->readBuffer((int16 *)*buf, size / 2) * 2;
+ if (soundHandle->compressedStream->endOfData()) {
+ delete soundHandle->compressedStream;
+ soundHandle->compressedStream = NULL;
+ soundHandle->lastFileName[0] = 0;
+ }
+ }
+
+ return size;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.h b/engines/scumm/imuse_digi/dimuse_sndmgr.h
new file mode 100644
index 0000000000..5844fa0c1b
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_sndmgr.h
@@ -0,0 +1,139 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef IMUSE_DIGI_SNDMGR_H
+#define IMUSE_DIGI_SNDMGR_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "sound/audiostream.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+namespace Scumm {
+
+class ScummEngine;
+class BundleMgr;
+
+class ImuseDigiSndMgr {
+public:
+
+#define MAX_IMUSE_SOUNDS 16
+
+#define IMUSE_RESOURCE 1
+#define IMUSE_BUNDLE 2
+
+#define IMUSE_VOLGRP_VOICE 1
+#define IMUSE_VOLGRP_SFX 2
+#define IMUSE_VOLGRP_MUSIC 3
+
+private:
+ struct _region {
+ int32 offset; // offset of region
+ int32 length; // lenght of region
+ };
+
+ struct _jump {
+ int32 offset; // jump offset position
+ int32 dest; // jump to dest position
+ byte hookId; // id of hook
+ int16 fadeDelay; // fade delay in ms
+ };
+
+ struct _sync {
+ int32 size; // size of sync
+ byte *ptr; // pointer to sync
+ };
+
+public:
+
+ struct soundStruct {
+ uint16 freq; // frequency
+ byte channels; // stereo or mono
+ byte bits; // 8, 12, 16
+ int numJumps; // number of Jumps
+ int numRegions; // number of Regions
+ int numSyncs; // number of Syncs
+ _region *region;
+ _jump *jump;
+ _sync *sync;
+ bool endFlag;
+ bool inUse;
+ byte *allData;
+ int32 offsetData;
+ byte *resPtr;
+ char name[15];
+ int16 soundId;
+ BundleMgr *bundle;
+ int type;
+ int volGroupId;
+ int disk;
+ AudioStream *compressedStream;
+ bool compressed;
+ char lastFileName[24];
+ };
+
+private:
+
+ soundStruct _sounds[MAX_IMUSE_SOUNDS];
+
+ bool checkForProperHandle(soundStruct *soundHandle);
+ soundStruct *allocSlot();
+ void prepareSound(byte *ptr, soundStruct *sound);
+ void prepareSoundFromRMAP(Common::File *file, soundStruct *sound, int32 offset, int32 size);
+
+ ScummEngine *_vm;
+ byte _disk;
+ BundleDirCache *_cacheBundleDir;
+
+ bool openMusicBundle(soundStruct *sound, int disk);
+ bool openVoiceBundle(soundStruct *sound, int disk);
+
+ void countElements(byte *ptr, int &numRegions, int &numJumps, int &numSyncs);
+
+public:
+
+ ImuseDigiSndMgr(ScummEngine *scumm);
+ ~ImuseDigiSndMgr();
+
+ soundStruct *openSound(int32 soundId, const char *soundName, int soundType, int volGroupId, int disk);
+ void closeSound(soundStruct *soundHandle);
+ soundStruct *cloneSound(soundStruct *soundHandle);
+
+ bool isCompressed(soundStruct *soundHandle);
+ int getFreq(soundStruct *soundHandle);
+ int getBits(soundStruct *soundHandle);
+ int getChannels(soundStruct *soundHandle);
+ bool isEndOfRegion(soundStruct *soundHandle, int region);
+ int getNumRegions(soundStruct *soundHandle);
+ int getNumJumps(soundStruct *soundHandle);
+ int getRegionOffset(soundStruct *soundHandle, int region);
+ int getJumpIdByRegionAndHookId(soundStruct *soundHandle, int region, int hookId);
+ int getRegionIdByJumpId(soundStruct *soundHandle, int jumpId);
+ int getJumpHookId(soundStruct *soundHandle, int number);
+ int getJumpFade(soundStruct *soundHandle, int number);
+ void getSyncSizeAndPtrById(soundStruct *soundHandle, int number, int32 &sync_size, byte **sync_ptr);
+
+ int32 getDataFromRegion(soundStruct *soundHandle, int region, byte **buf, int32 offset, int32 size);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_tables.cpp b/engines/scumm/imuse_digi/dimuse_tables.cpp
new file mode 100644
index 0000000000..f4fd25a160
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_tables.cpp
@@ -0,0 +1,881 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "scumm/imuse_digi/dimuse.h"
+
+namespace Scumm {
+
+#ifdef PALMOS_68K
+const imuseRoomMap *_digStateMusicMap;
+const imuseDigTable *_digStateMusicTable;
+const imuseDigTable *_digSeqMusicTable;
+const imuseComiTable *_comiStateMusicTable;
+const imuseComiTable *_comiSeqMusicTable;
+const imuseFtStateTable *_ftStateMusicTable;
+const imuseFtSeqTable *_ftSeqMusicTable;
+const imuseFtNames *_ftSeqNames;
+#else
+const imuseRoomMap _digStateMusicMap[] = {
+ {0, 0, 0, 0, 0, 0 },
+ {1, 0, 0, 0, 0, 0 },
+ {2, 2, 0, 0, 0, 0 },
+ {4, 3, 0, 0, 0, 0 },
+ {5, 3, 0, 0, 0, 0 },
+ {6, 3, 0, 0, 0, 0 },
+ {7, 3, 0, 0, 0, 0 },
+ {8, 4, 0, 0, 0, 0 },
+ {9, 5, 0, 0, 0, 0 },
+ {10, 4, 0, 0, 0, 0 },
+ {12, 5, 0, 0, 0, 0 },
+ {14, 5, 0, 0, 0, 0 },
+ {15, 6, 29, 7, 0, 0 },
+ {16, 8, 0, 0, 0, 0 },
+ {17, 1, 0, 0, 0, 0 },
+ {18, 9, 0, 0, 0, 0 },
+ {19, 9, 0, 0, 0, 0 },
+ {20, 6, 0, 0, 0, 0 },
+ {21, 6, 0, 0, 0, 0 },
+ {22, 44, 0, 0, 0, 0 },
+ {23, 10, 7, 0, 0, 0 },
+ {24, 26, 0, 0, 0, 0 },
+ {25, 17, 0, 0, 0, 0 },
+ {26, 17, 0, 0, 0, 0 },
+ {27, 18, 0, 0, 0, 0 },
+ {28, 1, 0, 0, 0, 0 },
+ {29, 20, 0, 0, 0, 0 },
+ {30, 22, 0, 0, 0, 0 },
+ {31, 23, 0, 0, 0, 0 },
+ {32, 22, 0, 0, 0, 0 },
+ {33, 26, 0, 0, 0, 0 },
+ {34, 24, 0, 0, 0, 0 },
+ {35, 1, 0, 0, 0, 0 },
+ {36, 1, 0, 0, 0, 0 },
+ {37, 42, 0, 0, 0, 0 },
+ {38, 43, 0, 0, 0, 0 },
+ {39, 44, 0, 0, 0, 0 },
+ {40, 1, 0, 0, 0, 0 },
+ {41, 43, 0, 0, 0, 0 },
+ {42, 44, 0, 0, 0, 0 },
+ {43, 43, 0, 0, 0, 0 },
+ {44, 45, 117,45, 114,46},
+ {47, 1, 0, 0, 0, 0 },
+ {48, 43, 0, 0, 0, 0 },
+ {49, 44, 0, 0, 0, 0 },
+ {51, 1, 0, 0, 0, 0 },
+ {53, 28, 0, 0, 0, 0 },
+ {54, 28, 0, 0, 0, 0 },
+ {55, 29, 0, 0, 0, 0 },
+ {56, 29, 0, 0, 0, 0 },
+ {57, 29, 0, 0, 0, 0 },
+ {58, 31, 0, 0, 0, 0 },
+ {59, 1, 0, 0, 0, 0 },
+ {60, 37, 0, 0, 0, 0 },
+ {61, 39, 0, 0, 0, 0 },
+ {62, 38, 0, 0, 0, 0 },
+ {63, 39, 0, 0, 0, 0 },
+ {64, 39, 0, 0, 0, 0 },
+ {65, 40, 0, 0, 0, 0 },
+ {67, 40, 0, 0, 0, 0 },
+ {68, 39, 0, 0, 0, 0 },
+ {69, 1, 0, 0, 0, 0 },
+ {70, 49, 0, 0, 0, 0 },
+ {73, 50, 0, 0, 0, 0 },
+ {75, 51, 0, 0, 0, 0 },
+ {76, 1, 0, 0, 0, 0 },
+ {77, 52, 7, 0, 0, 0 },
+ {78, 63, 0, 0, 0, 0 },
+ {79, 1, 0, 0, 0, 0 },
+ {82, 21, 0, 0, 0, 0 },
+ {85, 1, 0, 0, 0, 0 },
+ {86, 0, 0, 0, 0, 0 },
+ {89, 33, 6, 35, 5, 34},
+ {90, 16, 0, 0, 0, 0 },
+ {91, 57, 0, 0, 0, 0 },
+ {88, 32, 0, 0, 0, 0 },
+ {92, 25, 0, 0, 0, 0 },
+ {93, 0, 0, 0, 0, 0 },
+ {95, 19, 0, 0, 0, 0 },
+ {80, 41, 0, 0, 0, 0 },
+ {81, 48, 0, 0, 0, 0 },
+ {83, 27, 0, 0, 0, 0 },
+ {94, 36, 0, 0, 0, 0 },
+ {40, 1, 0, 0, 0, 0 },
+ {96, 13, 0, 0, 0, 0 },
+ {97, 14, 0, 0, 0, 0 },
+ {98, 11, 0, 0, 0, 0 },
+ {99, 15, 0, 0, 0, 0 },
+ {100, 17, 0, 0, 0, 0 },
+ {101, 38, 0, 0, 0, 0 },
+ {103, 0, 0, 0, 0, 0 },
+ {104, 0, 0, 0, 0, 0 },
+ {11, 44, 0, 0, 0, 0 },
+ {3, 47, 0, 0, 0, 0 },
+ {105, 30, 128,29, 0, 0 },
+ {106, 0, 0, 0, 0, 0 },
+ {107, 1, 0, 0, 0, 0 },
+ {108, 1, 0, 0, 0, 0 },
+ {47, 1, 0, 0, 0, 0 },
+ {50, 1, 0, 0, 0, 0 },
+ {52, 0, 0, 0, 0, 0 },
+ {71, 1, 0, 0, 0, 0 },
+ {13, 1, 0, 0, 0, 0 },
+ {72, 1, 0, 0, 0, 0 },
+ {46, 33, 6, 35, 5, 34},
+ {74, 1, 0, 0, 0, 0 },
+ {84, 1, 0, 0, 0, 0 },
+ {66, 1, 0, 0, 0, 0 },
+ {102, 1, 0, 0, 0, 0 },
+ {109, 1, 0, 0, 0, 0 },
+ {110, 2, 0, 0, 0, 0 },
+ {45, 1, 0, 0, 0, 0 },
+ {87, 1, 0, 0, 0, 0 },
+ {111, 1, 0, 0, 0, 0 },
+ {-1, 1, 0, 0, 0, 0 }
+};
+
+const imuseDigTable _digStateMusicTable[] = {
+ {0, 1000, "STATE_NULL", 0, 0, ""}, /* 00 */
+ {0, 1001, "stateNoChange", 0, 0, ""}, /* 01 */
+ {3, 1100, "stateAstShip", 2, 0, "ASTERO~1.IMU"}, /* 02 */
+ {3, 1120, "stateAstClose", 2, 0, "ASTERO~2.IMU"}, /* 03 */
+ {3, 1140, "stateAstInside", 0, 0, "ASTERO~3.IMU"}, /* 04 */
+ {3, 1150, "stateAstCore", 0, 2, "ASTERO~4.IMU"}, /* 05 */
+ {3, 1200, "stateCanyonClose", 0, 1, "CANYON~1.IMU"}, /* 06 */
+ {3, 1205, "stateCanyonClose_m", 0, 0, "CANYON~2.IMU"}, /* 07 */
+ {3, 1210, "stateCanyonOver", 0, 1, "CANYON~3.IMU"}, /* 08 */
+ {3, 1220, "stateCanyonWreck", 0, 1, "CANYON~4.IMU"}, /* 09 */
+ {3, 1300, "stateNexusCanyon", 10, 0, "NEXUS(~1.IMU"}, /* 10 */
+ {3, 1310, "stateNexusPlan", 10, 0, "NEXUS(~1.IMU"}, /* 11 */
+ {3, 1320, "stateNexusRamp", 10, 0, "NEXUS(~2.IMU"}, /* 12 */
+ {3, 1330, "stateNexusMuseum", 10, 0, "NEXUS(~3.IMU"}, /* 13 */
+ {3, 1340, "stateNexusMap", 10, 0, "NEXUS(~4.IMU"}, /* 14 */
+ {3, 1350, "stateNexusTomb", 10, 0, "NE3706~5.IMU"}, /* 15 */
+ {3, 1360, "stateNexusCath", 10, 0, "NE3305~5.IMU"}, /* 16 */
+ {3, 1370, "stateNexusAirlock", 0, 0, "NE2D3A~5.IMU"}, /* 17 */
+ {3, 1380, "stateNexusPowerOff", 0, 1, "NE8522~5.IMU"}, /* 18 */
+ {3, 1400, "stateMuseumTramNear", 0, 1, "TRAM(M~1.IMU"}, /* 19 */
+ {3, 1410, "stateMuseumTramFar", 0, 0, "TRAM(M~2.IMU"}, /* 20 */
+ {3, 1420, "stateMuseumLockup", 0, 0, "MUSEUM~1.IMU"}, /* 21 */
+ {3, 1433, "stateMuseumPool", 22, 1, "MUSEUM~2.IMU"}, /* 22 */
+ {3, 1436, "stateMuseumSpire", 22, 2, "MUSEUM~3.IMU"}, /* 23 */
+ {3, 1440, "stateMuseumMuseum", 22, 2, "MUSEUM~4.IMU"}, /* 24 */
+ {3, 1450, "stateMuseumLibrary", 0, 0, "MUB575~5.IMU"}, /* 25 */
+ {3, 1460, "stateMuseumCavern", 0, 0, "MUF9BE~5.IMU"}, /* 26 */
+ {3, 1500, "stateTombTramNear", 0, 1, "TRAM(T~1.IMU"}, /* 27 */
+ {3, 1510, "stateTombBase", 28, 2, "TOMB(A~1.IMU"}, /* 28 */
+ {3, 1520, "stateTombSpire", 28, 2, "TOMB(A~2.IMU"}, /* 29 */
+ {3, 1530, "stateTombCave", 28, 2, "TOMB(A~3.IMU"}, /* 30 */
+ {3, 1540, "stateTombCrypt", 31, 1, "TOMB(C~1.IMU"}, /* 31 */
+ {3, 1550, "stateTombGuards", 31, 1, "TOMB(C~2.IMU"}, /* 32 */
+ {3, 1560, "stateTombInner", 0, 1, "TOMB(I~1.IMU"}, /* 33 */
+ {3, 1570, "stateTombCreator1", 0, 0, "TOMB(C~3.IMU"}, /* 34 */
+ {3, 1580, "stateTombCreator2", 0, 0, "TOMB(C~4.IMU"}, /* 35 */
+ {3, 1600, "statePlanTramNear", 0, 1, "TRAM(P~1.IMU"}, /* 36 */
+ {3, 1610, "statePlanTramFar", 0, 0, "TRAM(P~2.IMU"}, /* 37 */
+ {3, 1620, "statePlanBase", 38, 2, "PLAN(A~1.IMU"}, /* 38 */
+ {3, 1630, "statePlanSpire", 38, 2, "PLAN(A~2.IMU"}, /* 39 */
+ {3, 1650, "statePlanDome", 0, 0, "PLAN(D~1.IMU"}, /* 40 */
+ {3, 1700, "stateMapTramNear", 0, 1, "TRAM(M~3.IMU"}, /* 41 */
+ {3, 1710, "stateMapTramFar", 0, 0, "TRAM(M~4.IMU"}, /* 42 */
+ {3, 1720, "stateMapCanyon", 43, 2, "MAP(AM~1.IMU"}, /* 43 */
+ {3, 1730, "stateMapExposed", 43, 2, "MAP(AM~2.IMU"}, /* 44 */
+ {3, 1750, "stateMapNestEmpty", 43, 2, "MAP(AM~4.IMU"}, /* 45 */
+ {3, 1760, "stateMapNestMonster", 0, 0, "MAP(MO~1.IMU"}, /* 46 */
+ {3, 1770, "stateMapKlein", 0, 0, "MAP(KL~1.IMU"}, /* 47 */
+ {3, 1800, "stateCathTramNear", 0, 1, "TRAM(C~1.IMU"}, /* 48 */
+ {3, 1810, "stateCathTramFar", 0, 0, "TRAM(C~2.IMU"}, /* 49 */
+ {3, 1820, "stateCathLab", 50, 1, "CATH(A~1.IMU"}, /* 50 */
+ {3, 1830, "stateCathOutside", 50, 1, "CATH(A~2.IMU"}, /* 51 */
+ {3, 1900, "stateWorldMuseum", 52, 0, "WORLD(~1.IMU"}, /* 52 */
+ {3, 1901, "stateWorldPlan", 52, 0, "WORLD(~2.IMU"}, /* 53 */
+ {3, 1902, "stateWorldTomb", 52, 0, "WORLD(~3.IMU"}, /* 54 */
+ {3, 1903, "stateWorldMap", 52, 0, "WORLD(~4.IMU"}, /* 55 */
+ {3, 1904, "stateWorldCath", 52, 0, "WO3227~5.IMU"}, /* 56 */
+ {3, 1910, "stateEye1", 0, 0, "EYE1~1.IMU"}, /* 57 */
+ {3, 1911, "stateEye2", 0, 0, "EYE2~1.IMU"}, /* 58 */
+ {3, 1912, "stateEye3", 0, 0, "EYE3~1.IMU"}, /* 59 */
+ {3, 1913, "stateEye4", 0, 0, "EYE4~1.IMU"}, /* 60 */
+ {3, 1914, "stateEye5", 0, 0, "EYE5~1.IMU"}, /* 61 */
+ {3, 1915, "stateEye6", 0, 0, "EYE6~1.IMU"}, /* 62 */
+ {3, 1916, "stateEye7", 0, 0, "EYE7~1.IMU"}, /* 63 */
+ {0, -1, "", 0, 0, ""}
+};
+
+const imuseDigTable _digSeqMusicTable[] = {
+ {0, 2000, "SEQ_NULL", 0, 0, ""},
+ {0, 2005, "seqLogo", 0, 0, ""},
+ {0, 2010, "seqIntro", 0, 0, ""},
+ {6, 2020, "seqExplosion1b", 0, 0, ""},
+ {3, 2030, "seqAstTunnel1a", 0, 0, "SEQ(AS~1.IMU"},
+ {6, 2031, "seqAstTunnel2b", 0, 0, ""},
+ {4, 2032, "seqAstTunnel3a", 0, 0, "SEQ(AS~2.IMU"},
+ {5, 2040, "seqToPlanet1b", 0, 0, ""},
+ {4, 2045, "seqArgBegin", 0, 0, "SEQ(AR~1.IMU"},
+ {4, 2046, "seqArgEnd", 0, 0, "SEQ(AR~2.IMU"},
+ {4, 2050, "seqWreckGhost", 0, 0, "SEQ(GH~1.IMU"},
+ {4, 2060, "seqCanyonGhost", 0, 0, "SEQ(GH~2.IMU"},
+ {0, 2070, "seqBrinkFall", 0, 0, ""},
+ {4, 2080, "seqPanUpCanyon", 0, 0, "SEQ(PA~1.IMU"},
+ {6, 2091, "seqAirlockTunnel1b", 0, 0, ""},
+ {6, 2100, "seqTramToMu", 0, 0, ""},
+ {6, 2101, "seqTramFromMu", 0, 0, ""},
+ {6, 2102, "seqTramToTomb", 0, 0, ""},
+ {6, 2103, "seqTramFromTomb", 0, 0, ""},
+ {6, 2104, "seqTramToPlan", 0, 0, ""},
+ {6, 2105, "seqTramFromPlan", 0, 0, ""},
+ {6, 2106, "seqTramToMap", 0, 0, ""},
+ {6, 2107, "seqTramFromMap", 0, 0, ""},
+ {6, 2108, "seqTramToCath", 0, 0, ""},
+ {6, 2109, "seqTramFromCath", 0, 0, ""},
+ {0, 2110, "seqMuseumGhost", 0, 0, ""},
+ {0, 2120, "seqSerpentAppears", 0, 0, ""},
+ {0, 2130, "seqSerpentEats", 0, 0, ""},
+ {6, 2140, "seqBrinkRes1b", 0, 0, ""},
+ {4, 2141, "seqBrinkRes2a", 0, 0, "SEQ(BR~1.IMU"},
+ {3, 2150, "seqLockupEntry", 0, 0, "SEQ(BR~1.IMU"},
+ {0, 2160, "seqSerpentExplodes", 0, 0, ""},
+ {4, 2170, "seqSwimUnderwater", 0, 0, "SEQ(DE~1.IMU"},
+ {4, 2175, "seqWavesPlunge", 0, 0, "SEQ(PL~1.IMU"},
+ {0, 2180, "seqCryptOpens", 0, 0, ""},
+ {0, 2190, "seqGuardsFight", 0, 0, ""},
+ {3, 2200, "seqCreatorRes1.1a", 0, 0, "SEQ(CR~1.IMU"},
+ {6, 2201, "seqCreatorRes1.2b", 0, 0, ""},
+ {6, 2210, "seqMaggieCapture1b", 0, 0, ""},
+ {3, 2220, "seqStealCrystals", 0, 0, "SEQ(BR~1.IMU"},
+ {0, 2230, "seqGetByMonster", 0, 0, ""},
+ {6, 2240, "seqKillMonster1b", 0, 0, ""},
+ {3, 2250, "seqCreatorRes2.1a", 0, 0, "SEQ(CR~2.IMU"},
+ {6, 2251, "seqCreatorRes2.2b", 0, 0, ""},
+ {4, 2252, "seqCreatorRes2.3a", 0, 0, "SEQ(CR~3.IMU"},
+ {0, 2260, "seqMaggieInsists", 0, 0, ""},
+ {0, 2270, "seqBrinkHelpCall", 0, 0, ""},
+ {3, 2280, "seqBrinkCrevice1a", 0, 0, "SEQ(BR~2.IMU"},
+ {3, 2281, "seqBrinkCrevice2a", 0, 0, "SEQ(BR~3.IMU"},
+ {6, 2290, "seqCathAccess1b", 0, 0, ""},
+ {4, 2291, "seqCathAccess2a", 0, 0, "SEQ(CA~1.IMU"},
+ {3, 2300, "seqBrinkAtGenerator", 0, 0, "SEQ(BR~1.IMU"},
+ {6, 2320, "seqFightBrink1b", 0, 0, ""},
+ {6, 2340, "seqMaggieDies1b", 0, 0, ""},
+ {6, 2346, "seqMaggieRes1b", 0, 0, ""},
+ {4, 2347, "seqMaggieRes2a", 0, 0, "SEQ(MA~1.IMU"},
+ {0, 2350, "seqCreatureFalls", 0, 0, ""},
+ {5, 2360, "seqFinale1b", 0, 0, ""},
+ {3, 2370, "seqFinale2a", 0, 0, "SEQ(FI~1.IMU"},
+ {6, 2380, "seqFinale3b1", 0, 0, ""},
+ {6, 2390, "seqFinale3b2", 0, 0, ""},
+ {3, 2400, "seqFinale4a", 0, 0, "SEQ(FI~2.IMU"},
+ {3, 2410, "seqFinale5a", 0, 0, "SEQ(FI~3.IMU"},
+ {3, 2420, "seqFinale6a", 0, 0, "SEQ(FI~4.IMU"},
+ {3, 2430, "seqFinale7a", 0, 0, "SE3D2B~5.IMU"},
+ {6, 2440, "seqFinale8b", 0, 0, ""},
+ {4, 2450, "seqFinale9a", 0, 0, "SE313B~5.IMU"},
+ {0, -1, "", 0, 0, ""}
+};
+
+const imuseComiTable _comiStateMusicTable[] = {
+ {0, 1000, "STATE_NULL", 0, 0, 0, ""}, /* 00 */
+ {0, 1001, "stateNoChange", 0, 0, 0, ""}, /* 01 */
+ {3, 1098, "stateCredits1", 0, 0, 60, "1098-C~1.IMX"}, /* 02 */
+ {3, 1099, "stateMenu", 0, 0, 60, "1099-M~1.IMX"}, /* 03 */
+ {3, 1100, "stateHold1", 4, 0, 60, "1100-H~1.IMX"}, /* 04 */
+ {3, 1101, "stateWaterline1", 4, 0, 60, "1101-W~1.IMX"}, /* 05 */
+ {3, 1102, "stateHold2", 6, 1, 60, "1102-H~1.IMX"}, /* 06 */
+ {3, 1103, "stateWaterline2", 6, 0, 60, "1103-W~1.IMX"}, /* 07 */
+ {3, 1104, "stateCannon", 0, 0, 60, "1104-C~1.IMX"}, /* 08 */
+ {3, 1105, "stateTreasure", 0, 0, 60, "1105-T~1.IMX"}, /* 09 */
+ {3, 1200, "stateFortBase", 10, 1, 60, "1200-F~1.IMX"}, /* 10 */
+ {3, 1201, "statePreFort", 10, 1, 60, "1201-P~1.IMX"}, /* 11 */
+ {3, 1202, "statePreVooOut", 12, 0, 60, "1202-P~1.IMX"}, /* 12 */
+ {3, 1203, "statePreVooIn", 12, 0, 60, "1203-P~1.IMX"}, /* 13 */
+ {3, 1204, "statePreVooLady", 12, 0, 60, "1204-P~1.IMX"}, /* 14 */
+ {3, 1205, "stateVoodooOut", 0, 0, 60, "1205-V~1.IMX"}, /* 15 */
+ {3, 1210, "stateVoodooIn", 0, 0, 60, "1210-V~1.IMX"}, /* 16 */
+ {12,1212, "stateVoodooInAlt", 0, 1, 42, "1210-V~1.IMX"}, /* 17 */
+ {3, 1215, "stateVoodooLady", 0, 0, 60, "1215-V~1.IMX"}, /* 18 */
+ {3, 1219, "statePrePlundermap", 0, 0, 60, "1219-P~1.IMX"}, /* 19 */
+ {3, 1220, "statePlundermap", 0, 0, 60, "1220-P~1.IMX"}, /* 20 */
+ {3, 1222, "statePreCabana", 0, 0, 60, "1222-P~1.IMX"}, /* 21 */
+ {3, 1223, "stateCabana", 0, 0, 60, "1223-C~1.IMX"}, /* 22 */
+ {3, 1224, "statePostCabana", 23, 0, 60, "1224-P~1.IMX"}, /* 23 */
+ {3, 1225, "stateBeachClub", 23, 0, 60, "1225-B~1.IMX"}, /* 24 */
+ {3, 1230, "stateCliff", 0, 0, 60, "1230-C~1.IMX"}, /* 25 */
+ {3, 1232, "stateBelly", 0, 0, 48, "1232-B~1.IMX"}, /* 26 */
+ {3, 1235, "stateQuicksand", 0, 0, 60, "1235-Q~1.IMX"}, /* 27 */
+ {3, 1240, "stateDangerBeach", 0, 0, 48, "1240-D~1.IMX"}, /* 28 */
+ {12,1241, "stateDangerBeachAlt",0, 2, 48, "1240-D~1.IMX"}, /* 29 */
+ {3, 1245, "stateRowBoat", 0, 0, 60, "1245-R~1.IMX"}, /* 30 */
+ {3, 1247, "stateAlongside", 0, 0, 48, "1247-A~1.IMX"}, /* 31 */
+ {12,1248, "stateAlongsideAlt", 0, 1, 48, "1247-A~1.IMX"}, /* 32 */
+ {3, 1250, "stateChimpBoat", 0, 0, 30, "1250-C~1.IMX"}, /* 33 */
+ {3, 1255, "stateMrFossey", 0, 0, 48, "1255-M~1.IMX"}, /* 34 */
+ {3, 1259, "statePreTown", 0, 0, 60, "1259-P~1.IMX"}, /* 35 */
+ {3, 1260, "stateTown", 0, 0, 60, "1260-T~1.IMX"}, /* 36 */
+ {3, 1264, "statePreMeadow", 0, 0, 60, "1264-P~1.IMX"}, /* 37 */
+ {3, 1265, "stateMeadow", 0, 0, 60, "1265-M~1.IMX"}, /* 38 */
+ {3, 1266, "stateMeadowAmb", 0, 0, 60, "1266-M~1.IMX"}, /* 39 */
+ {3, 1270, "stateWardrobePre", 40, 0, 60, "1270-W~1.IMX"}, /* 40 */
+ {3, 1272, "statePreShow", 40, 0, 60, "1272-P~1.IMX"}, /* 41 */
+ {3, 1274, "stateWardrobeShow", 42, 0, 60, "1274-W~1.IMX"}, /* 42 */
+ {3, 1276, "stateShow", 42, 0, 60, "1276-S~1.IMX"}, /* 43 */
+ {3, 1277, "stateWardrobeJug", 44, 0, 60, "1277-W~1.IMX"}, /* 44 */
+ {3, 1278, "stateJuggling", 44, 0, 60, "1278-J~1.IMX"}, /* 45 */
+ {3, 1279, "statePostShow", 0, 0, 60, "1279-P~1.IMX"}, /* 46 */
+ {3, 1280, "stateChickenShop", 0, 0, 60, "1280-C~1.IMX"}, /* 47 */
+ {3, 1285, "stateBarberShop", 48, 0, 60, "1285-B~1.IMX"}, /* 48 */
+ {3, 1286, "stateVanHelgen", 48, 0, 60, "1286-V~1.IMX"}, /* 49 */
+ {3, 1287, "stateBill", 48, 0, 60, "1287-B~1.IMX"}, /* 50 */
+ {3, 1288, "stateHaggis", 48, 0, 60, "1288-H~1.IMX"}, /* 51 */
+ {3, 1289, "stateRottingham", 48, 0, 60, "1289-R~1.IMX"}, /* 52 */
+ {3, 1305, "stateDeck", 0, 0, 60, "1305-D~1.IMX"}, /* 53 */
+ {3, 1310, "stateCombatMap", 0, 0, 60, "1310-C~1.IMX"}, /* 54 */
+ {3, 1320, "stateShipCombat", 0, 0, 60, "1320-S~1.IMX"}, /* 55 */
+ {3, 1325, "stateSwordfight", 0, 0, 60, "1325-S~1.IMX"}, /* 56 */
+ {3, 1327, "stateSwordRott", 0, 0, 60, "1327-S~1.IMX"}, /* 57 */
+ {3, 1330, "stateTownEdge", 0, 0, 60, "1330-T~1.IMX"}, /* 58 */
+ {3, 1335, "stateSwordLose", 0, 0, 60, "1335-S~1.IMX"}, /* 59 */
+ {3, 1340, "stateSwordWin", 0, 0, 60, "1340-S~1.IMX"}, /* 60 */
+ {3, 1345, "stateGetMap", 0, 0, 60, "1345-G~1.IMX"}, /* 61 */
+ {3, 1400, "stateWreckBeach", 0, 0, 60, "1400-W~1.IMX"}, /* 62 */
+ {3, 1405, "stateBloodMap", 63, 0, 60, "1405-B~1.IMX"}, /* 63 */
+ {3, 1410, "stateClearing", 0, 0, 60, "1410-C~1.IMX"}, /* 64 */
+ {3, 1415, "stateLighthouse", 63, 0, 60, "1415-L~1.IMX"}, /* 65 */
+ {3, 1420, "stateVillage", 66, 0, 60, "1420-V~1.IMX"}, /* 66 */
+ {3, 1423, "stateVolcano", 66, 0, 60, "1423-V~1.IMX"}, /* 67 */
+ {3, 1425, "stateAltar", 66, 0, 60, "1425-A~1.IMX"}, /* 68 */
+ {3, 1430, "stateHotelOut", 0, 0, 60, "1430-H~1.IMX"}, /* 69 */
+ {3, 1435, "stateHotelBar", 70, 0, 60, "1435-H~1.IMX"}, /* 70 */
+ {3, 1440, "stateHotelIn", 70, 0, 60, "1440-H~1.IMX"}, /* 71 */
+ {3, 1445, "stateTarotLady", 70, 0, 60, "1445-T~1.IMX"}, /* 72 */
+ {3, 1447, "stateGoodsoup", 70, 0, 60, "1447-G~1.IMX"}, /* 73 */
+ {3, 1448, "stateGuestRoom", 0, 0, 60, "1448-G~1.IMX"}, /* 74 */
+ {3, 1450, "stateWindmill", 63, 0, 60, "1450-W~1.IMX"}, /* 75 */
+ {3, 1455, "stateCemetary", 0, 0, 60, "1455-C~1.IMX"}, /* 76 */
+ {3, 1460, "stateCrypt", 77, 0, 60, "1460-C~1.IMX"}, /* 77 */
+ {3, 1463, "stateGraveDigger", 77, 0, 60, "1463-G~1.IMX"}, /* 78 */
+ {3, 1465, "stateMonkey1", 0, 0, 60, "1465-M~1.IMX"}, /* 79 */
+ {3, 1475, "stateStanDark", 0, 0, 60, "1475-S~1.IMX"}, /* 80 */
+ {3, 1477, "stateStanLight", 0, 0, 60, "1477-S~1.IMX"}, /* 81 */
+ {3, 1480, "stateEggBeach", 63, 0, 60, "1480-E~1.IMX"}, /* 82 */
+ {3, 1485, "stateSkullIsland", 0, 0, 60, "1485-S~1.IMX"}, /* 83 */
+ {3, 1490, "stateSmugglersCave", 0, 0, 60, "1490-S~1.IMX"}, /* 84 */
+ {3, 1500, "stateLeChuckTalk", 0, 0, 60, "1500-L~1.IMX"}, /* 85 */
+ {3, 1505, "stateCarnival", 0, 0, 60, "1505-C~1.IMX"}, /* 86 */
+ {3, 1511, "stateHang", 87, 0, 60, "1511-H~1.IMX"}, /* 87 */
+ {3, 1512, "stateRum", 87, 0, 60, "1512-RUM.IMX"}, /* 88 */
+ {3, 1513, "stateTorture", 87, 0, 60, "1513-T~1.IMX"}, /* 89 */
+ {3, 1514, "stateSnow", 87, 0, 60, "1514-S~1.IMX"}, /* 90 */
+ {3, 1515, "stateCredits", 0, 0, 60, "1515-C~1.IMX"}, /* 91 */
+ {3, 1520, "stateCarnAmb", 0, 0, 60, "1520-C~1.IMX"}, /* 92 */
+ {0, -1, "", 0, 0, 0, ""}
+};
+
+const imuseComiTable _comiSeqMusicTable[] = {
+ {0, 2000, "SEQ_NULL", 0, 0, 0, ""},
+ {0, 2100, "seqINTRO", 0, 0, 0, ""},
+ {3, 2105, "seqInterlude1", 0, 0, 60, "2105-I~1.IMX"},
+ {8, 2110, "seqLastBoat", 0, 1, 0, ""},
+ {0, 2115, "seqSINK_SHIP", 0, 0, 0, ""},
+ {0, 2120, "seqCURSED_RING", 0, 0, 60, ""},
+ {3, 2200, "seqInterlude2", 0, 0, 60, "2200-I~1.IMX"},
+ {3, 2210, "seqKidnapped", 0, 0, 60, "2210-K~1.IMX"},
+ {8, 2220, "seqSnakeVomits", 0, 1, 0, ""},
+ {8, 2222, "seqPopBalloon", 0, 1, 0, ""},
+ {3, 2225, "seqDropBalls", 0, 0, 60, "2225-D~1.IMX"},
+ {4, 2232, "seqArriveBarber", 0, 0, 60, "2232-A~1.IMX"},
+ {3, 2233, "seqAtonal", 0, 0, 60, "2233-A~1.IMX"},
+ {3, 2235, "seqShaveHead1", 0, 0, 60, "2235-S~1.IMX"},
+ {2, 2236, "seqShaveHead2", 0, 2, 60, "2235-S~1.IMX"},
+ {3, 2245, "seqCaberLose", 0, 0, 60, "2245-C~1.IMX"},
+ {3, 2250, "seqCaberWin", 0, 0, 60, "2250-C~1.IMX"},
+ {3, 2255, "seqDuel1", 0, 0, 60, "2255-D~1.IMX"},
+ {2, 2256, "seqDuel2", 0, 2, 60, "2255-D~1.IMX"},
+ {2, 2257, "seqDuel3", 0, 3, 60, "2255-D~1.IMX"},
+ {3, 2260, "seqBlowUpTree1", 0, 0, 60, "2260-B~1.IMX"},
+ {2, 2261, "seqBlowUpTree2", 0, 2, 60, "2260-B~1.IMX"},
+ {3, 2275, "seqMonkeys", 0, 0, 60, "2275-M~1.IMX"},
+ {9, 2277, "seqAttack", 0, 1, 0, ""},
+ {3, 2285, "seqSharks", 0, 0, 60, "2285-S~1.IMX"},
+ {3, 2287, "seqTowelWalk", 0, 0, 60, "2287-T~1.IMX"},
+ {0, 2293, "seqNICE_BOOTS", 0, 0, 0, ""},
+ {0, 2295, "seqBIG_BONED", 0, 0, 0, ""},
+ {3, 2300, "seqToBlood", 0, 0, 60, "2300-T~1.IMX"},
+ {3, 2301, "seqInterlude3", 0, 0, 60, "2301-I~1.IMX"},
+ {3, 2302, "seqRott1", 0, 0, 60, "2302-R~1.IMX"},
+ {2, 2304, "seqRott2", 0, 2, 60, "2302-R~1.IMX"},
+ {2, 2305, "seqRott2b", 0,21, 60, "2302-R~1.IMX"},
+ {2, 2306, "seqRott3", 0, 3, 60, "2302-R~1.IMX"},
+ {2, 2308, "seqRott4", 0, 4, 60, "2302-R~1.IMX"},
+ {2, 2309, "seqRott5", 0, 5, 60, "2302-R~1.IMX"},
+ {3, 2311, "seqVerse1", 0, 0, 60, "2311-S~1.IMX"},
+ {2, 2312, "seqVerse2", 0, 2, 60, "2311-S~1.IMX"},
+ {2, 2313, "seqVerse3", 0, 3, 60, "2311-S~1.IMX"},
+ {2, 2314, "seqVerse4", 0, 4, 60, "2311-S~1.IMX"},
+ {2, 2315, "seqVerse5", 0, 5, 60, "2311-S~1.IMX"},
+ {2, 2316, "seqVerse6", 0, 6, 60, "2311-S~1.IMX"},
+ {2, 2317, "seqVerse7", 0, 7, 60, "2311-S~1.IMX"},
+ {2, 2318, "seqVerse8", 0, 8, 60, "2311-S~1.IMX"},
+ {2, 2319, "seqSongEnd", 0, 9, 60, "2311-S~1.IMX"},
+ {2, 2336, "seqRiposteLose", 0, 0, 60, "2336-R~1.IMX"},
+ {2, 2337, "seqRiposteWin", 0, 0, 60, "2337-R~1.IMX"},
+ {2, 2338, "seqInsultLose", 0, 0, 60, "2338-I~1.IMX"},
+ {2, 2339, "seqInsultWin", 0, 0, 60, "2339-I~1.IMX"},
+ {3, 2340, "seqSwordLose", 0, 0, 60, "1335-S~1.IMX"},
+ {3, 2345, "seqSwordWin", 0, 0, 60, "1340-S~1.IMX"},
+ {3, 2347, "seqGetMap", 0, 0, 60, "1345-G~1.IMX"},
+ {3, 2400, "seqInterlude4", 0, 0, 60, "2400-I~1.IMX"},
+ {0, 2405, "seqSHIPWRECK", 0, 0, 0, ""},
+ {3, 2408, "seqFakeCredits", 0, 0, 60, "2408-F~1.IMX"},
+ {3, 2410, "seqPassOut", 0, 0, 60, "2410-P~1.IMX"},
+ {3, 2414, "seqGhostTalk", 0, 0, 60, "2414-G~1.IMX"},
+ {2, 2415, "seqGhostWedding", 0, 1, 60, "2414-G~1.IMX"},
+ {3, 2420, "seqEruption", 0, 0, 60, "2420-E~1.IMX"},
+ {3, 2425, "seqSacrifice", 0, 0, 60, "2425-S~1.IMX"},
+ {2, 2426, "seqSacrificeEnd", 0, 1, 60, "2425-S~1.IMX"},
+ {3, 2430, "seqScareDigger", 0, 0, 60, "2430-S~1.IMX"},
+ {3, 2445, "seqSkullArrive", 0, 0, 60, "2445-S~1.IMX"},
+ {3, 2450, "seqFloat", 0, 0, 60, "2450-C~1.IMX"},
+ {2, 2451, "seqFall", 0, 1, 60, "2450-C~1.IMX"},
+ {2, 2452, "seqUmbrella", 0, 0, 60, "2450-C~1.IMX"},
+ {3, 2460, "seqFight", 0, 0, 60, "2460-F~1.IMX"},
+ {0, 2465, "seqLAVE_RIDE", 0, 0, 0, ""},
+ {0, 2470, "seqMORE_SLAW", 0, 0, 0, ""},
+ {0, 2475, "seqLIFT_CURSE", 0, 0, 0, ""},
+ {3, 2500, "seqInterlude5", 0, 0, 60, "2500-I~1.IMX"},
+ {3, 2502, "seqExitSkycar", 0, 0, 60, "2502-E~1.IMX"},
+ {3, 2504, "seqGrow1", 0, 0, 60, "2504-G~1.IMX"},
+ {2, 2505, "seqGrow2", 0, 1, 60, "2504-G~1.IMX"},
+ {3, 2508, "seqInterlude6", 0, 0, 60, "2508-I~1.IMX"},
+ {0, 2515, "seqFINALE", 0, 0, 0, ""},
+ {3, 2520, "seqOut", 0, 0, 60, "2520-OUT.IMX"},
+ {3, 2530, "seqZap1a", 0, 0, 60, "2530-Z~1.IMX"},
+ {2, 2531, "seqZap1b", 0, 1, 60, "2530-Z~1.IMX"},
+ {2, 2532, "seqZap1c", 0, 2, 60, "2530-Z~1.IMX"},
+ {2, 2540, "seqZap2a", 0, 0, 60, "2540-Z~1.IMX"},
+ {2, 2541, "seqZap2b", 0, 1, 60, "2540-Z~1.IMX"},
+ {2, 2542, "seqZap2c", 0, 2, 60, "2540-Z~1.IMX"},
+ {3, 2550, "seqZap3a", 0, 0, 60, "2550-Z~1.IMX"},
+ {2, 2551, "seqZap3b", 0, 1, 60, "2550-Z~1.IMX"},
+ {2, 2552, "seqZap3c", 0, 2, 60, "2550-Z~1.IMX"},
+ {3, 2560, "seqZap4a", 0, 0, 60, "2560-Z~1.IMX"},
+ {2, 2561, "seqZap4b", 0, 1, 60, "2560-Z~1.IMX"},
+ {2, 2562, "seqZap4c", 0, 2, 60, "2560-Z~1.IMX"},
+ {0, -1, "", 0, 0, 0, ""}
+};
+
+const imuseFtStateTable _ftStateMusicTable[] = {
+ {"", 0, 0, "STATE_NULL" },
+ {"", 4, 127, "stateKstandOutside" },
+ {"kinside", 2, 127, "stateKstandInside" },
+ {"moshop", 3, 64, "stateMoesInside" },
+ {"melcut", 2, 127, "stateMoesOutside" },
+ {"mellover", 2, 127, "stateMellonAbove" },
+ {"radloop", 3, 28, "stateTrailerOutside" },
+ {"radloop", 3, 58, "stateTrailerInside" },
+ {"radloop", 3, 127, "stateTodShop" },
+ {"junkgate", 2, 127, "stateJunkGate" },
+ {"junkover", 3, 127, "stateJunkAbove" },
+ {"gastower", 2, 127, "stateGasTower" },
+ {"", 4, 0, "stateTowerAlarm" },
+ {"melcut", 2, 127, "stateCopsOnGround" },
+ {"melcut", 2, 127, "stateCopsAround" },
+ {"melcut", 2, 127, "stateMoesRuins" },
+ {"melcut", 2, 127, "stateKstandNight" },
+ {"trukblu2", 2, 127, "stateTruckerTalk" },
+ {"stretch", 2, 127, "stateMumblyPeg" },
+ {"kstand", 2, 100, "stateRanchOutside" },
+ {"kinside", 2, 127, "stateRanchInside" },
+ {"desert", 2, 127, "stateWreckedTruck" },
+ {"opening", 2, 100, "stateGorgeVista" },
+ {"caveopen", 2, 127, "stateCaveOpen" },
+ {"cavecut1", 2, 127, "stateCaveOuter" },
+ {"cavecut1", 1, 127, "stateCaveMiddle" },
+ {"cave", 2, 127, "stateCaveInner" },
+ {"corville", 2, 127, "stateCorvilleFront" },
+ {"mines", 2, 127, "stateMineField" },
+ {"bunyman3", 2, 127, "stateBunnyStore" },
+ {"stretch", 2, 127, "stateStretchBen" },
+ {"saveme", 2, 127, "stateBenPleas" },
+ {"", 4, 0, "stateBenConvinces" },
+ {"derby", 3, 127, "stateDemoDerby" },
+ {"fire", 3, 127, "stateLightMyFire" },
+ {"derby", 3, 127, "stateDerbyChase" },
+ {"carparts", 2, 127, "stateVultureCarParts"},
+ {"cavecut1", 2, 127, "stateVulturesInside" },
+ {"mines", 2, 127, "stateFactoryRear" },
+ {"croffice", 2, 127, "stateCorleyOffice" },
+ {"melcut", 2, 127, "stateCorleyHall" },
+ {"", 4, 0, "stateProjRoom" },
+ {"", 4, 0, "stateMMRoom" },
+ {"bumper", 2, 127, "stateBenOnBumper" },
+ {"benump", 2, 127, "stateBenOnBack" },
+ {"plane", 2, 127, "stateInCargoPlane" },
+ {"saveme", 2, 127, "statePlaneControls" },
+ {"", 4, 0, "stateCliffHanger1" },
+ {"", 4, 0, "stateCliffHanger2" },
+};
+
+const imuseFtNames _ftSeqNames[] = {
+ {"SEQ_NULL" },
+ {"seqLogo" },
+ {"seqOpenFlick" },
+ {"seqBartender" },
+ {"seqBenWakes" },
+ {"seqPhotoScram" },
+ {"seqClimbChain" },
+ {"seqDogChase" },
+ {"seqDogSquish" },
+ {"seqDogHoist" },
+ {"seqCopsArrive" },
+ {"seqCopsLand" },
+ {"seqCopsLeave" },
+ {"seqCopterFlyby" },
+ {"seqCopterCrash" },
+ {"seqMoGetsParts" },
+ {"seqMoFixesBike" },
+ {"seqFirstGoodbye" },
+ {"seqCopRoadblock" },
+ {"seqDivertCops" },
+ {"seqMurder" },
+ {"seqCorleyDies" },
+ {"seqTooLateAtMoes" },
+ {"seqPicture" },
+ {"seqNewsReel" },
+ {"seqCopsInspect" },
+ {"seqHijack" },
+ {"seqNestolusAtRanch" },
+ {"seqRipLimo" },
+ {"seqGorgeTurn" },
+ {"seqCavefishTalk" },
+ {"seqArriveCorville" },
+ {"seqSingleBunny" },
+ {"seqBunnyArmy" },
+ {"seqArriveAtMines" },
+ {"seqArriveAtVultures"},
+ {"seqMakePlan" },
+ {"seqShowPlan" },
+ {"seqDerbyStart" },
+ {"seqLightBales" },
+ {"seqNestolusBBQ" },
+ {"seqCallSecurity" },
+ {"seqFilmFail" },
+ {"seqFilmBurn" },
+ {"seqRipSpeech" },
+ {"seqExposeRip" },
+ {"seqRipEscape" },
+ {"seqRareMoment" },
+ {"seqFanBunnies" },
+ {"seqRipDead" },
+ {"seqFuneral" },
+ {"seqCredits" }
+};
+
+const imuseFtSeqTable _ftSeqMusicTable[] = {
+ {"", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"opening", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"barbeat", 2, 127},
+ {"barwarn", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0, },
+
+ {"benwakes", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"barwarn", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"swatben", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"dogattak", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 4, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 4, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"cops2", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"cops2", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"cops2", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"bunymrch", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 4, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 0, 0 },
+ {"melcut", 2, 127},
+ {"tada", 2, 127},
+ {"", 0, 0 },
+
+ {"", 4, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"trucker", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"cops2", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"barwarn", 2, 127},
+ {"murder", 2, 127},
+ {"murder2", 2, 127},
+ {"", 0, 0 },
+
+ {"corldie", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"barwarn", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"picture", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"ripintro", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"trucker", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"hosed", 2, 127},
+
+ {"ripdead", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"nesranch", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"scolding", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"desert", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"cavecut1", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"caveamb", 2, 80 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"castle", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"bunymrch", 2, 105},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"valkyrs", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"melcut", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"veltures", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"sorry", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"makeplan", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"castle", 2, 127},
+ {"derby", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"fire", 3, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"saveme", 3, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"scolding", 2, 127},
+
+ {"cops2", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"sorry", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"sorry", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"caveamb", 2, 85 },
+ {"tada", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"expose", 2, 127},
+ {"", 4, 0 },
+ {"", 0, 0 },
+ {"mocoup", 2, 127},
+
+ {"ripscram", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"valkyrs", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"ripdead", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"funeral", 2, 127},
+ {"", 2, 127},
+ {"moshop", 3, 64 },
+ {"", 0, 0 },
+
+ {"bornbad", 2, 127},
+ {"hammvox", 2, 127},
+ {"legavox", 2, 127},
+ {"chances", 2, 90 },
+};
+#endif
+
+} // End of namespace Scumm
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(DimuseTables)
+_GSETPTR(Scumm::_digStateMusicMap, GBVARS_DIGSTATEMUSICMAP_INDEX, Scumm::imuseRoomMap , GBVARS_SCUMM)
+_GSETPTR(Scumm::_digStateMusicTable, GBVARS_DIGSTATEMUSICTABLE_INDEX, Scumm::imuseDigTable , GBVARS_SCUMM)
+_GSETPTR(Scumm::_digSeqMusicTable, GBVARS_DIGSEQMUSICTABLE_INDEX, Scumm::imuseDigTable , GBVARS_SCUMM)
+_GSETPTR(Scumm::_comiStateMusicTable, GBVARS_COMISTATEMUSICTABLE_INDEX, Scumm::imuseComiTable , GBVARS_SCUMM)
+_GSETPTR(Scumm::_comiSeqMusicTable, GBVARS_COMISEQMUSICTABLE_INDEX, Scumm::imuseComiTable , GBVARS_SCUMM)
+_GSETPTR(Scumm::_ftStateMusicTable, GBVARS_FTSTATEMUSICTABLE_INDEX, Scumm::imuseFtStateTable, GBVARS_SCUMM)
+_GSETPTR(Scumm::_ftSeqMusicTable, GBVARS_FTSEQMUSICTABLE_INDEX, Scumm::imuseFtSeqTable , GBVARS_SCUMM)
+_GSETPTR(Scumm::_ftSeqNames, GBVARS_FTSEQNAMES_INDEX, Scumm::imuseFtNames , GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(DimuseTables)
+_GRELEASEPTR(GBVARS_DIGSTATEMUSICMAP_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_DIGSTATEMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_DIGSEQMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_COMISTATEMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_COMISEQMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_FTSTATEMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_FTSEQMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_FTSEQNAMES_INDEX , GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_track.cpp b/engines/scumm/imuse_digi/dimuse_track.cpp
new file mode 100644
index 0000000000..d1bd5b8923
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_track.cpp
@@ -0,0 +1,368 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/timer.h"
+
+#include "scumm/actor.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+int IMuseDigital::allocSlot(int priority) {
+ int l, lowest_priority = 127;
+ int trackId = -1;
+
+ for (l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ if (!_track[l]->used) {
+ trackId = l;
+ break;
+ }
+ }
+
+ if (trackId == -1) {
+ debug(5, "IMuseDigital::startSound(): All slots are full");
+ for (l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved &&
+ (lowest_priority > track->priority) && !track->stream2) {
+ lowest_priority = track->priority;
+ trackId = l;
+ }
+ }
+ if (lowest_priority <= priority) {
+ assert(trackId != -1);
+ _track[trackId]->toBeRemoved = true;
+ debug(5, "IMuseDigital::startSound(): Removed sound %d from track %d", _track[trackId]->soundId, trackId);
+ } else {
+ debug(5, "IMuseDigital::startSound(): Priority sound too low");
+ return -1;
+ }
+ }
+
+ return trackId;
+}
+
+void IMuseDigital::startSound(int soundId, const char *soundName, int soundType, int volGroupId, AudioStream *input, int hookId, int volume, int priority) {
+ debug(5, "IMuseDigital::startSound(%d)", soundId);
+
+ int l = allocSlot(priority);
+ if (l == -1) {
+ warning("IMuseDigital::startSound() Can't start sound - no free slots");
+ return;
+ }
+
+ Track *track = _track[l];
+ while (track->used) {
+ // The designated track is not yet available. So, we call flushTracks()
+ // to get it processed (and thus made ready for us). Since the actual
+ // processing is done by another thread, we also call parseEvents to
+ // give it some time (and to avoid busy waiting/looping).
+ flushTracks();
+#ifndef __PLAYSTATION2__
+ _vm->parseEvents();
+#endif
+ }
+
+ track->pan = 64;
+ track->vol = volume * 1000;
+ track->volFadeDest = 0;
+ track->volFadeStep = 0;
+ track->volFadeDelay = 0;
+ track->volFadeUsed = false;
+ track->soundId = soundId;
+ track->started = false;
+ track->volGroupId = volGroupId;
+ track->curHookId = hookId;
+ track->priority = priority;
+ track->curRegion = -1;
+ track->dataOffset = 0;
+ track->regionOffset = 0;
+ track->mod = 0;
+ track->mixerFlags = 0;
+ track->toBeRemoved = false;
+ track->readyToRemove = false;
+ track->soundType = soundType;
+
+ int bits = 0, freq = 0, channels = 0;
+
+ if (input) {
+ track->iteration = 0;
+ track->souStream = true;
+ track->soundName[0] = 0;
+ } else {
+ track->souStream = false;
+ strcpy(track->soundName, soundName);
+ track->soundHandle = _sound->openSound(soundId, soundName, soundType, volGroupId, -1);
+
+ if (track->soundHandle == NULL)
+ return;
+
+ track->compressed = _sound->isCompressed(track->soundHandle);
+
+ bits = _sound->getBits(track->soundHandle);
+ channels = _sound->getChannels(track->soundHandle);
+ freq = _sound->getFreq(track->soundHandle);
+
+ if ((soundId == kTalkSoundID) && (soundType == IMUSE_BUNDLE)) {
+ if (_vm->_actorToPrintStrFor != 0xFF && _vm->_actorToPrintStrFor != 0) {
+ Actor *a = _vm->derefActor(_vm->_actorToPrintStrFor, "IMuseDigital::startSound");
+ freq = (freq * a->_talkFrequency) / 256;
+ track->pan = a->_talkPan;
+ track->vol = a->_talkVolume * 1000;
+ }
+ }
+
+ assert(bits == 8 || bits == 12 || bits == 16);
+ assert(channels == 1 || channels == 2);
+ assert(0 < freq && freq <= 65535);
+
+ track->iteration = freq * channels;
+ if (channels == 2)
+ track->mixerFlags = Audio::Mixer::FLAG_STEREO | Audio::Mixer::FLAG_REVERSE_STEREO;
+
+ if ((bits == 12) || (bits == 16)) {
+ track->mixerFlags |= Audio::Mixer::FLAG_16BITS;
+ track->iteration *= 2;
+ } else if (bits == 8) {
+ track->mixerFlags |= Audio::Mixer::FLAG_UNSIGNED;
+ } else
+ error("IMuseDigital::startSound(): Can't handle %d bit samples", bits);
+
+#ifdef SCUMM_LITTLE_ENDIAN
+ if (track->compressed)
+ track->mixerFlags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+ }
+
+ if (input) {
+ track->stream2 = input;
+ track->stream = NULL;
+ track->started = false;
+ } else {
+ const int pan = (track->pan != 64) ? 2 * track->pan - 127 : 0;
+ const int vol = track->vol / 1000;
+ Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType;
+
+ if (track->volGroupId == 1)
+ type = Audio::Mixer::kSpeechSoundType;
+ if (track->volGroupId == 2)
+ type = Audio::Mixer::kSFXSoundType;
+ if (track->volGroupId == 3)
+ type = Audio::Mixer::kMusicSoundType;
+
+ // setup 1 second stream wrapped buffer
+ int32 streamBufferSize = track->iteration;
+ track->stream2 = NULL;
+ track->stream = makeAppendableAudioStream(freq, track->mixerFlags, streamBufferSize);
+ _vm->_mixer->playInputStream(type, &track->handle, track->stream, -1, vol, pan, false);
+ track->started = true;
+ }
+
+ track->used = true;
+}
+
+void IMuseDigital::setPriority(int soundId, int priority) {
+ Common::StackLock lock(_mutex, "IMuseDigital::setPriority()");
+ debug(5, "IMuseDigital::setPriority(%d, %d)", soundId, priority);
+ assert ((priority >= 0) && (priority <= 127));
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->priority = priority;
+ }
+ }
+}
+
+void IMuseDigital::setVolume(int soundId, int volume) {
+ Common::StackLock lock(_mutex, "IMuseDigital::setVolume()");
+ debug(5, "IMuseDigital::setVolume(%d, %d)", soundId, volume);
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->vol = volume * 1000;
+ }
+ }
+}
+
+void IMuseDigital::setHookId(int soundId, int hookId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::setHookId()");
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->curHookId = hookId;
+ }
+ }
+}
+
+int IMuseDigital::getCurMusicSoundId() {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicSoundId()");
+ int soundId = -1;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ soundId = track->soundId;
+ }
+ }
+
+ return soundId;
+}
+
+char *IMuseDigital::getCurMusicSoundName() {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicSoundName()");
+ char *soundName = NULL;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ soundName = track->soundName;
+ }
+ }
+
+ return soundName;
+}
+
+void IMuseDigital::setPan(int soundId, int pan) {
+ Common::StackLock lock(_mutex, "IMuseDigital::setPan()");
+ debug(5, "IMuseDigital::setPan(%d, %d)", soundId, pan);
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->pan = pan;
+ }
+ }
+}
+
+void IMuseDigital::selectVolumeGroup(int soundId, int volGroupId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::selectVolumeGroup()");
+ debug(5, "IMuseDigital::setGroupVolume(%d, %d)", soundId, volGroupId);
+ assert((volGroupId >= 1) && (volGroupId <= 4));
+
+ if (volGroupId == 4)
+ volGroupId = 3;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->volGroupId = volGroupId;
+ }
+ }
+}
+
+void IMuseDigital::setFade(int soundId, int destVolume, int delay60HzTicks) {
+ Common::StackLock lock(_mutex, "IMuseDigital::setFade()");
+ debug(5, "IMuseDigital::setFade(%d, %d, %d)", soundId, destVolume, delay60HzTicks);
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->volFadeDelay = delay60HzTicks;
+ track->volFadeDest = destVolume * 1000;
+ track->volFadeStep = (track->volFadeDest - track->vol) * 60 * (1000 / _callbackFps) / (1000 * delay60HzTicks);
+ track->volFadeUsed = true;
+ }
+ }
+}
+
+void IMuseDigital::fadeOutMusic(int fadeDelay) {
+ Common::StackLock lock(_mutex, "IMuseDigital::fadeOutMusic()");
+ debug(5, "IMuseDigital::fadeOutMusic");
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ cloneToFadeOutTrack(track, fadeDelay);
+ track->toBeRemoved = true;
+ }
+ }
+}
+
+IMuseDigital::Track *IMuseDigital::cloneToFadeOutTrack(Track *track, int fadeDelay) {
+ Common::StackLock lock(_mutex, "IMuseDigital::cloneToFadeOutTrack()");
+ assert(track);
+ Track *fadeTrack = 0;
+
+ debug(5, "IMuseDigital::cloneToFadeOutTrack(%d, %d)", track->trackId, fadeDelay);
+
+ if (_track[track->trackId + MAX_DIGITAL_TRACKS]->used) {
+ warning("IMuseDigital::cloneToFadeOutTrack: Not free fade track");
+ return NULL;
+ }
+
+ fadeTrack = _track[track->trackId + MAX_DIGITAL_TRACKS];
+ fadeTrack->pan = track->pan;
+ fadeTrack->vol = track->vol;
+ fadeTrack->volGroupId = track->volGroupId;
+ fadeTrack->priority = track->priority;
+ fadeTrack->soundId = track->soundId;
+ fadeTrack->dataOffset = track->dataOffset;
+ fadeTrack->regionOffset = track->regionOffset;
+ fadeTrack->curRegion = track->curRegion;
+ fadeTrack->curHookId = track->curHookId;
+ fadeTrack->iteration = track->iteration;
+ fadeTrack->mixerFlags = track->mixerFlags;
+ fadeTrack->mod = track->mod;
+ fadeTrack->toBeRemoved = track->toBeRemoved;
+ fadeTrack->readyToRemove = track->readyToRemove;
+ fadeTrack->souStream = track->souStream;
+ fadeTrack->started = track->started;
+ fadeTrack->stream2 = track->stream2;
+ strcpy(fadeTrack->soundName, track->soundName);
+ fadeTrack->soundType = track->soundType;
+ fadeTrack->soundHandle = _sound->cloneSound(track->soundHandle);
+ assert(fadeTrack->soundHandle);
+
+ fadeTrack->volFadeDelay = fadeDelay;
+ fadeTrack->volFadeDest = 0;
+ fadeTrack->volFadeStep = (fadeTrack->volFadeDest - fadeTrack->vol) * 60 * (1000 / _callbackFps) / (1000 * fadeDelay);
+ fadeTrack->volFadeUsed = true;
+
+ Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType;
+
+ if (fadeTrack->volGroupId == 1)
+ type = Audio::Mixer::kSpeechSoundType;
+ if (fadeTrack->volGroupId == 2)
+ type = Audio::Mixer::kSFXSoundType;
+ if (fadeTrack->volGroupId == 3)
+ type = Audio::Mixer::kMusicSoundType;
+
+ // setup 1 second stream wrapped buffer
+ int32 streamBufferSize = fadeTrack->iteration;
+ fadeTrack->stream = makeAppendableAudioStream(_sound->getFreq(fadeTrack->soundHandle), fadeTrack->mixerFlags, streamBufferSize);
+ _vm->_mixer->playInputStream(type, &fadeTrack->handle, fadeTrack->stream, -1, fadeTrack->vol / 1000, fadeTrack->pan, false);
+ fadeTrack->started = true;
+ fadeTrack->used = true;
+
+ return fadeTrack;
+}
+
+} // End of namespace Scumm