From 15a16e556ca03b51b1fc03f135834bf57f592b52 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Tue, 28 Jun 2011 22:55:14 +0300 Subject: SWORD25: Implement persistence functions for soundengine Now sound is properly saved/restored. Implemented savegame versioning. Compatibility with old saves pertained. --- engines/sword25/gfx/image/renderedimage.cpp | 5 +- engines/sword25/kernel/inputpersistenceblock.cpp | 5 +- engines/sword25/kernel/inputpersistenceblock.h | 6 +- engines/sword25/kernel/persistenceservice.cpp | 30 ++++++++-- engines/sword25/kernel/persistenceservice.h | 1 + engines/sword25/sfx/soundengine.cpp | 75 +++++++++++++++++++++--- engines/sword25/sfx/soundengine.h | 11 +++- 7 files changed, 115 insertions(+), 18 deletions(-) diff --git a/engines/sword25/gfx/image/renderedimage.cpp b/engines/sword25/gfx/image/renderedimage.cpp index f5f33d8e02..3b29b0333f 100644 --- a/engines/sword25/gfx/image/renderedimage.cpp +++ b/engines/sword25/gfx/image/renderedimage.cpp @@ -72,7 +72,10 @@ static byte *readSavegameThumbnail(const Common::String &filename, uint &fileSiz // Seek to the actual PNG image loadString(*file); // Marker (BS25SAVEGAME) - loadString(*file); // Version + Common::String storedVersionID = loadString(*file); // Version + if (storedVersionID != "SCUMMVM1") + loadString(*file); + loadString(*file); // Description uint32 compressedGamedataSize = atoi(loadString(*file).c_str()); loadString(*file); // Uncompressed game data size diff --git a/engines/sword25/kernel/inputpersistenceblock.cpp b/engines/sword25/kernel/inputpersistenceblock.cpp index c1cd771e39..2d45dfb640 100644 --- a/engines/sword25/kernel/inputpersistenceblock.cpp +++ b/engines/sword25/kernel/inputpersistenceblock.cpp @@ -35,9 +35,10 @@ namespace Sword25 { -InputPersistenceBlock::InputPersistenceBlock(const void *data, uint dataLength) : +InputPersistenceBlock::InputPersistenceBlock(const void *data, uint dataLength, int version) : _data(static_cast(data), dataLength), - _errorState(NONE) { + _errorState(NONE), + _version(version) { _iter = _data.begin(); } diff --git a/engines/sword25/kernel/inputpersistenceblock.h b/engines/sword25/kernel/inputpersistenceblock.h index f643b06bc1..7e68137246 100644 --- a/engines/sword25/kernel/inputpersistenceblock.h +++ b/engines/sword25/kernel/inputpersistenceblock.h @@ -46,7 +46,7 @@ public: OUT_OF_SYNC }; - InputPersistenceBlock(const void *data, uint dataLength); + InputPersistenceBlock(const void *data, uint dataLength, int version); virtual ~InputPersistenceBlock(); void read(int16 &value); @@ -64,6 +64,8 @@ public: return _errorState; } + int getVersion() const { return _version; } + private: bool checkMarker(byte marker); bool checkBlockSize(int size); @@ -72,6 +74,8 @@ private: Common::Array _data; Common::Array::const_iterator _iter; ErrorState _errorState; + + int _version; }; } // End of namespace Sword25 diff --git a/engines/sword25/kernel/persistenceservice.cpp b/engines/sword25/kernel/persistenceservice.cpp index c88360e031..27d669caa1 100644 --- a/engines/sword25/kernel/persistenceservice.cpp +++ b/engines/sword25/kernel/persistenceservice.cpp @@ -50,7 +50,9 @@ static const char *SAVEGAME_DIRECTORY = "saves"; static const char *FILE_MARKER = "BS25SAVEGAME"; static const uint SLOT_COUNT = 18; static const uint FILE_COPY_BUFFER_SIZE = 1024 * 10; -static const char *VERSIONID = "SCUMMVM1"; +static const char *VERSIONIDOLD = "SCUMMVM1"; +static const char *VERSIONID = "SCUMMVM2"; +static const int VERSIONNUM = 2; #define MAX_SAVEGAME_SIZE 100 @@ -99,6 +101,7 @@ struct SavegameInformation { bool isOccupied; bool isCompatible; Common::String description; + int version; uint gamedataLength; uint gamedataOffset; uint gamedataUncompressedLength; @@ -147,9 +150,15 @@ struct PersistenceService::Impl { // Read in the header Common::String storedMarker = loadString(file); Common::String storedVersionID = loadString(file); + if (storedVersionID == VERSIONIDOLD) { + curSavegameInfo.version = 1; + } else { + Common::String versionNum = loadString(file); + curSavegameInfo.version = atoi(versionNum.c_str()); + } Common::String gameDescription = loadString(file); - Common::String gameDataLength = loadString(file); - curSavegameInfo.gamedataLength = atoi(gameDataLength.c_str()); + Common::String gamedataLength = loadString(file); + curSavegameInfo.gamedataLength = atoi(gamedataLength.c_str()); Common::String gamedataUncompressedLength = loadString(file); curSavegameInfo.gamedataUncompressedLength = atoi(gamedataUncompressedLength.c_str()); @@ -158,7 +167,7 @@ struct PersistenceService::Impl { // The slot is marked as occupied. curSavegameInfo.isOccupied = true; // Check if the saved game is compatible with the current engine version. - curSavegameInfo.isCompatible = (storedVersionID == Common::String(VERSIONID)); + curSavegameInfo.isCompatible = (curSavegameInfo.version <= VERSIONNUM); // Load the save game description. curSavegameInfo.description = gameDescription; // The offset to the stored save game data within the file. @@ -242,6 +251,12 @@ Common::String &PersistenceService::getSavegameFilename(uint slotID) { return result; } +int PersistenceService::getSavegameVersion(uint slotID) { + if (!checkslotID(slotID)) + return -1; + return _impl->_savegameInformations[slotID].version; +} + bool PersistenceService::saveGame(uint slotID, const Common::String &screenshotFilename) { // FIXME: This code is a hack which bypasses the savefile API, // and should eventually be removed. @@ -264,6 +279,11 @@ bool PersistenceService::saveGame(uint slotID, const Common::String &screenshotF file->writeString(VERSIONID); file->writeByte(0); + char buf[20]; + snprintf(buf, 20, "%d", VERSIONNUM); + file->writeString(buf); + file->writeByte(0); + TimeDate dt; g_system->getTimeAndDate(dt); file->writeString(formatTimestamp(dt)); @@ -385,7 +405,7 @@ bool PersistenceService::loadGame(uint slotID) { memcpy(uncompressedDataBuffer, compressedDataBuffer, uncompressedBufferSize); } - InputPersistenceBlock reader(&uncompressedDataBuffer[0], curSavegameInfo.gamedataUncompressedLength); + InputPersistenceBlock reader(&uncompressedDataBuffer[0], curSavegameInfo.gamedataUncompressedLength, curSavegameInfo.version); // Einzelne Engine-Module depersistieren. bool success = true; diff --git a/engines/sword25/kernel/persistenceservice.h b/engines/sword25/kernel/persistenceservice.h index f73962892c..59e0a3661d 100644 --- a/engines/sword25/kernel/persistenceservice.h +++ b/engines/sword25/kernel/persistenceservice.h @@ -57,6 +57,7 @@ public: void reloadSlots(); bool isSlotOccupied(uint slotID); bool isSavegameCompatible(uint slotID); + int getSavegameVersion(uint slotID); Common::String &getSavegameDescription(uint slotID); Common::String &getSavegameFilename(uint slotID); diff --git a/engines/sword25/sfx/soundengine.cpp b/engines/sword25/sfx/soundengine.cpp index 7c8a6593aa..1b424dac65 100644 --- a/engines/sword25/sfx/soundengine.cpp +++ b/engines/sword25/sfx/soundengine.cpp @@ -33,6 +33,8 @@ #include "sword25/sfx/soundengine.h" #include "sword25/package/packagemanager.h" #include "sword25/kernel/resource.h" +#include "sword25/kernel/inputpersistenceblock.h" +#include "sword25/kernel/outputpersistenceblock.h" #include "audio/decoders/vorbis.h" @@ -202,17 +204,29 @@ bool SoundEngine::playSound(const Common::String &fileName, SOUND_TYPES type, fl return true; } -uint SoundEngine::playSoundEx(const Common::String &fileName, SOUND_TYPES type, float volume, float pan, bool loop, int loopStart, int loopEnd, uint layer) { +uint SoundEngine::playSoundEx(const Common::String &fileName, SOUND_TYPES type, float volume, float pan, bool loop, int loopStart, int loopEnd, uint layer, uint handleId) { Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(fileName); #ifdef USE_VORBIS Audio::SeekableAudioStream *stream = Audio::makeVorbisStream(in, DisposeAfterUse::YES); #endif uint id; - SndHandle *handle = getHandle(&id); + SndHandle *handle; - debugC(1, kDebugSound, "SoundEngine::playSoundEx(%s, %d, %f, %f, %d, %d, %d, %d)", fileName.c_str(), type, volume, pan, loop, loopStart, loopEnd, layer); + if (handleId == 0x1337) + handle = getHandle(&id); + else + handle = &_handles[handleId]; - handle->type = kAllocatedHandle; + handle->fileName = fileName; + handle->sndType = type; + handle->volume = volume; + handle->pan = pan; + handle->loop = loop; + handle->loopStart = loopStart; + handle->loopEnd = loopEnd; + handle->layer = layer; + + debugC(1, kDebugSound, "SoundEngine::playSoundEx(%s, %d, %f, %f, %d, %d, %d, %d)", fileName.c_str(), type, volume, pan, loop, loopStart, loopEnd, layer); #ifdef USE_VORBIS _mixer->playStream(getType(type), &(handle->handle), stream, -1, (byte)(volume * 255), (int8)(pan * 127)); @@ -311,16 +325,61 @@ bool SoundEngine::canLoadResource(const Common::String &fileName) { } -bool SoundEngine::persist(OutputPersistenceBlock &writer) { - warning("STUB: SoundEngine::persist()"); + bool SoundEngine::persist(OutputPersistenceBlock &writer) { + writer.write(_maxHandleId); + + for (uint i = 0; i < SOUND_HANDLES; i++) { + writer.write(_handles[i].id); + + writer.writeString(_handles[i].fileName); + writer.write((int)_handles[i].sndType); + writer.write(_handles[i].volume); + writer.write(_handles[i].pan); + writer.write(_handles[i].loop); + writer.write(_handles[i].loopStart); + writer.write(_handles[i].loopEnd); + writer.write(_handles[i].layer); + } return true; } bool SoundEngine::unpersist(InputPersistenceBlock &reader) { - warning("STUB: SoundEngine::unpersist()"); + _mixer->stopAll(); - return true; + if (reader.getVersion() < 2) + return true; + + reader.read(_maxHandleId); + + for (uint i = 0; i < SOUND_HANDLES; i++) { + reader.read(_handles[i].id); + + Common::String fileName; + int sndType; + float volume; + float pan; + bool loop; + int loopStart; + int loopEnd; + uint layer; + + reader.readString(fileName); + reader.read(sndType); + reader.read(volume); + reader.read(pan); + reader.read(loop); + reader.read(loopStart); + reader.read(loopEnd); + reader.read(layer); + + if (reader.isGood()) { + playSoundEx(fileName, (SOUND_TYPES)sndType, volume, pan, loop, loopStart, loopEnd, layer, i); + } else + return false; + } + + return reader.isGood(); } diff --git a/engines/sword25/sfx/soundengine.h b/engines/sword25/sfx/soundengine.h index 71f1602484..8132ec556e 100644 --- a/engines/sword25/sfx/soundengine.h +++ b/engines/sword25/sfx/soundengine.h @@ -65,6 +65,15 @@ struct SndHandle { Audio::SoundHandle handle; sndHandleType type; uint32 id; + + Common::String fileName; + int sndType; + float volume; + float pan; + bool loop; + int loopStart; + int loopEnd; + uint layer; }; @@ -176,7 +185,7 @@ public: * @remark If more control is needed over the playing, eg. changing the sound parameters * for Volume and Panning, then PlaySoundEx should be used. */ - uint playSoundEx(const Common::String &fileName, SOUND_TYPES type, float volume = 1.0f, float pan = 0.0f, bool loop = false, int loopStart = -1, int loopEnd = -1, uint layer = 0); + uint playSoundEx(const Common::String &fileName, SOUND_TYPES type, float volume = 1.0f, float pan = 0.0f, bool loop = false, int loopStart = -1, int loopEnd = -1, uint layer = 0, uint handleId = 0x1337); /** * Sets the volume of a playing sound -- cgit v1.2.3