[Scummvm-git-logs] scummvm master -> af580eaa853b2434c0d237cff49f7f273444a06d

grechnik diamondaz at yandex.ru
Sat Oct 20 15:35:53 CEST 2018


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
af580eaa85 LASTEXPRESS: save/load sound state


Commit: af580eaa853b2434c0d237cff49f7f273444a06d
    https://github.com/scummvm/scummvm/commit/af580eaa853b2434c0d237cff49f7f273444a06d
Author: Evgeny Grechnikov (diamondaz at yandex.ru)
Date: 2018-10-20T16:35:23+03:00

Commit Message:
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.

Changed paths:
    engines/lastexpress/data/snd.cpp
    engines/lastexpress/data/snd.h
    engines/lastexpress/game/savegame.cpp
    engines/lastexpress/game/savegame.h
    engines/lastexpress/sound/entry.cpp
    engines/lastexpress/sound/entry.h
    engines/lastexpress/sound/queue.cpp
    engines/lastexpress/sound/queue.h
    engines/lastexpress/sound/sound.cpp


diff --git a/engines/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp
index 835a1ab..d9d193e 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 9672167..8358370 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 8b45835..97fd67e 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 f374e18..6c3105b 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 8d9a496..d73642d 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 cb12214..998d1e5 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 3a10a7a..58b11f2 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 df8c945..63ff091 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 597edd3..013a166 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) {





More information about the Scummvm-git-logs mailing list