aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grechnikov2018-10-20 16:35:23 +0300
committerEvgeny Grechnikov2018-10-20 16:35:23 +0300
commitaf580eaa853b2434c0d237cff49f7f273444a06d (patch)
tree295dd4dfb9fadb348bd46bdc8512e16b19555c58
parent9f7ae73a7b7318311e5cc52d36e451cacfba8a02 (diff)
downloadscummvm-rg350-af580eaa853b2434c0d237cff49f7f273444a06d.tar.gz
scummvm-rg350-af580eaa853b2434c0d237cff49f7f273444a06d.tar.bz2
scummvm-rg350-af580eaa853b2434c0d237cff49f7f273444a06d.zip
LASTEXPRESS: save/load sound state
Warning: breaks compatibility with previous savefiles. They were mostly broken anyway, locking any NPC who waited for kActionEndSound when savefile was created.
-rw-r--r--engines/lastexpress/data/snd.cpp52
-rw-r--r--engines/lastexpress/data/snd.h2
-rw-r--r--engines/lastexpress/game/savegame.cpp4
-rw-r--r--engines/lastexpress/game/savegame.h5
-rw-r--r--engines/lastexpress/sound/entry.cpp224
-rw-r--r--engines/lastexpress/sound/entry.h21
-rw-r--r--engines/lastexpress/sound/queue.cpp65
-rw-r--r--engines/lastexpress/sound/queue.h7
-rw-r--r--engines/lastexpress/sound/sound.cpp10
9 files changed, 216 insertions, 174 deletions
diff --git a/engines/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp
index 835a1ab8fb..d9d193ecf3 100644
--- a/engines/lastexpress/data/snd.cpp
+++ b/engines/lastexpress/data/snd.cpp
@@ -363,6 +363,11 @@ public:
return !_running || (!_looped && Audio::ADPCMStream::endOfData());
}
+ void seekToBlock(uint32 block) {
+ reset();
+ _stream->seek(_startpos + _blockAlign * block);
+ }
+
int readBuffer(int16 *buffer, const int numSamples) {
int samples = 0;
// Temporary data
@@ -384,22 +389,15 @@ public:
_status.ima_ch[0].stepIndex = _stream->readSint16LE() << 6;
_blockPos[0] = 4;
- // Smooth transition, if requested
- // the original game clears kSoundFlagVolumeChanging here if _nextVolume == _smoothChangeTarget
- if (_nextVolume != _smoothChangeTarget) {
- if (_volumeHoldBlocks > 3) {
- if (_nextVolume < _smoothChangeTarget)
- ++_nextVolume;
- else
- --_nextVolume;
- _volumeHoldBlocks = 0;
- if (_nextVolume == 0) {
- _running = false;
- break;
- }
- } else {
- _volumeHoldBlocks++;
- }
+ // sanity check against broken stream
+ if ((unsigned)_status.ima_ch[0].stepIndex >= ARRAYSIZE(stepTable) * 4) {
+ // the original game sets kSoundFlagDecodeError here and stops playing
+ _status.ima_ch[0].stepIndex = 0;
+ }
+
+ if (!smoothVolumeChangeStep()) {
+ _running = false;
+ break;
}
// Get current volume
@@ -436,6 +434,21 @@ private:
uint32 _smoothChangeTarget;
uint32 _volumeHoldBlocks; // smooth change of volume keeps volume on hold for 4 blocks = 133ms for every value; this is the counter
bool _running, _looped;
+
+ bool smoothVolumeChangeStep() {
+ if (_nextVolume == _smoothChangeTarget)
+ return true; // the original game clears kSoundFlagVolumeChanging here
+ if (_volumeHoldBlocks <= 3) {
+ _volumeHoldBlocks++;
+ return true;
+ }
+ if (_nextVolume < _smoothChangeTarget)
+ ++_nextVolume;
+ else
+ --_nextVolume;
+ _volumeHoldBlocks = 0;
+ return (_nextVolume != 0);
+ }
};
//////////////////////////////////////////////////////////////////////////
@@ -444,7 +457,6 @@ private:
SimpleSound::SimpleSound() : _size(0), _blocks(0), _blockSize(0) {}
SimpleSound::~SimpleSound() {
- stop();
}
// Stop the sound
@@ -480,11 +492,12 @@ uint32 SimpleSound::getTimeMS() {
StreamedSound::StreamedSound() : _as(NULL), _loaded(false) {}
StreamedSound::~StreamedSound() {
+ stop(); // should execute before disposal of _as, so don't move in ~SimpleSound
delete _as;
_as = NULL;
}
-bool StreamedSound::load(Common::SeekableReadStream *stream, uint32 volume, bool looped) {
+bool StreamedSound::load(Common::SeekableReadStream *stream, uint32 volume, bool looped, uint32 startBlock) {
if (!stream)
return false;
@@ -498,6 +511,8 @@ bool StreamedSound::load(Common::SeekableReadStream *stream, uint32 volume, bool
}
// Start decoding the input stream
_as = makeDecoder(stream, _size, volume, looped);
+ if (startBlock)
+ _as->seekToBlock(startBlock);
// Start playing the decoded audio stream
play(_as, DisposeAfterUse::NO);
@@ -546,6 +561,7 @@ AppendableSound::AppendableSound() : SimpleSound() {
AppendableSound::~AppendableSound() {
finish();
+ stop();
_as = NULL;
}
diff --git a/engines/lastexpress/data/snd.h b/engines/lastexpress/data/snd.h
index 9672167a97..8358370581 100644
--- a/engines/lastexpress/data/snd.h
+++ b/engines/lastexpress/data/snd.h
@@ -80,7 +80,7 @@ public:
StreamedSound();
~StreamedSound();
- bool load(Common::SeekableReadStream *stream, uint32 volume, bool looped);
+ bool load(Common::SeekableReadStream *stream, uint32 volume, bool looped, uint32 startBlock = 0);
virtual bool isFinished();
void setVolume(uint32 newVolume);
diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp
index 8b45835fc7..97fd67e533 100644
--- a/engines/lastexpress/game/savegame.cpp
+++ b/engines/lastexpress/game/savegame.cpp
@@ -40,8 +40,6 @@
namespace LastExpress {
-#define DISABLE_COMPRESSION 1
-
// Names of savegames
static const struct {
const char *saveFile;
@@ -730,7 +728,7 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) {
writeValue(ser, "inventory", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveLoadWithSerializer), 7 * 32);
writeValue(ser, "objects", WRAP_SYNC_FUNCTION(getObjects(), Objects, saveLoadWithSerializer), 5 * 128);
writeValue(ser, "entities", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveLoadWithSerializer), 1262 * 40);
- writeValue(ser, "sound", WRAP_SYNC_FUNCTION(getSoundQueue(), SoundQueue, saveLoadWithSerializer), 3 * 4 + getSoundQueue()->count() * 64);
+ writeValue(ser, "sound", WRAP_SYNC_FUNCTION(getSoundQueue(), SoundQueue, saveLoadWithSerializer), 3 * 4 + getSoundQueue()->count() * 68);
writeValue(ser, "savepoints", WRAP_SYNC_FUNCTION(getSavePoints(), SavePoints, saveLoadWithSerializer), 128 * 16 + 4 + getSavePoints()->count() * 16);
_savegame->process();
diff --git a/engines/lastexpress/game/savegame.h b/engines/lastexpress/game/savegame.h
index f374e18a44..6c3105b8c0 100644
--- a/engines/lastexpress/game/savegame.h
+++ b/engines/lastexpress/game/savegame.h
@@ -79,8 +79,11 @@
namespace LastExpress {
+// our own hack until compression code will be confirmed stable
+#define DISABLE_COMPRESSION 1
+
// Savegame signatures
-#define SAVEGAME_SIGNATURE 0x12001200 // 301994496
+#define SAVEGAME_SIGNATURE (0x12001200 ^ DISABLE_COMPRESSION) // 301994496
#define SAVEGAME_ENTRY_SIGNATURE 0xE660E660 // 3865110112
#define WRAP_SYNC_FUNCTION(instance, className, method) \
diff --git a/engines/lastexpress/sound/entry.cpp b/engines/lastexpress/sound/entry.cpp
index 8d9a496fe1..d73642d92a 100644
--- a/engines/lastexpress/sound/entry.cpp
+++ b/engines/lastexpress/sound/entry.cpp
@@ -46,6 +46,7 @@ SoundEntry::SoundEntry(LastExpressEngine *engine) : _engine(engine) {
_tag = kSoundTagNone;
_blockCount = 0;
+ _startTime = 0;
_stream = NULL;
@@ -82,11 +83,12 @@ void SoundEntry::open(Common::String name, SoundFlag flag, int priority) {
}
void SoundEntry::close() {
+ if (_soundStream) {
+ delete _soundStream; // stops the sound in destructor
+ _soundStream = NULL;
+ _stream = NULL; // disposed by _soundStream
+ }
_status |= kSoundFlagClosed;
- // Loop until ready
- // _status |= kSoundFlagCloseRequested;
- //while (!(_status & kSoundFlagClosed) && !(getSoundQueue()->getFlag() & 8) && (getSoundQueue()->getFlag() & 1))
- // ; // empty loop body
// The original game remove the entry from the cache here,
// but since we are called from within an iterator loop
@@ -106,34 +108,41 @@ void SoundEntry::close() {
}
}
-void SoundEntry::play() {
+void SoundEntry::play(uint32 startTime) {
+ if (_status & kSoundFlagClosed)
+ return; // failed to load sound file
+
if (!_stream)
error("[SoundEntry::play] stream has been disposed");
// Prepare sound stream
- if (!_soundStream)
- _soundStream = new StreamedSound();
+ if (_soundStream)
+ error("[SoundEntry::play] already playing");
+
+ // BUG: the original game never checks for sound type when loading subtitles.
+ // NIS files and LNK files have the same base name,
+ // so without extra caution NIS subtitles would be loaded for LNK sounds as well.
+ // The original game instead separates calls to play() and setSubtitles()
+ // and does not call setSubtitles() for linked-after sounds.
+ // Unfortunately, that does not work well with save/load.
+ if ((_status & kSoundTypeMask) != kSoundTypeLink && (_status & kSoundTypeMask) != kSoundTypeConcert) {
+ // Get subtitles name
+ uint32 size = (_name.size() > 4 ? _name.size() - 4 : _name.size());
+ setSubtitles(Common::String(_name.c_str(), size));
+ }
+
+ _soundStream = new StreamedSound();
_stream->seek(0);
// Load the stream and start playing
- _soundStream->load(_stream, _status & kSoundVolumeMask, (_status & kSoundFlagLooped) != 0);
-}
-
-bool SoundEntry::isFinished() {
- if (!_stream)
- return true;
-
- if (!_soundStream)
- return false;
+ _soundStream->load(_stream, _status & kSoundVolumeMask, (_status & kSoundFlagLooped) != 0, startTime);
- // TODO check that all data has been queued
- return _soundStream->isFinished();
+ _status |= kSoundFlagPlaying;
}
void SoundEntry::setupTag(SoundFlag flag) {
switch (flag & kSoundTypeMask) {
- default:
case kSoundTypeNormal:
_tag = getSoundQueue()->generateNextTag();
break;
@@ -160,7 +169,7 @@ void SoundEntry::setupTag(SoundFlag flag) {
previous->fade();
}
- _tag = kSoundTagIntro;
+ _tag = kSoundTagWalla;
}
break;
@@ -199,14 +208,18 @@ void SoundEntry::setupTag(SoundFlag flag) {
_tag = kSoundTagMenu;
}
break;
+
+ default:
+ assert(false);
+ break;
}
}
void SoundEntry::setupStatus(SoundFlag flag) {
_status = flag;
- if ((_status & kSoundVolumeMask) == kVolumeNone)
- _status |= kSoundFlagMuteRequested;
+ // set the flag for the case that our savefiles
+ // will be ever loaded by the original game
if (!(_status & kSoundFlagLooped))
_status |= kSoundFlagCloseOnDataEnd;
}
@@ -222,6 +235,14 @@ void SoundEntry::loadStream(Common::String name) {
if (!_stream)
_status = kSoundFlagClosed;
+
+ // read total count of sound blocks for the case that our savefiles
+ // will be ever loaded by the original game
+ if (_stream) {
+ _stream->readUint32LE();
+ _blockCount = _stream->readUint16LE();
+ _status |= kSoundFlagHeaderProcessed;
+ }
}
void SoundEntry::setVolumeSmoothly(SoundFlag newVolume) {
@@ -246,59 +267,46 @@ void SoundEntry::setVolumeSmoothly(SoundFlag newVolume) {
}
bool SoundEntry::update() {
- assert(_name.size() < 16);
+ if (_soundStream && _soundStream->isFinished())
+ _status |= kSoundFlagClosed;
- bool result;
- char sub[16];
+ if (_status & kSoundFlagClosed)
+ return false;
- if (_status & kSoundFlagClosed) {
- result = false;
+ if (_status & kSoundFlagDelayedActivate) {
+ // counter overflow is processed correctly
+ if (_engine->_system->getMillis() - _initTimeMS >= _activateDelayMS) {
+ _status &= ~kSoundFlagDelayedActivate;
+ play();
+ }
} else {
- if (_status & kSoundFlagDelayedActivate) {
- // counter overflow is processed correctly
- if (_engine->_system->getMillis() - _initTimeMS >= _activateDelayMS) {
- _status &= ~kSoundFlagDelayedActivate;
- play();
-
- // drop .SND extension
- strcpy(sub, _name.c_str());
- int l = _name.size();
- if (l > 4)
- sub[l - 4] = 0;
- showSubtitle(sub);
- }
- } else {
- if (!(getSoundQueue()->getFlag() & 0x20)) {
- if (!(_status & kSoundFlagFixedVolume)) {
- if (_entity) {
- if (_entity < 0x80) {
- setVolume(getSound()->getSoundFlag(_entity));
- }
+ if (!(getSoundQueue()->getFlag() & 0x20)) {
+ if (!(_status & kSoundFlagFixedVolume)) {
+ if (_entity) {
+ if (_entity < 0x80) {
+ setVolume(getSound()->getSoundFlag(_entity));
}
}
}
- //if (_status & kSoundFlagHasUnreadData && !(_status & kSoundFlagMute) && v1->soundBuffer)
- // Sound_FillSoundBuffer(v1);
}
- result = true;
+ //if (_status & kSoundFlagHasUnreadData && !(_status & kSoundFlagMute) && v1->soundBuffer)
+ // Sound_FillSoundBuffer(v1);
}
- return result;
+ return true;
}
void SoundEntry::setVolume(SoundFlag newVolume) {
assert((newVolume & kSoundVolumeMask) == newVolume);
- if (newVolume) {
- if (getSoundQueue()->getFlag() & 0x20 && _tag != kSoundTagNIS && _tag != kSoundTagLink)
- setVolumeSmoothly(newVolume);
- else
- _status = newVolume + (_status & ~kSoundVolumeMask);
- } else {
+ if (newVolume == kVolumeNone) {
_volumeWithoutNIS = 0;
- _status |= kSoundFlagMuteRequested;
- _status &= ~(kSoundFlagVolumeChanging | kSoundVolumeMask);
+ } else if (getSoundQueue()->getFlag() & 0x20 && _tag != kSoundTagNIS && _tag != kSoundTagLink) {
+ setVolumeSmoothly(newVolume);
+ return;
}
+
+ _status = (_status & ~kSoundVolumeMask) | newVolume;
if (_soundStream)
_soundStream->setVolume(_status & kSoundVolumeMask);
}
@@ -324,24 +332,7 @@ void SoundEntry::initDelayedActivate(unsigned activateDelay) {
_status |= kSoundFlagDelayedActivate;
}
-void SoundEntry::kill() {
- _status |= kSoundFlagClosed;
- _entity = kEntityPlayer;
-
- if (_stream) {
- if (!_soundStream) {
- SAFE_DELETE(_stream);
- } else {
- // the original stream will be disposed
- _soundStream->stop();
- SAFE_DELETE(_soundStream);
- }
-
- _stream = NULL;
- }
-}
-
-void SoundEntry::showSubtitle(Common::String filename) {
+void SoundEntry::setSubtitles(Common::String filename) {
_subtitle = new SubtitleEntry(_engine);
_subtitle->load(filename, this);
@@ -354,32 +345,79 @@ void SoundEntry::showSubtitle(Common::String filename) {
}
void SoundEntry::saveLoadWithSerializer(Common::Serializer &s) {
- assert(_name.size() <= 16);
+ if (s.isLoading()) {
+ // load the fields
+ uint32 blocksLeft;
- if (_name.matchString("NISSND?") && ((_status & kSoundTypeMask) != kSoundTypeMenu)) {
s.syncAsUint32LE(_status);
s.syncAsUint32LE(_tag);
- s.syncAsUint32LE(_blockCount); // field_8;
+ s.syncAsUint32LE(blocksLeft);
+ s.syncAsUint32LE(_startTime);
+ uint32 unused;
+ s.syncAsUint32LE(unused);
+ s.syncAsUint32LE(unused);
+ s.syncAsUint32LE(_entity);
+
+ uint32 activateDelay;
+ s.syncAsUint32LE(activateDelay);
+ s.syncAsUint32LE(_priority);
+
+ char name[16];
+ s.syncBytes((byte *)name, 16); // _linkAfter name, should be always empty for entries in savefile
+ s.syncBytes((byte *)name, 16); // real name
+ name[15] = 0;
+
+ // load the sound
+ _blockCount = blocksLeft + _startTime;
+
+ // if we are loading a savefile from the original game
+ // and this savefile has been saved at a bizarre moment,
+ // we can see transient flags here.
+ // Let's pretend that the IRQ handler has run once.
+ if (_status & kSoundFlagPlayRequested)
+ _status |= kSoundFlagPlaying;
+ if (_status & (kSoundFlagCloseRequested | kSoundFlagFading))
+ _status |= kSoundFlagClosed;
+ _status &= kSoundVolumeMask
+ | kSoundFlagPlaying
+ | kSoundFlagClosed
+ | kSoundFlagCloseOnDataEnd
+ | kSoundFlagLooped
+ | kSoundFlagDelayedActivate
+ | kSoundFlagHasSubtitles
+ | kSoundFlagFixedVolume
+ | kSoundFlagHeaderProcessed
+ | kSoundTypeMask;
+
+ loadStream(name); // also sets _name
+ if (_status & kSoundFlagPlaying)
+ play((_status & kSoundFlagLooped) ? 0 : _startTime); // also loads subtitles
+
+ _initTimeMS = _engine->_system->getMillis();
+ _activateDelayMS = activateDelay * 1000 / 30;
+
+ } else {
+ assert(_name.size() < 16);
+ assert(needSaving());
+ // we can save our flags as is
+ // the original game can reconstruct kSoundFlagMute, kSoundFlagCyclicBuffer, kSoundFlagHasUnreadData,
+ // and we set other important flags correctly
+ s.syncAsUint32LE(_status);
+ s.syncAsUint32LE(_tag);
uint32 time = getTime();
+ uint32 blocksLeft = _blockCount - time;
+ s.syncAsUint32LE(blocksLeft);
s.syncAsUint32LE(time);
uint32 unused = 0;
s.syncAsUint32LE(unused);
s.syncAsUint32LE(unused);
s.syncAsUint32LE(_entity);
- if (s.isLoading()) {
- uint32 delta;
- s.syncAsUint32LE(delta);
- _initTimeMS = _engine->_system->getMillis();
- _activateDelayMS = delta * 1000 / 15;
- } else {
- uint32 deltaMS = _initTimeMS + _activateDelayMS - _engine->_system->getMillis();
- if (deltaMS > 0x8000000u) // sanity check against overflow
- deltaMS = 0;
- uint32 delta = deltaMS * 15 / 1000;
- s.syncAsUint32LE(delta);
- }
-
+ uint32 deltaMS = _initTimeMS + _activateDelayMS - _engine->_system->getMillis();
+ if (deltaMS > 0x8000000u) // sanity check against overflow
+ deltaMS = 0;
+ uint32 delta = deltaMS * 30 / 1000;
+ s.syncAsUint32LE(delta);
s.syncAsUint32LE(_priority);
char name[16] = {0};
diff --git a/engines/lastexpress/sound/entry.h b/engines/lastexpress/sound/entry.h
index cb12214d4a..998d1e53e8 100644
--- a/engines/lastexpress/sound/entry.h
+++ b/engines/lastexpress/sound/entry.h
@@ -96,9 +96,13 @@ public:
void open(Common::String name, SoundFlag flag, int priority);
void close();
- void play();
- void kill();
- bool isFinished();
+ // startTime is measured in sound ticks, 30Hz timer
+ // [used for restoring the entry from savefile]
+ void play(uint32 startTime = 0);
+ void kill() {
+ _entity = kEntityPlayer; // no kActionEndSound notifications
+ close();
+ }
void setVolumeSmoothly(SoundFlag newVolume);
// setVolumeSmoothly() treats kVolumeNone in a special way;
// fade() terminates the stream after the transition
@@ -110,18 +114,20 @@ public:
void initDelayedActivate(unsigned activateDelay);
// Subtitles
- void showSubtitle(Common::String filename);
+ void setSubtitles(Common::String filename);
// Serializable
void saveLoadWithSerializer(Common::Serializer &ser);
// Accessors
- void addStatusFlag(SoundFlag flag) { _status |= flag; }
void setEntity(EntityIndex entity) { _entity = entity; }
+ bool needSaving() const {
+ return (_name != "NISSND?" && (_status & kSoundTypeMask) != kSoundTypeMenu);
+ }
uint32 getStatus() { return _status; }
int32 getTag() { return _tag; }
- uint32 getTime() { return _soundStream ? (_soundStream->getTimeMS() * 30 / 1000) : 0; }
+ uint32 getTime() { return _soundStream ? (_soundStream->getTimeMS() * 30 / 1000) + _startTime : 0; }
EntityIndex getEntity() { return _entity; }
uint32 getPriority() { return _priority; }
const Common::String& getName(){ return _name; }
@@ -138,9 +144,10 @@ private:
uint32 _status;
int32 _tag; // member of SoundTag for special sounds, unique value for normal sounds
//byte *_bufferStart, *_bufferEnd, *_decodePointer, *_buffer, *_readPointer;
- // the original game uses uint32 _blocksLeft, _time instead of _totalBlocks
+ // the original game uses uint32 _blocksLeft, _time instead of _blockCount
// we ask the backend for sound time
uint32 _blockCount;
+ uint32 _startTime;
//uint32 _bufferSize;
//union { uint32 _streamPos; enum StreamCloseReason _streamCloseReason; };
Common::SeekableReadStream *_stream; // The file stream
diff --git a/engines/lastexpress/sound/queue.cpp b/engines/lastexpress/sound/queue.cpp
index 3a10a7a89b..58b11f2402 100644
--- a/engines/lastexpress/sound/queue.cpp
+++ b/engines/lastexpress/sound/queue.cpp
@@ -79,8 +79,6 @@ void SoundQueue::stop(Common::String filename) {
}
void SoundQueue::updateQueue() {
- ++_flag;
-
if (getAmbientState() & kAmbientSoundEnabled) {
SoundEntry *entry = getEntry(kSoundTagAmbient);
if (!entry || getFlags()->flag_3 || (entry && entry->getTime() > getSound()->getAmbientSoundDuration())) {
@@ -100,32 +98,23 @@ void SoundQueue::updateQueue() {
// Original removes the entry data from the cache and sets the archive as not loaded
// and if the sound data buffer is not full, loads a new entry to be played based on
- // its priority and filter id
+ // its priority and volume
if (!entry->update() && !(entry->getStatus() & kSoundFlagKeepAfterFinish)) {
entry->close();
SAFE_DELETE(entry);
it = _soundList.reverse_erase(it);
- continue;
- }
-
- // When the entry has stopped playing, we remove his buffer
- if (entry->isFinished()) {
- entry->close();
- SAFE_DELETE(entry);
- it = _soundList.reverse_erase(it);
- continue;
}
}
// Original update the current entry, loading another set of samples to be decoded
getFlags()->flag_3 = false;
-
- --_flag;
}
void SoundQueue::stopAmbient() {
+ _ambientState = 0;
+
for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) {
if ((*i)->getTag() == kSoundTagAmbient) {
(*i)->kill();
@@ -141,12 +130,12 @@ void SoundQueue::stopAmbient() {
}
}
-void SoundQueue::stopAllExcept(SoundTag type1, SoundTag type2) {
- if (!type2)
- type2 = type1;
+void SoundQueue::stopAllExcept(SoundTag tag1, SoundTag tag2) {
+ if (!tag2)
+ tag2 = tag1;
for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) {
- if ((*i)->getTag() != type1 && (*i)->getTag() != type2)
+ if ((*i)->getTag() != tag1 && (*i)->getTag() != tag2)
(*i)->kill();
}
}
@@ -160,7 +149,7 @@ void SoundQueue::destroyAllSound() {
error("[SoundQueue::destroyAllSound] Invalid entry found in sound queue");
// Delete entry
- entry->close();
+ entry->kill();
SAFE_DELETE(entry);
i = _soundList.reverse_erase(i);
@@ -174,7 +163,7 @@ void SoundQueue::destroyAllSound() {
//////////////////////////////////////////////////////////////////////////
void SoundQueue::stopAll() {
for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i)
- (*i)->addStatusFlag(kSoundFlagClosed);
+ (*i)->close();
}
//////////////////////////////////////////////////////////////////////////
@@ -194,8 +183,8 @@ void SoundQueue::fade(EntityIndex entity) {
}
}
-void SoundQueue::fade(SoundTag type) {
- SoundEntry *entry = getEntry(type);
+void SoundQueue::fade(SoundTag tag) {
+ SoundEntry *entry = getEntry(tag);
if (entry)
entry->fade();
}
@@ -236,9 +225,9 @@ SoundEntry *SoundQueue::getEntry(Common::String name) {
return NULL;
}
-SoundEntry *SoundQueue::getEntry(SoundTag type) {
+SoundEntry *SoundQueue::getEntry(SoundTag tag) {
for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) {
- if ((*i)->getTag() == type)
+ if ((*i)->getTag() == tag)
return *i;
}
@@ -321,24 +310,22 @@ void SoundQueue::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsUint32LE(_ambientState);
s.syncAsUint32LE(_currentTag);
- // Compute the number of entries to save
- uint32 numEntries = count();
- s.syncAsUint32LE(numEntries);
-
// Save or load each entry data
if (s.isSaving()) {
+ // Compute the number of entries to save
+ uint32 numEntries = count();
+ s.syncAsUint32LE(numEntries);
+
for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i)
- (*i)->saveLoadWithSerializer(s);
+ if ((*i)->needSaving())
+ (*i)->saveLoadWithSerializer(s);
} else {
- warning("[Sound::saveLoadWithSerializer] Loading not implemented");
-
- uint32 unusedDataSize = numEntries * 64;
- if (s.isLoading()) {
- byte *empty = (byte *)malloc(unusedDataSize);
- s.syncBytes(empty, unusedDataSize);
- free(empty);
- } else {
- s.skip(unusedDataSize);
+ uint32 numEntries;
+ s.syncAsUint32LE(numEntries);
+ for (uint32 i = 0; i < numEntries; i++) {
+ SoundEntry* entry = new SoundEntry(_engine);
+ entry->saveLoadWithSerializer(s);
+ addToQueue(entry);
}
}
}
@@ -347,7 +334,7 @@ void SoundQueue::saveLoadWithSerializer(Common::Serializer &s) {
uint32 SoundQueue::count() {
uint32 numEntries = 0;
for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i)
- if ((*i)->getName().matchString("NISSND?"))
+ if ((*i)->needSaving())
++numEntries;
return numEntries;
diff --git a/engines/lastexpress/sound/queue.h b/engines/lastexpress/sound/queue.h
index df8c945ee9..63ff091d80 100644
--- a/engines/lastexpress/sound/queue.h
+++ b/engines/lastexpress/sound/queue.h
@@ -46,7 +46,7 @@ public:
void stop(EntityIndex entity);
void updateQueue();
void stopAmbient();
- void stopAllExcept(SoundTag type1, SoundTag type2 = kSoundTagNone);
+ void stopAllExcept(SoundTag tag1, SoundTag tag2 = kSoundTagNone);
void destroyAllSound();
// State
@@ -58,10 +58,10 @@ public:
// Entries
void assignNISLink(EntityIndex index);
void fade(EntityIndex entity);
- void fade(SoundTag type);
+ void fade(SoundTag tag);
void fade(Common::String filename);
void endAmbient();
- SoundEntry *getEntry(SoundTag type);
+ SoundEntry *getEntry(SoundTag tag);
SoundEntry *getEntry(EntityIndex index);
SoundEntry *getEntry(Common::String name);
uint32 getEntryTime(EntityIndex index);
@@ -96,7 +96,6 @@ private:
// State & shared data
int _ambientState;
int32 _currentTag;
- // TODO: this seems to be a synchronization flag for the sound timer
uint32 _flag;
// Entries
diff --git a/engines/lastexpress/sound/sound.cpp b/engines/lastexpress/sound/sound.cpp
index 597edd3b11..013a166ea8 100644
--- a/engines/lastexpress/sound/sound.cpp
+++ b/engines/lastexpress/sound/sound.cpp
@@ -137,7 +137,7 @@ void SoundManager::playSound(EntityIndex entity, Common::String filename, SoundF
if (_queue->isBuffered(entity) && entity && entity < kEntityTrain)
_queue->stop(entity);
- SoundFlag currentFlag = (flag == kSoundVolumeEntityDefault) ? getSoundFlag(entity) : (SoundFlag)(flag | 0x80000);
+ SoundFlag currentFlag = (flag == kSoundVolumeEntityDefault) ? getSoundFlag(entity) : (SoundFlag)(flag | kSoundFlagFixedVolume);
// Add .SND at the end of the filename if needed
if (!filename.contains('.'))
@@ -164,12 +164,6 @@ bool SoundManager::playSoundWithSubtitles(Common::String filename, uint32 flag,
if (activateDelay) {
entry->initDelayedActivate(activateDelay);
} else {
- // Get subtitles name
- uint32 size = filename.size();
- while (filename.size() > size - 4)
- filename.deleteLastChar();
-
- entry->showSubtitle(filename);
entry->play();
}
@@ -323,7 +317,7 @@ void SoundManager::playSteam(CityIndex index) {
// Get the new sound entry and show subtitles
SoundEntry *entry = _queue->getEntry(kSoundTagAmbient);
if (entry)
- entry->showSubtitle(cities[index]);
+ entry->setSubtitles(cities[index]);
}
void SoundManager::playFightSound(byte action, byte a4) {