[Scummvm-git-logs] scummvm master -> 3bb0094d7d2269152c031f8ea462fc262bab5ffb
elasota
noreply at scummvm.org
Sat Apr 1 16:02:30 UTC 2023
This automated email contains information about 5 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
b188d295c3 VCRUISE: Refactor a bunch of things to allow for sound cycles
445de7972c VCRUISE: Refactor sound cache so sounds can be unloaded
c3154d5299 VCRUISE: Keep track of loop state, evict ended sounds.
7f549ce5c5 VCRUISE: Hook up volume controls.
3bb0094d7d VCRUISE: Save format rework
Commit: b188d295c3abd2ebd8940b5261fb4827db1ac7e1
https://github.com/scummvm/scummvm/commit/b188d295c3abd2ebd8940b5261fb4827db1ac7e1
Author: elasota (ejlasota at gmail.com)
Date: 2023-04-01T12:01:48-04:00
Commit Message:
VCRUISE: Refactor a bunch of things to allow for sound cycles
Changed paths:
engines/vcruise/runtime.cpp
engines/vcruise/runtime.h
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 80f7681b956..6049434a0ab 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -87,6 +87,70 @@ void Runtime::RenderSection::init(const Common::Rect ¶mRect, const Graphics:
Runtime::OSEvent::OSEvent() : type(kOSEventTypeInvalid), keyCode(static_cast<Common::KeyCode>(0)) {
}
+Runtime::StackValue::ValueUnion::ValueUnion() {
+}
+
+Runtime::StackValue::ValueUnion::ValueUnion(StackInt_t iVal) : i(iVal) {
+}
+
+Runtime::StackValue::ValueUnion::ValueUnion(const Common::String &strVal) : s(strVal) {
+}
+
+Runtime::StackValue::ValueUnion::ValueUnion(Common::String &&strVal) : s(Common::move(strVal)) {
+}
+
+Runtime::StackValue::ValueUnion::~ValueUnion() {
+}
+
+Runtime::StackValue::StackValue() : type(kNumber), value(0) {
+ new (&value) ValueUnion(0);
+}
+
+Runtime::StackValue::StackValue(const StackValue &other) : type(kNumber), value(0) {
+ (*this) = other;
+}
+
+Runtime::StackValue::StackValue(StackValue &&other) : type(kNumber), value(0) {
+ (*this) = Common::move(other);
+}
+
+Runtime::StackValue::StackValue(StackInt_t i) : type(kNumber), value(i) {
+}
+
+Runtime::StackValue::StackValue(const Common::String &str) : type(kString), value(str) {
+}
+
+Runtime::StackValue::StackValue(Common::String &&str) : type(kString), value(Common::move(str)) {
+}
+
+Runtime::StackValue::~StackValue() {
+ value.~ValueUnion();
+}
+
+Runtime::StackValue &Runtime::StackValue::operator=(const StackValue &other) {
+ value.~ValueUnion();
+
+ if (other.type == StackValue::kNumber)
+ new (&value) ValueUnion(other.value.i);
+
+ if (other.type == StackValue::kString)
+ new (&value) ValueUnion(other.value.s);
+
+ return *this;
+}
+
+Runtime::StackValue &Runtime::StackValue::operator=(StackValue &&other) {
+ value.~ValueUnion();
+
+ if (other.type == StackValue::kNumber)
+ new (&value) ValueUnion(other.value.i);
+
+ if (other.type == StackValue::kString)
+ new (&value) ValueUnion(Common::move(other.value.s));
+
+ return *this;
+}
+
Runtime::Gyro::Gyro() {
reset();
}
@@ -304,7 +368,7 @@ void SfxData::load(Common::SeekableReadStream &stream, Audio::Mixer *mixer) {
}
CachedSound::CachedSound()
- : rampStartVolume(0), rampEndVolume(0), rampRatePerMSec(0), rampStartTime(0), rampTerminateOnCompletion(false),
+ : id(0), rampStartVolume(0), rampEndVolume(0), rampRatePerMSec(0), rampStartTime(0), rampTerminateOnCompletion(false),
volume(0), balance(0), effectiveBalance(0), effectiveVolume(0), is3D(false), x(0), y(0), z(0) {
}
@@ -1373,24 +1437,90 @@ void Runtime::findWaves() {
}
}
-void Runtime::loadWave(uint soundID, const Common::String &soundName, const Common::ArchiveMemberPtr &archiveMemberPtr) {
+Common::SharedPtr<CachedSound> Runtime::loadWave(const Common::String &soundName, uint soundID, const Common::ArchiveMemberPtr &archiveMemberPtr) {
+ for (const Common::SharedPtr<CachedSound> &activeSound : _activeSounds) {
+ if (activeSound->name == soundName)
+ return activeSound;
+ }
+
Common::SeekableReadStream *stream = archiveMemberPtr->createReadStream();
if (!stream) {
warning("Couldn't open read stream for sound '%s'", soundName.c_str());
- return;
+ return nullptr;
}
Audio::SeekableAudioStream *audioStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
if (!audioStream) {
warning("Couldn't open audio stream for sound '%s'", soundName.c_str());
- return;
+ return nullptr;
}
Common::SharedPtr<CachedSound> cachedSound(new CachedSound());
- _cachedSounds[soundID] = cachedSound;
cachedSound->stream.reset(audioStream);
cachedSound->name = soundName;
+ cachedSound->id = soundID;
+
+ bool foundExisting = false;
+ for (Common::SharedPtr<CachedSound> &existingSound : _activeSounds) {
+ if (existingSound->id == soundID) {
+ existingSound = cachedSound;
+ foundExisting = true;
+ break;
+ }
+ }
+
+ if (!foundExisting)
+ _activeSounds.push_back(cachedSound);
+
+ return cachedSound;
+}
+
+void Runtime::resolveSoundByName(const Common::String &soundName, StackInt_t &outSoundID, CachedSound *&outWave) {
+ Common::String sndName = soundName;
+
+ uint soundID = 0;
+ for (uint i = 0; i < 4; i++)
+ soundID = soundID * 10u + (sndName[i] - '0');
+
+ sndName.toLowercase();
+
+ outSoundID = soundID;
+ outWave = nullptr;
+
+ for (const Common::SharedPtr<CachedSound> &snd : _activeSounds) {
+ if (snd->name == sndName) {
+ outWave = snd.get();
+ return;
+ }
+ }
+
+ Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator waveIt = _waves.find(sndName);
+
+ if (waveIt != _waves.end()) {
+ Common::SharedPtr<CachedSound> snd = loadWave(sndName, soundID, waveIt->_value);
+ outWave = snd.get();
+ }
+}
+
+void Runtime::resolveSoundByNameOrID(const StackValue &stackValue, StackInt_t &outSoundID, CachedSound *&outWave) {
+ outSoundID = 0;
+ outWave = nullptr;
+
+ if (stackValue.type == StackValue::kNumber) {
+ outSoundID = stackValue.value.i;
+
+ for (const Common::SharedPtr<CachedSound> &snd : _activeSounds) {
+ if (snd->id == static_cast<uint>(stackValue.value.i)) {
+ outWave = snd.get();
+ break;
+ }
+ }
+ return;
+ }
+
+ if (stackValue.type == StackValue::kString)
+ resolveSoundByName(stackValue.value.s, outSoundID, outWave);
}
void Runtime::changeToScreen(uint roomNumber, uint screenNumber) {
@@ -1853,28 +1983,13 @@ void Runtime::changeAnimation(const AnimationDef &animDef, uint initialFrame, bo
debug(1, "Animation last frame set to %u", animDef.lastFrame);
}
-void Runtime::setSound3DParameters(uint soundID, int32 x, int32 y, const SoundParams3D &soundParams3D) {
- Common::HashMap<uint, Common::SharedPtr<CachedSound> >::iterator it = _cachedSounds.find(soundID);
- if (it == _cachedSounds.end()) {
- warning("Couldn't set sound parameters for sound ID %u, the sound wasn't loaded", soundID);
- return;
- }
-
- CachedSound &snd = *it->_value;
+void Runtime::setSound3DParameters(CachedSound &snd, int32 x, int32 y, const SoundParams3D &soundParams3D) {
snd.x = x;
snd.y = y;
snd.params3D = soundParams3D;
}
-void Runtime::triggerSound(bool looping, uint soundID, uint volume, int32 balance, bool is3D) {
- Common::HashMap<uint, Common::SharedPtr<CachedSound> >::iterator it = _cachedSounds.find(soundID);
- if (it == _cachedSounds.end()) {
- warning("Couldn't trigger sound ID %u, the sound wasn't loaded", soundID);
- return;
- }
-
- CachedSound &snd = *it->_value;
-
+void Runtime::triggerSound(bool looping, CachedSound &snd, uint volume, int32 balance, bool is3D) {
snd.volume = volume;
snd.balance = balance;
snd.is3D = is3D;
@@ -1911,13 +2026,7 @@ void Runtime::triggerSound(bool looping, uint soundID, uint volume, int32 balanc
}
}
-void Runtime::triggerSoundRamp(uint soundID, uint durationMSec, uint newVolume, bool terminateOnCompletion) {
- Common::HashMap<uint, Common::SharedPtr<CachedSound> >::const_iterator it = _cachedSounds.find(soundID);
- if (it == _cachedSounds.end())
- return;
-
- CachedSound &snd = *it->_value;
-
+void Runtime::triggerSoundRamp(CachedSound &snd, uint durationMSec, uint newVolume, bool terminateOnCompletion) {
snd.rampStartVolume = snd.volume;
snd.rampEndVolume = newVolume;
snd.rampTerminateOnCompletion = terminateOnCompletion;
@@ -1931,8 +2040,8 @@ void Runtime::triggerSoundRamp(uint soundID, uint durationMSec, uint newVolume,
void Runtime::updateSounds(uint32 timestamp) {
Common::Array<uint> condemnedSounds;
- for (const Common::HashMap<uint, Common::SharedPtr<CachedSound> >::Node &node : _cachedSounds) {
- CachedSound &snd = *node._value;
+ for (uint sndIndex = 0; sndIndex < _activeSounds.size(); sndIndex++) {
+ CachedSound &snd = *_activeSounds[sndIndex];
if (snd.rampRatePerMSec) {
uint ramp = snd.rampRatePerMSec * (timestamp - snd.rampStartTime);
@@ -1941,7 +2050,7 @@ void Runtime::updateSounds(uint32 timestamp) {
snd.rampRatePerMSec = 0;
newVolume = snd.rampEndVolume;
if (snd.rampTerminateOnCompletion)
- condemnedSounds.push_back(node._key);
+ condemnedSounds.push_back(sndIndex);
} else {
uint rampedVolume = (snd.rampStartVolume * (65536u - ramp)) + (snd.rampEndVolume * ramp);
newVolume = rampedVolume >> 16;
@@ -1958,13 +2067,19 @@ void Runtime::updateSounds(uint32 timestamp) {
}
}
- for (uint id : condemnedSounds)
- _cachedSounds.erase(id);
+ // condemnedSounds must be sorted
+ if (condemnedSounds.size()) {
+ uint numDeleted = 0;
+ for (uint index : condemnedSounds) {
+ _activeSounds.remove_at(index - numDeleted);
+ numDeleted++;
+ }
+ }
}
void Runtime::update3DSounds() {
- for (const Common::HashMap<uint, Common::SharedPtr<CachedSound> >::Node &node : _cachedSounds) {
- CachedSound &snd = *node._value;
+ for (const Common::SharedPtr<CachedSound> &sndPtr : _activeSounds) {
+ CachedSound &snd = *sndPtr;
if (!snd.is3D)
continue;
@@ -2044,7 +2159,7 @@ bool Runtime::computeEffectiveVolumeAndBalance(CachedSound &snd) {
return changed;
}
-AnimationDef Runtime::stackArgsToAnimDef(const StackValue_t *args) const {
+AnimationDef Runtime::stackArgsToAnimDef(const StackInt_t *args) const {
AnimationDef def;
def.animNum = args[0];
def.firstFrame = args[1];
@@ -2061,14 +2176,14 @@ AnimationDef Runtime::stackArgsToAnimDef(const StackValue_t *args) const {
}
void Runtime::pushAnimDef(const AnimationDef &animDef) {
- _scriptStack.push_back(animDef.animNum);
- _scriptStack.push_back(animDef.firstFrame);
- _scriptStack.push_back(animDef.lastFrame);
+ _scriptStack.push_back(StackValue(animDef.animNum));
+ _scriptStack.push_back(StackValue(animDef.firstFrame));
+ _scriptStack.push_back(StackValue(animDef.lastFrame));
- _scriptStack.push_back(animDef.constraintRect.left);
- _scriptStack.push_back(animDef.constraintRect.top);
- _scriptStack.push_back(animDef.constraintRect.right);
- _scriptStack.push_back(animDef.constraintRect.bottom);
+ _scriptStack.push_back(StackValue(animDef.constraintRect.left));
+ _scriptStack.push_back(StackValue(animDef.constraintRect.top));
+ _scriptStack.push_back(StackValue(animDef.constraintRect.right));
+ _scriptStack.push_back(StackValue(animDef.constraintRect.bottom));
uint animNameIndex = 0;
Common::HashMap<Common::String, uint>::const_iterator nameIt = _animDefNameToIndex.find(animDef.animName);
@@ -2079,7 +2194,7 @@ void Runtime::pushAnimDef(const AnimationDef &animDef) {
} else
animNameIndex = nameIt->_value;
- _scriptStack.push_back(animNameIndex);
+ _scriptStack.push_back(StackValue(animNameIndex));
}
void Runtime::activateScript(const Common::SharedPtr<Script> &script, const ScriptEnvironmentVars &envVars) {
@@ -2610,38 +2725,78 @@ Runtime::LoadGameOutcome Runtime::loadGame(Common::ReadStream *stream) {
#endif
#define PEEK_STACK(n) \
- if (this->_scriptStack.size() < (n)) { \
+ if (this->_scriptStack.size() < (n)) { \
error("Script stack underflow"); \
return; \
} \
- const ScriptArg_t *stackArgs = &this->_scriptStack[this->_scriptStack.size() - (n)]
+ const StackValue *stackArgs = &this->_scriptStack[this->_scriptStack.size() - (n)]
-#define TAKE_STACK(n) \
- StackValue_t stackArgs[n]; \
+#define TAKE_STACK_INT_NAMED(n, arrayName) \
+ StackInt_t arrayName[n]; \
do { \
const uint stackSize = _scriptStack.size(); \
if (stackSize < (n)) { \
error("Script stack underflow"); \
return; \
} \
- const StackValue_t *stackArgsPtr = &this->_scriptStack[stackSize - (n)]; \
- for (uint i = 0; i < (n); i++) \
- stackArgs[i] = stackArgsPtr[i]; \
+ const StackValue *stackArgsPtr = &this->_scriptStack[stackSize - (n)]; \
+ for (uint i = 0; i < (n); i++) { \
+ if (stackArgsPtr[i].type != StackValue::kNumber) \
+ error("Expected op argument %u to be a number"); \
+ arrayName[i] = stackArgsPtr[i].value.i; \
+ } \
this->_scriptStack.resize(stackSize - (n)); \
} while (false)
+#define TAKE_STACK_INT(n) TAKE_STACK_INT_NAMED(n, stackArgs)
+
+#define TAKE_STACK_STR_NAMED(n, arrayName) \
+ Common::String arrayName[n]; \
+ do { \
+ const uint stackSize = _scriptStack.size(); \
+ if (stackSize < (n)) { \
+ error("Script stack underflow"); \
+ return; \
+ } \
+ const StackValue *stackArgsPtr = &this->_scriptStack[stackSize - (n)]; \
+ for (uint i = 0; i < (n); i++) { \
+ if (stackArgsPtr[i].type != StackValue::kNumber) \
+ error("Expected op argument %u to be a string"); \
+ arrayName[i] = Common::move(stackArgsPtr[i].value.s); \
+ } \
+ this->_scriptStack.resize(stackSize - (n)); \
+ } while (false)
+
+#define TAKE_STACK_STR(n) TAKE_STACK_STR_NAMED(n, stackArgs)
+
+#define TAKE_STACK_VAR_NAMED(n, arrayName) \
+ StackValue arrayName[n]; \
+ do { \
+ const uint stackSize = _scriptStack.size(); \
+ if (stackSize < (n)) { \
+ error("Script stack underflow"); \
+ return; \
+ } \
+ const StackValue *stackArgsPtr = &this->_scriptStack[stackSize - (n)]; \
+ for (uint i = 0; i < (n); i++) \
+ arrayName[i] = Common::move(stackArgsPtr[i]); \
+ this->_scriptStack.resize(stackSize - (n)); \
+ } while (false)
+
+#define TAKE_STACK_VAR(n) TAKE_STACK_VAR_NAMED(n, stackArgs)
+
#define OPCODE_STUB(op) \
void Runtime::scriptOp##op(ScriptArg_t arg) { \
error("Unimplemented opcode '" #op "'"); \
}
void Runtime::scriptOpNumber(ScriptArg_t arg) {
- _scriptStack.push_back(arg);
+ _scriptStack.push_back(StackValue(arg));
}
void Runtime::scriptOpRotate(ScriptArg_t arg) {
- TAKE_STACK(kAnimDefStackArgs + kAnimDefStackArgs);
+ TAKE_STACK_INT(kAnimDefStackArgs + kAnimDefStackArgs);
_panLeftAnimationDef = stackArgsToAnimDef(stackArgs + 0);
_panRightAnimationDef = stackArgsToAnimDef(stackArgs + kAnimDefStackArgs);
@@ -2649,28 +2804,28 @@ void Runtime::scriptOpRotate(ScriptArg_t arg) {
}
void Runtime::scriptOpAngle(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
- _scriptStack.push_back((stackArgs[0] == static_cast<StackValue_t>(_direction)) ? 1 : 0);
+ _scriptStack.push_back(StackValue((stackArgs[0] == static_cast<StackInt_t>(_direction)) ? 1 : 0));
}
void Runtime::scriptOpAngleGGet(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
- if (stackArgs[0] < 0 || stackArgs[0] >= static_cast<StackValue_t>(GyroState::kNumGyros))
+ if (stackArgs[0] < 0 || stackArgs[0] >= static_cast<StackInt_t>(GyroState::kNumGyros))
error("Invalid gyro index in angleGGet op");
- _scriptStack.push_back(_gyros.gyros[stackArgs[0]].currentState);
+ _scriptStack.push_back(StackValue(_gyros.gyros[stackArgs[0]].currentState));
}
void Runtime::scriptOpSpeed(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
_scriptEnv.fpsOverride = stackArgs[0];
}
void Runtime::scriptOpSAnimL(ScriptArg_t arg) {
- TAKE_STACK(kAnimDefStackArgs + 2);
+ TAKE_STACK_INT(kAnimDefStackArgs + 2);
if (stackArgs[kAnimDefStackArgs] != 0)
warning("sanimL second operand wasn't zero (what does that do?)");
@@ -2691,7 +2846,7 @@ void Runtime::scriptOpSAnimL(ScriptArg_t arg) {
}
void Runtime::scriptOpChangeL(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
// ChangeL changes the screen number, but it also forces screen entry scripts to replay, which is
// needed for things like the fountain.
@@ -2752,7 +2907,7 @@ void Runtime::scriptOpAnimR(ScriptArg_t arg) {
}
void Runtime::scriptOpAnimF(ScriptArg_t arg) {
- TAKE_STACK(kAnimDefStackArgs + 3);
+ TAKE_STACK_INT(kAnimDefStackArgs + 3);
AnimationDef animDef = stackArgsToAnimDef(stackArgs + 0);
@@ -2782,7 +2937,7 @@ void Runtime::scriptOpAnimF(ScriptArg_t arg) {
}
void Runtime::scriptOpAnimN(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
const AnimationDef *faceDirectionAnimDef = nullptr;
uint initialFrame = 0;
@@ -2800,7 +2955,7 @@ void Runtime::scriptOpAnimN(ScriptArg_t arg) {
}
void Runtime::scriptOpAnimG(ScriptArg_t arg) {
- TAKE_STACK(kAnimDefStackArgs * 2 + 1);
+ TAKE_STACK_INT(kAnimDefStackArgs * 2 + 1);
_gyros.posAnim = stackArgsToAnimDef(stackArgs + 0);
_gyros.negAnim = stackArgsToAnimDef(stackArgs + kAnimDefStackArgs);
@@ -2819,7 +2974,7 @@ void Runtime::scriptOpAnimG(ScriptArg_t arg) {
}
void Runtime::scriptOpAnimS(ScriptArg_t arg) {
- TAKE_STACK(kAnimDefStackArgs + 2);
+ TAKE_STACK_INT(kAnimDefStackArgs + 2);
AnimationDef animDef = stackArgsToAnimDef(stackArgs + 0);
@@ -2835,7 +2990,7 @@ void Runtime::scriptOpAnimS(ScriptArg_t arg) {
}
void Runtime::scriptOpAnim(ScriptArg_t arg) {
- TAKE_STACK(kAnimDefStackArgs + 2);
+ TAKE_STACK_INT(kAnimDefStackArgs + 2);
AnimationDef animDef = stackArgsToAnimDef(stackArgs + 0);
changeAnimation(animDef, animDef.firstFrame, true, _animSpeedDefault);
@@ -2849,7 +3004,7 @@ void Runtime::scriptOpAnim(ScriptArg_t arg) {
}
void Runtime::scriptOpStatic(ScriptArg_t arg) {
- TAKE_STACK(kAnimDefStackArgs);
+ TAKE_STACK_INT(kAnimDefStackArgs);
// QUIRK/BUG WORKAROUND: Static animations don't override other static animations!
//
@@ -2878,19 +3033,19 @@ void Runtime::scriptOpStatic(ScriptArg_t arg) {
}
void Runtime::scriptOpVarLoad(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
uint32 varID = (static_cast<uint32>(_roomNumber) << 16) | static_cast<uint32>(stackArgs[0]);
Common::HashMap<uint32, int32>::const_iterator it = _variables.find(varID);
if (it == _variables.end())
- _scriptStack.push_back(0);
+ _scriptStack.push_back(StackValue(0));
else
- _scriptStack.push_back(it->_value);
+ _scriptStack.push_back(StackValue(it->_value));
}
void Runtime::scriptOpVarStore(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
uint32 varID = (static_cast<uint32>(_roomNumber) << 16) | static_cast<uint32>(stackArgs[1]);
@@ -2898,7 +3053,7 @@ void Runtime::scriptOpVarStore(ScriptArg_t arg) {
}
void Runtime::scriptOpVarAddAndStore(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
uint32 varID = (static_cast<uint32>(_roomNumber) << 16) | static_cast<uint32>(stackArgs[0]);
@@ -2910,19 +3065,19 @@ void Runtime::scriptOpVarAddAndStore(ScriptArg_t arg) {
}
void Runtime::scriptOpVarGlobalLoad(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
uint32 varID = static_cast<uint32>(stackArgs[0]);
Common::HashMap<uint32, int32>::const_iterator it = _variables.find(varID);
if (it == _variables.end())
- _scriptStack.push_back(0);
+ _scriptStack.push_back(StackValue(0));
else
- _scriptStack.push_back(it->_value);
+ _scriptStack.push_back(StackValue(it->_value));
}
void Runtime::scriptOpVarGlobalStore(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
uint32 varID = static_cast<uint32>(stackArgs[1]);
@@ -2930,27 +3085,27 @@ void Runtime::scriptOpVarGlobalStore(ScriptArg_t arg) {
}
void Runtime::scriptOpItemCheck(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
for (const InventoryItem &item : _inventory) {
if (item.itemID == static_cast<uint>(stackArgs[0])) {
_scriptEnv.lastHighlightedItem = item.itemID;
- _scriptStack.push_back(1);
+ _scriptStack.push_back(StackValue(1));
return;
}
}
- _scriptStack.push_back(0);
+ _scriptStack.push_back(StackValue(0));
}
void Runtime::scriptOpItemRemove(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
inventoryRemoveItem(stackArgs[0]);
}
void Runtime::scriptOpItemHighlightSet(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
bool isHighlighted = (stackArgs[1] != 0);
@@ -2965,7 +3120,7 @@ void Runtime::scriptOpItemHighlightSet(ScriptArg_t arg) {
}
void Runtime::scriptOpItemAdd(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
if (stackArgs[0] == 0) {
// Weird special case, happens in Reah when breaking the glass barrier, this is called with 0 as the parameter.
@@ -2992,23 +3147,23 @@ void Runtime::scriptOpItemClear(ScriptArg_t arg) {
void Runtime::scriptOpItemHaveSpace(ScriptArg_t arg) {
for (const InventoryItem &item : _inventory) {
if (item.itemID == 0) {
- _scriptStack.push_back(1);
+ _scriptStack.push_back(StackValue(1));
return;
}
}
- _scriptStack.push_back(0);
+ _scriptStack.push_back(StackValue(0));
}
void Runtime::scriptOpSetCursor(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
if (stackArgs[0] < 0 || static_cast<uint>(stackArgs[0]) >= _cursors.size())
error("Invalid cursor ID");
uint resolvedCursorID = stackArgs[0];
- Common::HashMap<StackValue_t, uint>::const_iterator overrideIt = _scriptCursorIDToResourceIDOverride.find(resolvedCursorID);
+ Common::HashMap<StackInt_t, uint>::const_iterator overrideIt = _scriptCursorIDToResourceIDOverride.find(resolvedCursorID);
if (overrideIt != _scriptCursorIDToResourceIDOverride.end())
resolvedCursorID = overrideIt->_value;
@@ -3016,7 +3171,7 @@ void Runtime::scriptOpSetCursor(ScriptArg_t arg) {
}
void Runtime::scriptOpSetRoom(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
_roomNumber = stackArgs[0];
}
@@ -3036,60 +3191,115 @@ void Runtime::scriptOpLMB1(ScriptArg_t arg) {
}
void Runtime::scriptOpSoundS1(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
+
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
- triggerSound(false, stackArgs[0], 100, 0, false);
+ if (cachedSound)
+ triggerSound(false, *cachedSound, 100, 0, false);
}
void Runtime::scriptOpSoundS2(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT_NAMED(1, sndParamArgs);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
- triggerSound(false, stackArgs[0], stackArgs[1], 0, false);
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
+
+ if (cachedSound)
+ triggerSound(false, *cachedSound, sndParamArgs[0], 0, false);
}
void Runtime::scriptOpSoundS3(ScriptArg_t arg) {
- TAKE_STACK(3);
+ TAKE_STACK_INT_NAMED(2, sndParamArgs);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
+
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
- triggerSound(false, stackArgs[0], stackArgs[1], stackArgs[2], false);
+ if (cachedSound)
+ triggerSound(false, *cachedSound, sndParamArgs[0], sndParamArgs[1], false);
}
void Runtime::scriptOpSoundL1(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
- triggerSound(true, stackArgs[0], 100, 0, false);
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
+
+ if (cachedSound)
+ triggerSound(true, *cachedSound, 100, 0, false);
}
void Runtime::scriptOpSoundL2(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT_NAMED(1, sndParamArgs);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
+
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
- triggerSound(true, stackArgs[0], stackArgs[1], 0, false);
+ if (cachedSound)
+ triggerSound(true, *cachedSound, sndParamArgs[0], 0, false);
}
void Runtime::scriptOpSoundL3(ScriptArg_t arg) {
- TAKE_STACK(3);
+ TAKE_STACK_INT_NAMED(2, sndParamArgs);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
- triggerSound(true, stackArgs[0], stackArgs[1], stackArgs[2], false);
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
+
+ if (cachedSound)
+ triggerSound(true, *cachedSound, sndParamArgs[0], sndParamArgs[1], false);
}
void Runtime::scriptOp3DSoundL2(ScriptArg_t arg) {
- TAKE_STACK(4);
+ TAKE_STACK_INT_NAMED(3, sndParamArgs);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
+
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
- setSound3DParameters(stackArgs[0], stackArgs[2], stackArgs[3], _pendingSoundParams3D);
- triggerSound(true, stackArgs[0], stackArgs[1], 0, true);
+ if (cachedSound) {
+ setSound3DParameters(*cachedSound, sndParamArgs[1], sndParamArgs[2], _pendingSoundParams3D);
+ triggerSound(true, *cachedSound, sndParamArgs[0], 0, true);
+ }
}
void Runtime::scriptOp3DSoundL3(ScriptArg_t arg) {
- TAKE_STACK(5);
+ TAKE_STACK_INT_NAMED(4, sndParamArgs);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
+
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
- setSound3DParameters(stackArgs[0], stackArgs[3], stackArgs[4], _pendingSoundParams3D);
- triggerSound(true, stackArgs[0], stackArgs[1], stackArgs[2], true);
+ if (cachedSound) {
+ setSound3DParameters(*cachedSound, sndParamArgs[2], sndParamArgs[3], _pendingSoundParams3D);
+ triggerSound(true, *cachedSound, sndParamArgs[0], sndParamArgs[1], true);
+ }
}
void Runtime::scriptOp3DSoundS2(ScriptArg_t arg) {
- TAKE_STACK(4);
+ TAKE_STACK_INT_NAMED(3, sndParamArgs);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
+
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
- setSound3DParameters(stackArgs[0], stackArgs[2], stackArgs[3], _pendingSoundParams3D);
- triggerSound(false, stackArgs[0], stackArgs[1], 0, true);
+ if (cachedSound) {
+ setSound3DParameters(*cachedSound, sndParamArgs[1], sndParamArgs[2], _pendingSoundParams3D);
+ triggerSound(false, *cachedSound, sndParamArgs[0], 0, true);
+ }
}
void Runtime::scriptOpStopAL(ScriptArg_t arg) {
@@ -3097,7 +3307,7 @@ void Runtime::scriptOpStopAL(ScriptArg_t arg) {
}
void Runtime::scriptOpAddXSound(ScriptArg_t arg) {
- TAKE_STACK(4);
+ TAKE_STACK_INT(4);
warning("AddXSound not implemented yet");
(void)stackArgs;
@@ -3112,14 +3322,14 @@ void Runtime::scriptOpStopSndLA(ScriptArg_t arg) {
}
void Runtime::scriptOpStopSndLO(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
warning("StopSndLO not implemented yet");
(void)stackArgs;
}
void Runtime::scriptOpRange(ScriptArg_t arg) {
- TAKE_STACK(3);
+ TAKE_STACK_INT(3);
_pendingSoundParams3D.minRange = stackArgs[0];
_pendingSoundParams3D.maxRange = stackArgs[1];
@@ -3127,27 +3337,27 @@ void Runtime::scriptOpRange(ScriptArg_t arg) {
}
void Runtime::scriptOpMusic(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
changeMusicTrack(stackArgs[0]);
}
void Runtime::scriptOpMusicUp(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
warning("Music volume ramp up is not implemented");
(void)stackArgs;
}
void Runtime::scriptOpMusicDn(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
warning("Music volume ramp down is not implemented");
(void)stackArgs;
}
void Runtime::scriptOpParm0(ScriptArg_t arg) {
- TAKE_STACK(4);
+ TAKE_STACK_INT(4);
if (stackArgs[0] < 0 || static_cast<uint>(stackArgs[0]) >= GyroState::kNumGyros)
error("Invalid gyro index for Parm0");
@@ -3161,7 +3371,7 @@ void Runtime::scriptOpParm0(ScriptArg_t arg) {
}
void Runtime::scriptOpParm1(ScriptArg_t arg) {
- TAKE_STACK(3);
+ TAKE_STACK_INT(3);
if (stackArgs[0] < 0 || static_cast<uint>(stackArgs[0]) >= GyroState::kNumGyros)
error("Invalid gyro index for Parm1");
@@ -3176,7 +3386,7 @@ void Runtime::scriptOpParm1(ScriptArg_t arg) {
}
void Runtime::scriptOpParm2(ScriptArg_t arg) {
- TAKE_STACK(3);
+ TAKE_STACK_INT(3);
_gyros.completeInteraction = stackArgs[0];
_gyros.failureInteraction = stackArgs[1];
@@ -3187,7 +3397,7 @@ void Runtime::scriptOpParm2(ScriptArg_t arg) {
}
void Runtime::scriptOpParm3(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
if (stackArgs[0] < 0 || static_cast<uint>(stackArgs[0]) >= GyroState::kNumGyros)
error("Invalid gyro index for Parm3");
@@ -3199,7 +3409,7 @@ void Runtime::scriptOpParm3(ScriptArg_t arg) {
}
void Runtime::scriptOpParmG(ScriptArg_t arg) {
- TAKE_STACK(3);
+ TAKE_STACK_INT(3);
int32 gyroSlot = stackArgs[0];
int32 dragMargin = stackArgs[1];
@@ -3214,7 +3424,7 @@ void Runtime::scriptOpParmG(ScriptArg_t arg) {
}
void Runtime::scriptOpSParmX(ScriptArg_t arg) {
- TAKE_STACK(3);
+ TAKE_STACK_INT(3);
_pendingStaticAnimParams.initialDelay = stackArgs[0];
_pendingStaticAnimParams.repeatDelay = stackArgs[1];
@@ -3225,7 +3435,7 @@ void Runtime::scriptOpSParmX(ScriptArg_t arg) {
}
void Runtime::scriptOpSAnimX(ScriptArg_t arg) {
- TAKE_STACK(kAnimDefStackArgs * 2 + 1);
+ TAKE_STACK_INT(kAnimDefStackArgs * 2 + 1);
AnimationDef animDef1 = stackArgsToAnimDef(stackArgs + 0);
AnimationDef animDef2 = stackArgsToAnimDef(stackArgs + kAnimDefStackArgs);
@@ -3246,113 +3456,158 @@ void Runtime::scriptOpSAnimX(ScriptArg_t arg) {
}
void Runtime::scriptOpVolumeUp3(ScriptArg_t arg) {
- TAKE_STACK(3);
+ TAKE_STACK_INT_NAMED(2, sndParamArgs);
+ TAKE_STACK_VAR_NAMED(1, sndIDArgs);
+
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByNameOrID(sndIDArgs[0], soundID, cachedSound);
- triggerSoundRamp(stackArgs[0], stackArgs[1] * 100, stackArgs[2], false);
+ if (cachedSound)
+ triggerSoundRamp(*cachedSound, sndParamArgs[0] * 100, sndParamArgs[1], false);
}
void Runtime::scriptOpVolumeDn2(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT_NAMED(1, sndParamArgs);
+ TAKE_STACK_VAR_NAMED(1, sndIDArgs);
+
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByNameOrID(sndIDArgs[0], soundID, cachedSound);
// FIXME: Just do this instantly
- triggerSoundRamp(stackArgs[0], 1, stackArgs[1], false);
+ if (cachedSound)
+ triggerSoundRamp(*cachedSound, 1, sndParamArgs[0], false);
}
void Runtime::scriptOpVolumeDn3(ScriptArg_t arg) {
- TAKE_STACK(3);
+ TAKE_STACK_INT_NAMED(2, sndParamArgs);
+ TAKE_STACK_VAR_NAMED(1, sndIDArgs);
+
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByNameOrID(sndIDArgs[0], soundID, cachedSound);
- triggerSoundRamp(stackArgs[0], stackArgs[1] * 100, stackArgs[2], false);
+ if (cachedSound)
+ triggerSoundRamp(*cachedSound, sndParamArgs[0] * 100, sndParamArgs[1], false);
}
void Runtime::scriptOpVolumeDn4(ScriptArg_t arg) {
- TAKE_STACK(4);
+ TAKE_STACK_INT_NAMED(3, sndParamArgs);
+ TAKE_STACK_VAR_NAMED(1, sndIDArgs);
- triggerSoundRamp(stackArgs[0], stackArgs[1] * 100, stackArgs[2], stackArgs[3] != 0);
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByNameOrID(sndIDArgs[0], soundID, cachedSound);
+
+ if (cachedSound)
+ triggerSoundRamp(*cachedSound, sndParamArgs[0] * 100, sndParamArgs[1], sndParamArgs[2] != 0);
}
void Runtime::scriptOpRandom(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
if (stackArgs[0] == 0)
- _scriptStack.push_back(0);
+ _scriptStack.push_back(StackValue(0));
else
- _scriptStack.push_back(_rng->getRandomNumber(stackArgs[0] - 1));
+ _scriptStack.push_back(StackValue(_rng->getRandomNumber(stackArgs[0] - 1)));
}
void Runtime::scriptOpDrop(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_VAR(1);
(void)stackArgs;
}
void Runtime::scriptOpDup(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_VAR(1);
_scriptStack.push_back(stackArgs[0]);
_scriptStack.push_back(stackArgs[0]);
}
void Runtime::scriptOpSwap(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_VAR(2);
- _scriptStack.push_back(stackArgs[1]);
- _scriptStack.push_back(stackArgs[0]);
+ _scriptStack.push_back(Common::move(stackArgs[1]));
+ _scriptStack.push_back(Common::move(stackArgs[0]));
}
void Runtime::scriptOpSay1(ScriptArg_t arg) {
- TAKE_STACK(3);
+ TAKE_STACK_INT_NAMED(2, sndParamArgs);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
warning("Say1 cycles are not implemented yet, playing first sound in the cycle");
- uint soundID = stackArgs[0];
- // uint cycleLength = stackArgs[2];
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
- triggerSound(false, soundID, 100, 0, false);
+ // uint ? = sndParamArgs[0];
+ // uint cycleLength = sndParamArgs[1];
+
+ if (cachedSound)
+ triggerSound(false, *cachedSound, 100, 0, false);
}
void Runtime::scriptOpSay3(ScriptArg_t arg) {
- TAKE_STACK(3);
+ TAKE_STACK_INT_NAMED(2, sndParamArgs);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
+
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
- TriggeredOneShot oneShot;
- oneShot.soundID = stackArgs[0];
- oneShot.uniqueSlot = stackArgs[1];
+ if (cachedSound) {
+ TriggeredOneShot oneShot;
+ oneShot.soundID = soundID;
+ oneShot.uniqueSlot = sndParamArgs[0];
- // The third param seems to control sound interruption, but say3 is a Reah-only op and it's only ever 1.
- if (stackArgs[2] != 1)
- error("Invalid interrupt arg for say3, only 1 is supported.");
+ // The third param seems to control sound interruption, but say3 is a Reah-only op and it's only ever 1.
+ if (sndParamArgs[1] != 1)
+ error("Invalid interrupt arg for say3, only 1 is supported.");
- if (Common::find(_triggeredOneShots.begin(), _triggeredOneShots.end(), oneShot) == _triggeredOneShots.end()) {
- triggerSound(false, oneShot.soundID, 100, 0, false);
- _triggeredOneShots.push_back(oneShot);
+ if (Common::find(_triggeredOneShots.begin(), _triggeredOneShots.end(), oneShot) == _triggeredOneShots.end()) {
+ triggerSound(false, *cachedSound, 100, 0, false);
+ _triggeredOneShots.push_back(oneShot);
+ }
}
}
void Runtime::scriptOpSay3Get(ScriptArg_t arg) {
- TAKE_STACK(3);
-
- TriggeredOneShot oneShot;
- oneShot.soundID = stackArgs[0];
- oneShot.uniqueSlot = stackArgs[1];
-
- // The third param seems to control sound interruption, but say3 is a Reah-only op and it's only ever 1.
- if (stackArgs[2] != 1)
- error("Invalid interrupt arg for say3, only 1 is supported.");
-
- if (Common::find(_triggeredOneShots.begin(), _triggeredOneShots.end(), oneShot) == _triggeredOneShots.end()) {
- triggerSound(false, oneShot.soundID, 100, 0, false);
- _triggeredOneShots.push_back(oneShot);
- _scriptStack.push_back(oneShot.soundID);
+ TAKE_STACK_INT_NAMED(2, sndParamArgs);
+ TAKE_STACK_STR_NAMED(1, sndNameArgs);
+
+ StackInt_t soundID = 0;
+ CachedSound *cachedSound = nullptr;
+ resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
+
+ if (cachedSound) {
+ TriggeredOneShot oneShot;
+ oneShot.soundID = soundID;
+ oneShot.uniqueSlot = sndParamArgs[0];
+
+ // The third param seems to control sound interruption, but say3 is a Reah-only op and it's only ever 1.
+ if (sndParamArgs[1] != 1)
+ error("Invalid interrupt arg for say3, only 1 is supported.");
+
+ if (Common::find(_triggeredOneShots.begin(), _triggeredOneShots.end(), oneShot) == _triggeredOneShots.end()) {
+ triggerSound(false, *cachedSound, 100, 0, false);
+ _triggeredOneShots.push_back(oneShot);
+ _scriptStack.push_back(StackValue(soundID));
+ } else
+ _scriptStack.push_back(StackValue(0));
} else
- _scriptStack.push_back(0);
+ _scriptStack.push_back(StackValue(0));
}
void Runtime::scriptOpSetTimer(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
_timers[static_cast<uint>(stackArgs[0])] = g_system->getMillis() + static_cast<uint32>(stackArgs[1]) * 1000u;
}
void Runtime::scriptOpGetTimer(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
bool isCompleted = true;
@@ -3360,11 +3615,11 @@ void Runtime::scriptOpGetTimer(ScriptArg_t arg) {
if (timerIt != _timers.end())
isCompleted = (g_system->getMillis() >= timerIt->_value);
- _scriptStack.push_back(isCompleted ? 1 : 0);
+ _scriptStack.push_back(StackValue(isCompleted ? 1 : 0));
}
void Runtime::scriptOpDelay(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
_gameState = kGameStateDelay;
_delayCompletionTime = g_system->getMillis() + stackArgs[0];
@@ -3387,7 +3642,7 @@ void Runtime::scriptOpHiGet(ScriptArg_t arg) {
}
void Runtime::scriptOpVerticalPanSet(bool *flags) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
uint baseDirection = static_cast<uint>(stackArgs[0]) % kNumDirections;
uint radius = stackArgs[1];
@@ -3411,7 +3666,7 @@ void Runtime::scriptOpVerticalPanSet(bool *flags) {
}
void Runtime::scriptOpVerticalPanGet() {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
// In any scenario where this is used, there is a corresponding hi/lo set and this only ever triggers off of interactions,
// so don't really even need to check anything other than the facing direction?
@@ -3423,11 +3678,11 @@ void Runtime::scriptOpVerticalPanGet() {
bool isInRadius = (rtDirection <= radius || lfDirection <= radius);
- _scriptStack.push_back(isInRadius ? 1 : 0);
+ _scriptStack.push_back(StackValue(isInRadius ? 1 : 0));
}
void Runtime::scriptOpSaveAs(ScriptArg_t arg) {
- TAKE_STACK(4);
+ TAKE_STACK_INT(4);
// Just ignore this op, it looks like it's for save room remapping of some sort but we allow
// saves at any idle screen.
@@ -3443,103 +3698,103 @@ void Runtime::scriptOpExit(ScriptArg_t arg) {
}
void Runtime::scriptOpNot(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
- _scriptStack.push_back((stackArgs[0] == 0) ? 1 : 0);
+ _scriptStack.push_back(StackValue((stackArgs[0] == 0) ? 1 : 0));
}
void Runtime::scriptOpAnd(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
- _scriptStack.push_back((stackArgs[0] != 0 && stackArgs[1] != 0) ? 1 : 0);
+ _scriptStack.push_back(StackValue((stackArgs[0] != 0 && stackArgs[1] != 0) ? 1 : 0));
}
void Runtime::scriptOpOr(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
- _scriptStack.push_back((stackArgs[0] != 0 || stackArgs[1] != 0) ? 1 : 0);
+ _scriptStack.push_back(StackValue((stackArgs[0] != 0 || stackArgs[1] != 0) ? 1 : 0));
}
void Runtime::scriptOpAdd(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
- _scriptStack.push_back(stackArgs[0] + stackArgs[1]);
+ _scriptStack.push_back(StackValue(stackArgs[0] + stackArgs[1]));
}
void Runtime::scriptOpSub(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
- _scriptStack.push_back(stackArgs[0] - stackArgs[1]);
+ _scriptStack.push_back(StackValue(stackArgs[0] - stackArgs[1]));
}
void Runtime::scriptOpNegate(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
- _scriptStack.push_back(-stackArgs[0]);
+ _scriptStack.push_back(StackValue(-stackArgs[0]));
}
void Runtime::scriptOpCmpEq(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
- _scriptStack.push_back((stackArgs[0] == stackArgs[1]) ? 1 : 0);
+ _scriptStack.push_back(StackValue((stackArgs[0] == stackArgs[1]) ? 1 : 0));
}
void Runtime::scriptOpCmpLt(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
- _scriptStack.push_back((stackArgs[0] < stackArgs[1]) ? 1 : 0);
+ _scriptStack.push_back(StackValue((stackArgs[0] < stackArgs[1]) ? 1 : 0));
}
void Runtime::scriptOpCmpGt(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
- _scriptStack.push_back((stackArgs[0] > stackArgs[1]) ? 1 : 0);
+ _scriptStack.push_back(StackValue((stackArgs[0] > stackArgs[1]) ? 1 : 0));
}
void Runtime::scriptOpBitLoad(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
- _scriptStack.push_back((stackArgs[0] >> stackArgs[1]) & 1);
+ _scriptStack.push_back(StackValue((stackArgs[0] >> stackArgs[1]) & 1));
}
void Runtime::scriptOpBitSet0(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
ScriptArg_t bitMask = static_cast<ScriptArg_t>(1) << stackArgs[1];
- _scriptStack.push_back(stackArgs[0] & ~bitMask);
+ _scriptStack.push_back(StackValue(stackArgs[0] & ~bitMask));
}
void Runtime::scriptOpBitSet1(ScriptArg_t arg) {
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
ScriptArg_t bitMask = static_cast<ScriptArg_t>(1) << stackArgs[1];
- _scriptStack.push_back(stackArgs[0] | bitMask);
+ _scriptStack.push_back(StackValue(stackArgs[0] | bitMask));
}
void Runtime::scriptOpDisc1(ScriptArg_t arg) {
// Disc check, always pass
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
(void)stackArgs;
- _scriptStack.push_back(1);
+ _scriptStack.push_back(StackValue(1));
}
void Runtime::scriptOpDisc2(ScriptArg_t arg) {
// Disc check, always pass
- TAKE_STACK(2);
+ TAKE_STACK_INT(2);
(void)stackArgs;
- _scriptStack.push_back(1);
+ _scriptStack.push_back(StackValue(1));
}
void Runtime::scriptOpDisc3(ScriptArg_t arg) {
// Disc check, always pass
- TAKE_STACK(3);
+ TAKE_STACK_INT(3);
(void)stackArgs;
- _scriptStack.push_back(1);
+ _scriptStack.push_back(StackValue(1));
}
void Runtime::scriptOpGoto(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
uint newInteraction = static_cast<uint>(stackArgs[0]);
@@ -3569,7 +3824,7 @@ void Runtime::scriptOpGoto(ScriptArg_t arg) {
}
void Runtime::scriptOpEscOn(ScriptArg_t arg) {
- TAKE_STACK(1);
+ TAKE_STACK_INT(1);
_escOn = (stackArgs[0] != 0);
}
@@ -3611,7 +3866,7 @@ void Runtime::scriptOpValueName(ScriptArg_t arg) {
if (it == roomDef->values.end())
error("Value '%s' doesn't exist in room %i", varName.c_str(), static_cast<int>(_roomNumber));
- _scriptStack.push_back(it->_value);
+ _scriptStack.push_back(StackValue(it->_value));
}
void Runtime::scriptOpVarName(ScriptArg_t arg) {
@@ -3628,44 +3883,23 @@ void Runtime::scriptOpVarName(ScriptArg_t arg) {
if (it == roomDef->vars.end())
error("Var '%s' doesn't exist in room %i", varName.c_str(), static_cast<int>(_roomNumber));
- _scriptStack.push_back(it->_value);
+ _scriptStack.push_back(StackValue(it->_value));
}
void Runtime::scriptOpSoundName(ScriptArg_t arg) {
- Common::String sndName = _scriptSet->strings[arg];
-
- uint soundID = 0;
- for (uint i = 0; i < 4; i++)
- soundID = soundID * 10u + (sndName[i] - '0');
-
- sndName.toLowercase();
-
- Common::HashMap<uint, Common::SharedPtr<CachedSound> >::const_iterator cachedSoundIt = _cachedSounds.find(soundID);
- if (cachedSoundIt != _cachedSounds.end() && cachedSoundIt->_value->name != sndName) {
- _cachedSounds.erase(cachedSoundIt);
- cachedSoundIt = _cachedSounds.end();
- }
-
- if (cachedSoundIt == _cachedSounds.end()) {
- Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator waveIt = _waves.find(sndName);
-
- if (waveIt != _waves.end())
- loadWave(soundID, sndName, waveIt->_value);
- }
-
- _scriptStack.push_back(soundID);
+ _scriptStack.push_back(StackValue(_scriptSet->strings[arg]));
}
void Runtime::scriptOpCursorName(ScriptArg_t arg) {
const Common::String &cursorName = _scriptSet->strings[arg];
- Common::HashMap<Common::String, StackValue_t>::const_iterator namedCursorIt = _namedCursors.find(cursorName);
+ Common::HashMap<Common::String, StackInt_t>::const_iterator namedCursorIt = _namedCursors.find(cursorName);
if (namedCursorIt == _namedCursors.end()) {
error("Unimplemented cursor name '%s'", cursorName.c_str());
return;
}
- _scriptStack.push_back(namedCursorIt->_value);
+ _scriptStack.push_back(StackValue(namedCursorIt->_value));
}
void Runtime::scriptOpDubbing(ScriptArg_t arg) {
@@ -3675,7 +3909,7 @@ void Runtime::scriptOpDubbing(ScriptArg_t arg) {
void Runtime::scriptOpCheckValue(ScriptArg_t arg) {
PEEK_STACK(1);
- if (arg == stackArgs[0])
+ if (stackArgs[0].type == StackValue::kNumber && stackArgs[0].value.i == arg)
_scriptStack.pop_back();
else
_scriptNextInstruction++;
@@ -3685,7 +3919,12 @@ void Runtime::scriptOpJump(ScriptArg_t arg) {
_scriptNextInstruction = arg;
}
-#undef TAKE_STACK
+#undef TAKE_STACK_STR
+#undef TAKE_STACK_STR_NAMED
+#undef TAKE_STACK_INT
+#undef TAKE_STACK_INT_NAMED
+#undef TAKE_STACK_VAR
+#undef TAKE_STACK_VAR_NAMED
#undef PEEK_STACK
#undef OPCODE_STUB
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index 185030d1b1f..452fc691e86 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -195,6 +195,8 @@ struct CachedSound {
Common::SharedPtr<Audio::AudioStream> loopingStream;
Common::SharedPtr<AudioPlayer> player;
+ uint id;
+
uint rampStartVolume;
uint rampEndVolume;
uint32 rampRatePerMSec;
@@ -450,7 +452,39 @@ private:
};
typedef int32 ScriptArg_t;
- typedef int32 StackValue_t;
+ typedef int32 StackInt_t;
+
+ struct StackValue {
+ enum StackValueType {
+ kNumber,
+ kString,
+ };
+
+ union ValueUnion {
+ StackInt_t i;
+ Common::String s;
+
+ ValueUnion();
+ explicit ValueUnion(StackInt_t iVal);
+ explicit ValueUnion(const Common::String &strVal);
+ explicit ValueUnion(Common::String &&strVal);
+ ~ValueUnion();
+ };
+
+ StackValue();
+ StackValue(const StackValue &other);
+ StackValue(StackValue &&other);
+ explicit StackValue(StackInt_t i);
+ explicit StackValue(const Common::String &str);
+ explicit StackValue(Common::String &&str);
+ ~StackValue();
+
+ StackValue &operator=(const StackValue &other);
+ StackValue &operator=(StackValue &&other);
+
+ StackValueType type;
+ ValueUnion value;
+ };
bool runIdle();
bool runDelay();
@@ -475,7 +509,10 @@ private:
void loadIndex();
void findWaves();
- void loadWave(uint soundID, const Common::String &soundName, const Common::ArchiveMemberPtr &archiveMemberPtr);
+ Common::SharedPtr<CachedSound> loadWave(const Common::String &soundName, uint soundID, const Common::ArchiveMemberPtr &archiveMemberPtr);
+ void resolveSoundByName(const Common::String &soundName, StackInt_t &outSoundID, CachedSound *&outWave);
+ void resolveSoundByNameOrID(const StackValue &stackValue, StackInt_t &outSoundID, CachedSound *&outWave);
+
void changeToScreen(uint roomNumber, uint screenNumber);
void returnToIdleState();
void changeToCursor(const Common::SharedPtr<Graphics::WinCursorGroup> &cursor);
@@ -491,14 +528,14 @@ private:
void changeAnimation(const AnimationDef &animDef, uint initialFrame, bool consumeFPSOverride);
void changeAnimation(const AnimationDef &animDef, uint initialFrame, bool consumeFPSOverride, const Fraction &defaultFrameRate);
- void setSound3DParameters(uint soundID, int32 x, int32 y, const SoundParams3D &soundParams3D);
- void triggerSound(bool looping, uint soundID, uint volume, int32 balance, bool is3D);
- void triggerSoundRamp(uint soundID, uint durationMSec, uint newVolume, bool terminateOnCompletion);
+ void setSound3DParameters(CachedSound &sound, int32 x, int32 y, const SoundParams3D &soundParams3D);
+ void triggerSound(bool looping, CachedSound &sound, uint volume, int32 balance, bool is3D);
+ void triggerSoundRamp(CachedSound &sound, uint durationMSec, uint newVolume, bool terminateOnCompletion);
void updateSounds(uint32 timestamp);
void update3DSounds();
bool computeEffectiveVolumeAndBalance(CachedSound &snd);
- AnimationDef stackArgsToAnimDef(const StackValue_t *args) const;
+ AnimationDef stackArgsToAnimDef(const StackInt_t *args) const;
void pushAnimDef(const AnimationDef &animDef);
void activateScript(const Common::SharedPtr<Script> &script, const ScriptEnvironmentVars &envVars);
@@ -657,8 +694,8 @@ private:
uint _panCursors[kPanCursorMaxCount];
- Common::HashMap<Common::String, StackValue_t> _namedCursors;
- Common::HashMap<StackValue_t, uint> _scriptCursorIDToResourceIDOverride;
+ Common::HashMap<Common::String, StackInt_t> _namedCursors;
+ Common::HashMap<StackInt_t, uint> _scriptCursorIDToResourceIDOverride;
OSystem *_system;
uint _roomNumber; // Room number can be changed independently of the loaded room, the screen doesn't change until a command changes it
@@ -708,7 +745,7 @@ private:
Common::SharedPtr<Script> _activeScript;
uint _scriptNextInstruction;
- Common::Array<StackValue_t> _scriptStack;
+ Common::Array<StackValue> _scriptStack;
ScriptEnvironmentVars _scriptEnv;
Common::SharedPtr<Common::RandomSource> _rng;
@@ -769,7 +806,7 @@ private:
Common::Array<OSEvent> _pendingEvents;
Common::HashMap<Common::String, Common::ArchiveMemberPtr> _waves;
- Common::HashMap<uint, Common::SharedPtr<CachedSound> > _cachedSounds;
+ Common::Array<Common::SharedPtr<CachedSound> > _activeSounds;
SoundParams3D _pendingSoundParams3D;
Common::Array<TriggeredOneShot> _triggeredOneShots;
Commit: 445de7972c3128370f19335feeec06afbfa5b3be
https://github.com/scummvm/scummvm/commit/445de7972c3128370f19335feeec06afbfa5b3be
Author: elasota (ejlasota at gmail.com)
Date: 2023-04-01T12:01:48-04:00
Commit Message:
VCRUISE: Refactor sound cache so sounds can be unloaded
Changed paths:
engines/vcruise/runtime.cpp
engines/vcruise/runtime.h
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 6049434a0ab..d8bbe3fb444 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -367,12 +367,7 @@ void SfxData::load(Common::SeekableReadStream &stream, Audio::Mixer *mixer) {
}
}
-CachedSound::CachedSound()
- : id(0), rampStartVolume(0), rampEndVolume(0), rampRatePerMSec(0), rampStartTime(0), rampTerminateOnCompletion(false),
- volume(0), balance(0), effectiveBalance(0), effectiveVolume(0), is3D(false), x(0), y(0), z(0) {
-}
-
-CachedSound::~CachedSound() {
+SoundCache::~SoundCache() {
// Dispose player first so playback stops
this->player.reset();
@@ -382,6 +377,14 @@ CachedSound::~CachedSound() {
this->stream.reset();
}
+SoundInstance::SoundInstance()
+ : id(0), rampStartVolume(0), rampEndVolume(0), rampRatePerMSec(0), rampStartTime(0), rampTerminateOnCompletion(false),
+ volume(0), balance(0), effectiveBalance(0), effectiveVolume(0), is3D(false), x(0), y(0), z(0), evictTime(0) {
+}
+
+SoundInstance::~SoundInstance() {
+}
+
TriggeredOneShot::TriggeredOneShot() : soundID(0), uniqueSlot(0) {
}
@@ -427,7 +430,7 @@ Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &roo
/*_loadedArea(0), */_lmbDown(false), _lmbDragging(false), _lmbReleaseWasClick(false), _lmbDownTime(0),
_delayCompletionTime(0),
_panoramaState(kPanoramaStateInactive),
- _listenerX(0), _listenerY(0), _listenerAngle(0) {
+ _listenerX(0), _listenerY(0), _listenerAngle(0), _soundCacheIndex(0) {
for (uint i = 0; i < kNumDirections; i++) {
_haveIdleAnimations[i] = false;
@@ -1437,8 +1440,8 @@ void Runtime::findWaves() {
}
}
-Common::SharedPtr<CachedSound> Runtime::loadWave(const Common::String &soundName, uint soundID, const Common::ArchiveMemberPtr &archiveMemberPtr) {
- for (const Common::SharedPtr<CachedSound> &activeSound : _activeSounds) {
+Common::SharedPtr<SoundInstance> Runtime::loadWave(const Common::String &soundName, uint soundID, const Common::ArchiveMemberPtr &archiveMemberPtr) {
+ for (const Common::SharedPtr<SoundInstance> &activeSound : _activeSounds) {
if (activeSound->name == soundName)
return activeSound;
}
@@ -1455,28 +1458,72 @@ Common::SharedPtr<CachedSound> Runtime::loadWave(const Common::String &soundName
return nullptr;
}
- Common::SharedPtr<CachedSound> cachedSound(new CachedSound());
+ Common::SharedPtr<SoundInstance> soundInstance(new SoundInstance());
- cachedSound->stream.reset(audioStream);
- cachedSound->name = soundName;
- cachedSound->id = soundID;
+ soundInstance->name = soundName;
+ soundInstance->id = soundID;
bool foundExisting = false;
- for (Common::SharedPtr<CachedSound> &existingSound : _activeSounds) {
+ for (Common::SharedPtr<SoundInstance> &existingSound : _activeSounds) {
if (existingSound->id == soundID) {
- existingSound = cachedSound;
+ existingSound = soundInstance;
foundExisting = true;
break;
}
}
if (!foundExisting)
- _activeSounds.push_back(cachedSound);
+ _activeSounds.push_back(soundInstance);
+
+ return soundInstance;
+}
+
+SoundCache *Runtime::loadCache(SoundInstance &sound) {
+ if (sound.cache)
+ return sound.cache.get();
+
+ // See if this is already in the cache
+ for (const Common::Pair<Common::String, Common::SharedPtr<SoundCache> > &cacheItem : _soundCache) {
+ if (cacheItem.first == sound.name) {
+ sound.cache = cacheItem.second;
+ return sound.cache.get();
+ }
+ }
+
+ Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator waveIt = _waves.find(sound.name);
+
+ if (waveIt == _waves.end())
+ return nullptr;
+
+ Common::SeekableReadStream *stream = waveIt->_value->createReadStream();
+ if (!stream) {
+ warning("Couldn't open read stream for sound '%s'", sound.name.c_str());
+ return nullptr;
+ }
+
+ Audio::SeekableAudioStream *audioStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
+ if (!audioStream) {
+ warning("Couldn't open audio stream for sound '%s'", sound.name.c_str());
+ return nullptr;
+ }
+
+ Common::SharedPtr<SoundCache> cachedSound(new SoundCache());
+
+ cachedSound->stream.reset(audioStream);
+
+ _soundCache[_soundCacheIndex].first = sound.name;
+ _soundCache[_soundCacheIndex].second = cachedSound;
+
+ _soundCacheIndex++;
+ if (_soundCacheIndex == kSoundCacheSize)
+ _soundCacheIndex = 0;
- return cachedSound;
+ sound.cache = cachedSound;
+
+ return cachedSound.get();
}
-void Runtime::resolveSoundByName(const Common::String &soundName, StackInt_t &outSoundID, CachedSound *&outWave) {
+void Runtime::resolveSoundByName(const Common::String &soundName, StackInt_t &outSoundID, SoundInstance *&outWave) {
Common::String sndName = soundName;
uint soundID = 0;
@@ -1488,7 +1535,7 @@ void Runtime::resolveSoundByName(const Common::String &soundName, StackInt_t &ou
outSoundID = soundID;
outWave = nullptr;
- for (const Common::SharedPtr<CachedSound> &snd : _activeSounds) {
+ for (const Common::SharedPtr<SoundInstance> &snd : _activeSounds) {
if (snd->name == sndName) {
outWave = snd.get();
return;
@@ -1498,19 +1545,19 @@ void Runtime::resolveSoundByName(const Common::String &soundName, StackInt_t &ou
Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator waveIt = _waves.find(sndName);
if (waveIt != _waves.end()) {
- Common::SharedPtr<CachedSound> snd = loadWave(sndName, soundID, waveIt->_value);
+ Common::SharedPtr<SoundInstance> snd = loadWave(sndName, soundID, waveIt->_value);
outWave = snd.get();
}
}
-void Runtime::resolveSoundByNameOrID(const StackValue &stackValue, StackInt_t &outSoundID, CachedSound *&outWave) {
+void Runtime::resolveSoundByNameOrID(const StackValue &stackValue, StackInt_t &outSoundID, SoundInstance *&outWave) {
outSoundID = 0;
outWave = nullptr;
if (stackValue.type == StackValue::kNumber) {
outSoundID = stackValue.value.i;
- for (const Common::SharedPtr<CachedSound> &snd : _activeSounds) {
+ for (const Common::SharedPtr<SoundInstance> &snd : _activeSounds) {
if (snd->id == static_cast<uint>(stackValue.value.i)) {
outWave = snd.get();
break;
@@ -1983,50 +2030,57 @@ void Runtime::changeAnimation(const AnimationDef &animDef, uint initialFrame, bo
debug(1, "Animation last frame set to %u", animDef.lastFrame);
}
-void Runtime::setSound3DParameters(CachedSound &snd, int32 x, int32 y, const SoundParams3D &soundParams3D) {
+void Runtime::setSound3DParameters(SoundInstance &snd, int32 x, int32 y, const SoundParams3D &soundParams3D) {
snd.x = x;
snd.y = y;
snd.params3D = soundParams3D;
}
-void Runtime::triggerSound(bool looping, CachedSound &snd, uint volume, int32 balance, bool is3D) {
+void Runtime::triggerSound(bool looping, SoundInstance &snd, uint volume, int32 balance, bool is3D) {
snd.volume = volume;
snd.balance = balance;
snd.is3D = is3D;
computeEffectiveVolumeAndBalance(snd);
+ SoundCache *cache = loadCache(snd);
+
// Reset if looping state changes
- if (snd.loopingStream && !looping) {
- snd.player.reset();
- snd.loopingStream.reset();
- snd.stream->rewind();
+ if (cache->loopingStream && !looping) {
+ cache->player.reset();
+ cache->loopingStream.reset();
+ cache->stream->rewind();
}
- if (!snd.loopingStream && looping)
- snd.player.reset();
+ if (!cache->loopingStream && looping)
+ cache->player.reset();
// Construct looping stream if needed and none exists
- if (looping && !snd.loopingStream)
- snd.loopingStream.reset(new Audio::LoopingAudioStream(snd.stream.get(), 0, DisposeAfterUse::NO, true));
+ if (looping && !cache->loopingStream)
+ cache->loopingStream.reset(new Audio::LoopingAudioStream(cache->stream.get(), 0, DisposeAfterUse::NO, true));
- if (snd.player) {
+ if (cache->player) {
// If there is already a player and this is non-looping, start over
if (!looping) {
- snd.player->stop();
- snd.stream->rewind();
- snd.player->play(snd.effectiveVolume, snd.effectiveBalance);
+ cache->player->stop();
+ cache->stream->rewind();
+ cache->player->play(snd.effectiveVolume, snd.effectiveBalance);
} else {
// Adjust volume and balance at least
- snd.player->setVolumeAndBalance(snd.effectiveVolume, snd.effectiveBalance);
+ cache->player->setVolumeAndBalance(snd.effectiveVolume, snd.effectiveBalance);
}
} else {
- snd.player.reset(new AudioPlayer(_mixer, looping ? snd.loopingStream.staticCast<Audio::AudioStream>() : snd.stream.staticCast<Audio::AudioStream>()));
- snd.player->play(snd.effectiveVolume, snd.effectiveBalance);
+ cache->player.reset(new AudioPlayer(_mixer, looping ? cache->loopingStream.staticCast<Audio::AudioStream>() : cache->stream.staticCast<Audio::AudioStream>()));
+ cache->player->play(snd.effectiveVolume, snd.effectiveBalance);
}
+
+ if (looping)
+ snd.evictTime = 0;
+ else
+ snd.evictTime = g_system->getMillis(true) + static_cast<uint32>(cache->stream->getLength().msecs()) + 20000u;
}
-void Runtime::triggerSoundRamp(CachedSound &snd, uint durationMSec, uint newVolume, bool terminateOnCompletion) {
+void Runtime::triggerSoundRamp(SoundInstance &snd, uint durationMSec, uint newVolume, bool terminateOnCompletion) {
snd.rampStartVolume = snd.volume;
snd.rampEndVolume = newVolume;
snd.rampTerminateOnCompletion = terminateOnCompletion;
@@ -2037,11 +2091,20 @@ void Runtime::triggerSoundRamp(CachedSound &snd, uint durationMSec, uint newVolu
snd.rampRatePerMSec = 65536 / durationMSec;
}
-void Runtime::updateSounds(uint32 timestamp) {
- Common::Array<uint> condemnedSounds;
+void Runtime::stopSound(SoundInstance &sound) {
+ if (!sound.cache)
+ return;
+
+ if (sound.cache->player)
+ sound.cache->player->stop();
+
+ sound.cache.reset();
+ sound.evictTime = g_system->getMillis(true) + 20000u;
+}
+void Runtime::updateSounds(uint32 timestamp) {
for (uint sndIndex = 0; sndIndex < _activeSounds.size(); sndIndex++) {
- CachedSound &snd = *_activeSounds[sndIndex];
+ SoundInstance &snd = *_activeSounds[sndIndex];
if (snd.rampRatePerMSec) {
uint ramp = snd.rampRatePerMSec * (timestamp - snd.rampStartTime);
@@ -2050,7 +2113,7 @@ void Runtime::updateSounds(uint32 timestamp) {
snd.rampRatePerMSec = 0;
newVolume = snd.rampEndVolume;
if (snd.rampTerminateOnCompletion)
- condemnedSounds.push_back(sndIndex);
+ stopSound(snd);
} else {
uint rampedVolume = (snd.rampStartVolume * (65536u - ramp)) + (snd.rampEndVolume * ramp);
newVolume = rampedVolume >> 16;
@@ -2059,27 +2122,26 @@ void Runtime::updateSounds(uint32 timestamp) {
if (snd.volume != newVolume) {
snd.volume = newVolume;
- if (snd.player) {
- computeEffectiveVolumeAndBalance(snd);
- snd.player->setVolumeAndBalance(snd.effectiveVolume, snd.effectiveBalance);
+ if (snd.cache) {
+ SoundCache *cache = snd.cache.get();
+ if (cache->player) {
+ computeEffectiveVolumeAndBalance(snd);
+ cache->player->setVolumeAndBalance(snd.effectiveVolume, snd.effectiveBalance);
+ }
}
}
}
- }
- // condemnedSounds must be sorted
- if (condemnedSounds.size()) {
- uint numDeleted = 0;
- for (uint index : condemnedSounds) {
- _activeSounds.remove_at(index - numDeleted);
- numDeleted++;
+ if (snd.evictTime && snd.evictTime <= timestamp) {
+ snd.cache.reset();
+ snd.evictTime = 0;
}
}
}
void Runtime::update3DSounds() {
- for (const Common::SharedPtr<CachedSound> &sndPtr : _activeSounds) {
- CachedSound &snd = *sndPtr;
+ for (const Common::SharedPtr<SoundInstance> &sndPtr : _activeSounds) {
+ SoundInstance &snd = *sndPtr;
if (!snd.is3D)
continue;
@@ -2087,14 +2149,19 @@ void Runtime::update3DSounds() {
bool changed = computeEffectiveVolumeAndBalance(snd);
if (changed) {
- VCruise::AudioPlayer *player = snd.player.get();
- if (player)
- player->setVolumeAndBalance(snd.effectiveVolume, snd.effectiveBalance);
+ if (snd.cache) {
+ SoundCache *cache = snd.cache.get();
+ if (cache) {
+ VCruise::AudioPlayer *player = cache->player.get();
+ if (player)
+ player->setVolumeAndBalance(snd.effectiveVolume, snd.effectiveBalance);
+ }
+ }
}
}
}
-bool Runtime::computeEffectiveVolumeAndBalance(CachedSound &snd) {
+bool Runtime::computeEffectiveVolumeAndBalance(SoundInstance &snd) {
uint effectiveVolume = snd.volume;
int32 effectiveBalance = snd.balance;
@@ -3194,7 +3261,7 @@ void Runtime::scriptOpSoundS1(ScriptArg_t arg) {
TAKE_STACK_STR_NAMED(1, sndNameArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
@@ -3206,7 +3273,7 @@ void Runtime::scriptOpSoundS2(ScriptArg_t arg) {
TAKE_STACK_STR_NAMED(1, sndNameArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
@@ -3218,7 +3285,7 @@ void Runtime::scriptOpSoundS3(ScriptArg_t arg) {
TAKE_STACK_STR_NAMED(1, sndNameArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
@@ -3229,7 +3296,7 @@ void Runtime::scriptOpSoundL1(ScriptArg_t arg) {
TAKE_STACK_STR_NAMED(1, sndNameArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
@@ -3241,7 +3308,7 @@ void Runtime::scriptOpSoundL2(ScriptArg_t arg) {
TAKE_STACK_STR_NAMED(1, sndNameArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
@@ -3253,7 +3320,7 @@ void Runtime::scriptOpSoundL3(ScriptArg_t arg) {
TAKE_STACK_STR_NAMED(1, sndNameArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
@@ -3265,7 +3332,7 @@ void Runtime::scriptOp3DSoundL2(ScriptArg_t arg) {
TAKE_STACK_STR_NAMED(1, sndNameArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound) {
@@ -3279,7 +3346,7 @@ void Runtime::scriptOp3DSoundL3(ScriptArg_t arg) {
TAKE_STACK_STR_NAMED(1, sndNameArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound) {
@@ -3293,7 +3360,7 @@ void Runtime::scriptOp3DSoundS2(ScriptArg_t arg) {
TAKE_STACK_STR_NAMED(1, sndNameArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound) {
@@ -3460,7 +3527,7 @@ void Runtime::scriptOpVolumeUp3(ScriptArg_t arg) {
TAKE_STACK_VAR_NAMED(1, sndIDArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByNameOrID(sndIDArgs[0], soundID, cachedSound);
if (cachedSound)
@@ -3472,7 +3539,7 @@ void Runtime::scriptOpVolumeDn2(ScriptArg_t arg) {
TAKE_STACK_VAR_NAMED(1, sndIDArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByNameOrID(sndIDArgs[0], soundID, cachedSound);
// FIXME: Just do this instantly
@@ -3485,7 +3552,7 @@ void Runtime::scriptOpVolumeDn3(ScriptArg_t arg) {
TAKE_STACK_VAR_NAMED(1, sndIDArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByNameOrID(sndIDArgs[0], soundID, cachedSound);
if (cachedSound)
@@ -3497,7 +3564,7 @@ void Runtime::scriptOpVolumeDn4(ScriptArg_t arg) {
TAKE_STACK_VAR_NAMED(1, sndIDArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByNameOrID(sndIDArgs[0], soundID, cachedSound);
if (cachedSound)
@@ -3539,7 +3606,7 @@ void Runtime::scriptOpSay1(ScriptArg_t arg) {
warning("Say1 cycles are not implemented yet, playing first sound in the cycle");
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
// uint ? = sndParamArgs[0];
@@ -3554,7 +3621,7 @@ void Runtime::scriptOpSay3(ScriptArg_t arg) {
TAKE_STACK_STR_NAMED(1, sndNameArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound) {
@@ -3578,7 +3645,7 @@ void Runtime::scriptOpSay3Get(ScriptArg_t arg) {
TAKE_STACK_STR_NAMED(1, sndNameArgs);
StackInt_t soundID = 0;
- CachedSound *cachedSound = nullptr;
+ SoundInstance *cachedSound = nullptr;
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound) {
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index 452fc691e86..c784cc45110 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -186,14 +186,20 @@ struct SoundParams3D {
uint unknownRange;
};
-struct CachedSound {
- CachedSound();
- ~CachedSound();
+struct SoundCache {
+ ~SoundCache();
- Common::String name;
Common::SharedPtr<Audio::SeekableAudioStream> stream;
Common::SharedPtr<Audio::AudioStream> loopingStream;
Common::SharedPtr<AudioPlayer> player;
+};
+
+struct SoundInstance {
+ SoundInstance();
+ ~SoundInstance();
+
+ Common::String name;
+ Common::SharedPtr<SoundCache> cache;
uint id;
@@ -215,6 +221,8 @@ struct CachedSound {
int32 z;
SoundParams3D params3D;
+
+ uint32 evictTime;
};
struct TriggeredOneShot {
@@ -509,9 +517,10 @@ private:
void loadIndex();
void findWaves();
- Common::SharedPtr<CachedSound> loadWave(const Common::String &soundName, uint soundID, const Common::ArchiveMemberPtr &archiveMemberPtr);
- void resolveSoundByName(const Common::String &soundName, StackInt_t &outSoundID, CachedSound *&outWave);
- void resolveSoundByNameOrID(const StackValue &stackValue, StackInt_t &outSoundID, CachedSound *&outWave);
+ Common::SharedPtr<SoundInstance> loadWave(const Common::String &soundName, uint soundID, const Common::ArchiveMemberPtr &archiveMemberPtr);
+ SoundCache *loadCache(SoundInstance &sound);
+ void resolveSoundByName(const Common::String &soundName, StackInt_t &outSoundID, SoundInstance *&outWave);
+ void resolveSoundByNameOrID(const StackValue &stackValue, StackInt_t &outSoundID, SoundInstance *&outWave);
void changeToScreen(uint roomNumber, uint screenNumber);
void returnToIdleState();
@@ -528,12 +537,13 @@ private:
void changeAnimation(const AnimationDef &animDef, uint initialFrame, bool consumeFPSOverride);
void changeAnimation(const AnimationDef &animDef, uint initialFrame, bool consumeFPSOverride, const Fraction &defaultFrameRate);
- void setSound3DParameters(CachedSound &sound, int32 x, int32 y, const SoundParams3D &soundParams3D);
- void triggerSound(bool looping, CachedSound &sound, uint volume, int32 balance, bool is3D);
- void triggerSoundRamp(CachedSound &sound, uint durationMSec, uint newVolume, bool terminateOnCompletion);
+ void setSound3DParameters(SoundInstance &sound, int32 x, int32 y, const SoundParams3D &soundParams3D);
+ void triggerSound(bool looping, SoundInstance &sound, uint volume, int32 balance, bool is3D);
+ void triggerSoundRamp(SoundInstance &sound, uint durationMSec, uint newVolume, bool terminateOnCompletion);
+ void stopSound(SoundInstance &sound);
void updateSounds(uint32 timestamp);
void update3DSounds();
- bool computeEffectiveVolumeAndBalance(CachedSound &snd);
+ bool computeEffectiveVolumeAndBalance(SoundInstance &snd);
AnimationDef stackArgsToAnimDef(const StackInt_t *args) const;
void pushAnimDef(const AnimationDef &animDef);
@@ -806,7 +816,7 @@ private:
Common::Array<OSEvent> _pendingEvents;
Common::HashMap<Common::String, Common::ArchiveMemberPtr> _waves;
- Common::Array<Common::SharedPtr<CachedSound> > _activeSounds;
+ Common::Array<Common::SharedPtr<SoundInstance> > _activeSounds;
SoundParams3D _pendingSoundParams3D;
Common::Array<TriggeredOneShot> _triggeredOneShots;
@@ -829,6 +839,11 @@ private:
static const uint kSaveGameIdentifier = 0x53566372;
static const uint kSaveGameCurrentVersion = 2;
static const uint kSaveGameEarliestSupportedVersion = 2;
+
+ static const uint kSoundCacheSize = 16;
+
+ Common::Pair<Common::String, Common::SharedPtr<SoundCache> > _soundCache[kSoundCacheSize];
+ uint _soundCacheIndex;
};
} // End of namespace VCruise
Commit: c3154d5299a0cc2fab4191bba1a263e843a607c8
https://github.com/scummvm/scummvm/commit/c3154d5299a0cc2fab4191bba1a263e843a607c8
Author: elasota (ejlasota at gmail.com)
Date: 2023-04-01T12:01:48-04:00
Commit Message:
VCRUISE: Keep track of loop state, evict ended sounds.
Changed paths:
engines/vcruise/runtime.cpp
engines/vcruise/runtime.h
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index d8bbe3fb444..fd5bbe1248a 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -379,7 +379,7 @@ SoundCache::~SoundCache() {
SoundInstance::SoundInstance()
: id(0), rampStartVolume(0), rampEndVolume(0), rampRatePerMSec(0), rampStartTime(0), rampTerminateOnCompletion(false),
- volume(0), balance(0), effectiveBalance(0), effectiveVolume(0), is3D(false), x(0), y(0), z(0), evictTime(0) {
+ volume(0), balance(0), effectiveBalance(0), effectiveVolume(0), is3D(false), isLooping(false), x(0), y(0), z(0), endTime(0) {
}
SoundInstance::~SoundInstance() {
@@ -2040,6 +2040,7 @@ void Runtime::triggerSound(bool looping, SoundInstance &snd, uint volume, int32
snd.volume = volume;
snd.balance = balance;
snd.is3D = is3D;
+ snd.isLooping = looping;
computeEffectiveVolumeAndBalance(snd);
@@ -2075,9 +2076,9 @@ void Runtime::triggerSound(bool looping, SoundInstance &snd, uint volume, int32
}
if (looping)
- snd.evictTime = 0;
+ snd.endTime = 0;
else
- snd.evictTime = g_system->getMillis(true) + static_cast<uint32>(cache->stream->getLength().msecs()) + 20000u;
+ snd.endTime = g_system->getMillis(true) + static_cast<uint32>(cache->stream->getLength().msecs()) + 1000u;
}
void Runtime::triggerSoundRamp(SoundInstance &snd, uint durationMSec, uint newVolume, bool terminateOnCompletion) {
@@ -2099,7 +2100,7 @@ void Runtime::stopSound(SoundInstance &sound) {
sound.cache->player->stop();
sound.cache.reset();
- sound.evictTime = g_system->getMillis(true) + 20000u;
+ sound.endTime = 0;
}
void Runtime::updateSounds(uint32 timestamp) {
@@ -2132,9 +2133,10 @@ void Runtime::updateSounds(uint32 timestamp) {
}
}
- if (snd.evictTime && snd.evictTime <= timestamp) {
+ // Cache-evict stopped sounds
+ if (snd.endTime && snd.endTime <= timestamp) {
snd.cache.reset();
- snd.evictTime = 0;
+ snd.endTime = 0;
}
}
}
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index c784cc45110..7bde55c90d5 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -216,13 +216,14 @@ struct SoundInstance {
int32 effectiveBalance;
bool is3D;
+ bool isLooping;
int32 x;
int32 y;
int32 z;
SoundParams3D params3D;
- uint32 evictTime;
+ uint32 endTime;
};
struct TriggeredOneShot {
Commit: 7f549ce5c52572acc2a36dde2b50e7797dcb7a10
https://github.com/scummvm/scummvm/commit/7f549ce5c52572acc2a36dde2b50e7797dcb7a10
Author: elasota (ejlasota at gmail.com)
Date: 2023-04-01T12:01:48-04:00
Commit Message:
VCRUISE: Hook up volume controls.
Changed paths:
engines/vcruise/audio_player.cpp
engines/vcruise/audio_player.h
engines/vcruise/runtime.cpp
engines/vcruise/runtime.h
diff --git a/engines/vcruise/audio_player.cpp b/engines/vcruise/audio_player.cpp
index abe704aa929..65957de5e5f 100644
--- a/engines/vcruise/audio_player.cpp
+++ b/engines/vcruise/audio_player.cpp
@@ -23,8 +23,8 @@
namespace VCruise {
-AudioPlayer::AudioPlayer(Audio::Mixer *mixer, const Common::SharedPtr<Audio::AudioStream> &baseStream)
- : _exhausted(false), _isPlaying(false), _mixer(mixer), _baseStream(baseStream) {
+AudioPlayer::AudioPlayer(Audio::Mixer *mixer, const Common::SharedPtr<Audio::AudioStream> &baseStream, Audio::Mixer::SoundType soundType)
+ : _exhausted(false), _isPlaying(false), _mixer(mixer), _baseStream(baseStream), _soundType(soundType) {
}
AudioPlayer::~AudioPlayer() {
@@ -62,7 +62,7 @@ void AudioPlayer::play(byte volume, int8 balance) {
if (!_isPlaying) {
_isPlaying = true;
_exhausted = false;
- _mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, this, -1, volume, balance, DisposeAfterUse::NO);
+ _mixer->playStream(_soundType, &_handle, this, -1, volume, balance, DisposeAfterUse::NO);
}
}
diff --git a/engines/vcruise/audio_player.h b/engines/vcruise/audio_player.h
index feac26907e1..a7115b29d93 100644
--- a/engines/vcruise/audio_player.h
+++ b/engines/vcruise/audio_player.h
@@ -34,7 +34,7 @@ class CachedAudio;
class AudioPlayer : public Audio::AudioStream {
public:
- AudioPlayer(Audio::Mixer *mixer, const Common::SharedPtr<Audio::AudioStream> &baseStream);
+ AudioPlayer(Audio::Mixer *mixer, const Common::SharedPtr<Audio::AudioStream> &baseStream, Audio::Mixer::SoundType soundType);
~AudioPlayer();
int readBuffer(int16 *buffer, const int numSamples) override;
@@ -58,6 +58,8 @@ private:
bool _exhausted;
Audio::Mixer *_mixer;
Common::SharedPtr<Audio::AudioStream> _baseStream;
+
+ Audio::Mixer::SoundType _soundType;
};
} // End of namespace VCruise
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index fd5bbe1248a..f5af7e25feb 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -275,7 +275,7 @@ void SfxData::load(Common::SeekableReadStream &stream, Audio::Mixer *mixer) {
sample->memoryStream.reset(new Common::MemoryReadStream(&sample->soundData[0], static_cast<uint32>(size)));
sample->audioStream.reset(Audio::makeWAVStream(sample->memoryStream.get(), DisposeAfterUse::NO));
- sample->audioPlayer.reset(new AudioPlayer(mixer, sample->audioStream));
+ sample->audioPlayer.reset(new AudioPlayer(mixer, sample->audioStream, Audio::Mixer::kSFXSoundType));
this->sounds[keyValue.key] = sample;
}
@@ -379,7 +379,7 @@ SoundCache::~SoundCache() {
SoundInstance::SoundInstance()
: id(0), rampStartVolume(0), rampEndVolume(0), rampRatePerMSec(0), rampStartTime(0), rampTerminateOnCompletion(false),
- volume(0), balance(0), effectiveBalance(0), effectiveVolume(0), is3D(false), isLooping(false), x(0), y(0), z(0), endTime(0) {
+ volume(0), balance(0), effectiveBalance(0), effectiveVolume(0), is3D(false), isLooping(false), isSpeech(false), x(0), y(0), z(0), endTime(0) {
}
SoundInstance::~SoundInstance() {
@@ -1924,7 +1924,7 @@ void Runtime::changeMusicTrack(int track) {
if (Audio::SeekableAudioStream *audioStream = Audio::makeWAVStream(wavFile, DisposeAfterUse::YES)) {
Common::SharedPtr<Audio::AudioStream> loopingStream(Audio::makeLoopingAudioStream(audioStream, 0));
- _musicPlayer.reset(new AudioPlayer(_mixer, loopingStream));
+ _musicPlayer.reset(new AudioPlayer(_mixer, loopingStream, Audio::Mixer::kMusicSoundType));
_musicPlayer->play(100, 0);
}
} else {
@@ -2036,11 +2036,12 @@ void Runtime::setSound3DParameters(SoundInstance &snd, int32 x, int32 y, const S
snd.params3D = soundParams3D;
}
-void Runtime::triggerSound(bool looping, SoundInstance &snd, uint volume, int32 balance, bool is3D) {
+void Runtime::triggerSound(bool looping, SoundInstance &snd, uint volume, int32 balance, bool is3D, bool isSpeech) {
snd.volume = volume;
snd.balance = balance;
snd.is3D = is3D;
snd.isLooping = looping;
+ snd.isSpeech = isSpeech;
computeEffectiveVolumeAndBalance(snd);
@@ -2060,6 +2061,8 @@ void Runtime::triggerSound(bool looping, SoundInstance &snd, uint volume, int32
if (looping && !cache->loopingStream)
cache->loopingStream.reset(new Audio::LoopingAudioStream(cache->stream.get(), 0, DisposeAfterUse::NO, true));
+ const Audio::Mixer::SoundType soundType = (isSpeech ? Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType);
+
if (cache->player) {
// If there is already a player and this is non-looping, start over
if (!looping) {
@@ -2071,7 +2074,7 @@ void Runtime::triggerSound(bool looping, SoundInstance &snd, uint volume, int32
cache->player->setVolumeAndBalance(snd.effectiveVolume, snd.effectiveBalance);
}
} else {
- cache->player.reset(new AudioPlayer(_mixer, looping ? cache->loopingStream.staticCast<Audio::AudioStream>() : cache->stream.staticCast<Audio::AudioStream>()));
+ cache->player.reset(new AudioPlayer(_mixer, looping ? cache->loopingStream.staticCast<Audio::AudioStream>() : cache->stream.staticCast<Audio::AudioStream>(), soundType));
cache->player->play(snd.effectiveVolume, snd.effectiveBalance);
}
@@ -3267,7 +3270,7 @@ void Runtime::scriptOpSoundS1(ScriptArg_t arg) {
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
- triggerSound(false, *cachedSound, 100, 0, false);
+ triggerSound(false, *cachedSound, 100, 0, false, false);
}
void Runtime::scriptOpSoundS2(ScriptArg_t arg) {
@@ -3279,7 +3282,7 @@ void Runtime::scriptOpSoundS2(ScriptArg_t arg) {
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
- triggerSound(false, *cachedSound, sndParamArgs[0], 0, false);
+ triggerSound(false, *cachedSound, sndParamArgs[0], 0, false, false);
}
void Runtime::scriptOpSoundS3(ScriptArg_t arg) {
@@ -3291,7 +3294,7 @@ void Runtime::scriptOpSoundS3(ScriptArg_t arg) {
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
- triggerSound(false, *cachedSound, sndParamArgs[0], sndParamArgs[1], false);
+ triggerSound(false, *cachedSound, sndParamArgs[0], sndParamArgs[1], false, false);
}
void Runtime::scriptOpSoundL1(ScriptArg_t arg) {
@@ -3302,7 +3305,7 @@ void Runtime::scriptOpSoundL1(ScriptArg_t arg) {
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
- triggerSound(true, *cachedSound, 100, 0, false);
+ triggerSound(true, *cachedSound, 100, 0, false, false);
}
void Runtime::scriptOpSoundL2(ScriptArg_t arg) {
@@ -3314,7 +3317,7 @@ void Runtime::scriptOpSoundL2(ScriptArg_t arg) {
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
- triggerSound(true, *cachedSound, sndParamArgs[0], 0, false);
+ triggerSound(true, *cachedSound, sndParamArgs[0], 0, false, false);
}
void Runtime::scriptOpSoundL3(ScriptArg_t arg) {
@@ -3326,7 +3329,7 @@ void Runtime::scriptOpSoundL3(ScriptArg_t arg) {
resolveSoundByName(sndNameArgs[0], soundID, cachedSound);
if (cachedSound)
- triggerSound(true, *cachedSound, sndParamArgs[0], sndParamArgs[1], false);
+ triggerSound(true, *cachedSound, sndParamArgs[0], sndParamArgs[1], false, false);
}
void Runtime::scriptOp3DSoundL2(ScriptArg_t arg) {
@@ -3339,7 +3342,7 @@ void Runtime::scriptOp3DSoundL2(ScriptArg_t arg) {
if (cachedSound) {
setSound3DParameters(*cachedSound, sndParamArgs[1], sndParamArgs[2], _pendingSoundParams3D);
- triggerSound(true, *cachedSound, sndParamArgs[0], 0, true);
+ triggerSound(true, *cachedSound, sndParamArgs[0], 0, true, false);
}
}
@@ -3353,7 +3356,7 @@ void Runtime::scriptOp3DSoundL3(ScriptArg_t arg) {
if (cachedSound) {
setSound3DParameters(*cachedSound, sndParamArgs[2], sndParamArgs[3], _pendingSoundParams3D);
- triggerSound(true, *cachedSound, sndParamArgs[0], sndParamArgs[1], true);
+ triggerSound(true, *cachedSound, sndParamArgs[0], sndParamArgs[1], true, false);
}
}
@@ -3367,7 +3370,7 @@ void Runtime::scriptOp3DSoundS2(ScriptArg_t arg) {
if (cachedSound) {
setSound3DParameters(*cachedSound, sndParamArgs[1], sndParamArgs[2], _pendingSoundParams3D);
- triggerSound(false, *cachedSound, sndParamArgs[0], 0, true);
+ triggerSound(false, *cachedSound, sndParamArgs[0], 0, true, false);
}
}
@@ -3615,7 +3618,7 @@ void Runtime::scriptOpSay1(ScriptArg_t arg) {
// uint cycleLength = sndParamArgs[1];
if (cachedSound)
- triggerSound(false, *cachedSound, 100, 0, false);
+ triggerSound(false, *cachedSound, 100, 0, false, true);
}
void Runtime::scriptOpSay3(ScriptArg_t arg) {
@@ -3636,7 +3639,7 @@ void Runtime::scriptOpSay3(ScriptArg_t arg) {
error("Invalid interrupt arg for say3, only 1 is supported.");
if (Common::find(_triggeredOneShots.begin(), _triggeredOneShots.end(), oneShot) == _triggeredOneShots.end()) {
- triggerSound(false, *cachedSound, 100, 0, false);
+ triggerSound(false, *cachedSound, 100, 0, false, true);
_triggeredOneShots.push_back(oneShot);
}
}
@@ -3660,7 +3663,7 @@ void Runtime::scriptOpSay3Get(ScriptArg_t arg) {
error("Invalid interrupt arg for say3, only 1 is supported.");
if (Common::find(_triggeredOneShots.begin(), _triggeredOneShots.end(), oneShot) == _triggeredOneShots.end()) {
- triggerSound(false, *cachedSound, 100, 0, false);
+ triggerSound(false, *cachedSound, 100, 0, false, true);
_triggeredOneShots.push_back(oneShot);
_scriptStack.push_back(StackValue(soundID));
} else
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index 7bde55c90d5..67cfc8ec66d 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -217,6 +217,7 @@ struct SoundInstance {
bool is3D;
bool isLooping;
+ bool isSpeech;
int32 x;
int32 y;
int32 z;
@@ -539,7 +540,7 @@ private:
void changeAnimation(const AnimationDef &animDef, uint initialFrame, bool consumeFPSOverride, const Fraction &defaultFrameRate);
void setSound3DParameters(SoundInstance &sound, int32 x, int32 y, const SoundParams3D &soundParams3D);
- void triggerSound(bool looping, SoundInstance &sound, uint volume, int32 balance, bool is3D);
+ void triggerSound(bool looping, SoundInstance &sound, uint volume, int32 balance, bool is3D, bool isSpeech);
void triggerSoundRamp(SoundInstance &sound, uint durationMSec, uint newVolume, bool terminateOnCompletion);
void stopSound(SoundInstance &sound);
void updateSounds(uint32 timestamp);
Commit: 3bb0094d7d2269152c031f8ea462fc262bab5ffb
https://github.com/scummvm/scummvm/commit/3bb0094d7d2269152c031f8ea462fc262bab5ffb
Author: elasota (ejlasota at gmail.com)
Date: 2023-04-01T12:01:48-04:00
Commit Message:
VCRUISE: Save format rework
Changed paths:
engines/vcruise/runtime.cpp
engines/vcruise/runtime.h
engines/vcruise/vcruise.cpp
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index f5af7e25feb..cd0658585dc 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -379,7 +379,7 @@ SoundCache::~SoundCache() {
SoundInstance::SoundInstance()
: id(0), rampStartVolume(0), rampEndVolume(0), rampRatePerMSec(0), rampStartTime(0), rampTerminateOnCompletion(false),
- volume(0), balance(0), effectiveBalance(0), effectiveVolume(0), is3D(false), isLooping(false), isSpeech(false), x(0), y(0), z(0), endTime(0) {
+ volume(0), balance(0), effectiveBalance(0), effectiveVolume(0), is3D(false), isLooping(false), isSpeech(false), x(0), y(0), endTime(0) {
}
SoundInstance::~SoundInstance() {
@@ -396,9 +396,31 @@ bool TriggeredOneShot::operator!=(const TriggeredOneShot &other) const {
return !((*this) == other);
}
+void TriggeredOneShot::write(Common::WriteStream *stream) const {
+ stream->writeUint32BE(soundID);
+ stream->writeUint32BE(uniqueSlot);
+}
+
+void TriggeredOneShot::read(Common::ReadStream *stream) {
+ soundID = stream->readUint32BE();
+ uniqueSlot = stream->readUint32BE();
+}
+
StaticAnimParams::StaticAnimParams() : initialDelay(0), repeatDelay(0), lockInteractions(false) {
}
+void StaticAnimParams::write(Common::WriteStream *stream) const {
+ stream->writeUint32BE(initialDelay);
+ stream->writeUint32BE(repeatDelay);
+ stream->writeByte(lockInteractions ? 1 : 0);
+}
+
+void StaticAnimParams::read(Common::ReadStream *stream) {
+ initialDelay = stream->readUint32BE();
+ repeatDelay = stream->readUint32BE();
+ lockInteractions = (stream->readByte() != 0);
+}
+
StaticAnimation::StaticAnimation() : currentAlternation(0), nextStartTime(0) {
}
@@ -411,6 +433,18 @@ FrameData2::FrameData2() : x(0), y(0), angle(0), frameNumberInArea(0), unknown(0
SoundParams3D::SoundParams3D() : minRange(0), maxRange(0), unknownRange(0) {
}
+void SoundParams3D::write(Common::WriteStream *stream) const {
+ stream->writeUint32BE(minRange);
+ stream->writeUint32BE(maxRange);
+ stream->writeUint32BE(unknownRange);
+}
+
+void SoundParams3D::read(Common::ReadStream *stream) {
+ minRange = stream->readUint32BE();
+ maxRange = stream->readUint32BE();
+ unknownRange = stream->readUint32BE();
+}
+
InventoryItem::InventoryItem() : itemID(0), highlighted(false) {
}
@@ -420,10 +454,195 @@ Fraction::Fraction() : numerator(0), denominator(1) {
Fraction::Fraction(uint pNumerator, uint pDenominator) : numerator(pNumerator), denominator(pDenominator) {
}
+SaveGameSnapshot::InventoryItem::InventoryItem() : itemID(0), highlighted(false) {
+}
+
+void SaveGameSnapshot::InventoryItem::write(Common::WriteStream *stream) const {
+ stream->writeUint32BE(itemID);
+ stream->writeByte(highlighted ? 1 : 0);
+}
+
+void SaveGameSnapshot::InventoryItem::read(Common::ReadStream *stream) {
+ itemID = stream->readUint32BE();
+ highlighted = (stream->readByte() != 0);
+}
+
+SaveGameSnapshot::Sound::Sound() : id(0), volume(0), balance(0), is3D(false), isLooping(false), isSpeech(false), x(0), y(0) {
+}
+
+void SaveGameSnapshot::Sound::write(Common::WriteStream *stream) const {
+ stream->writeUint32BE(name.size());
+ stream->writeString(name);
+
+ stream->writeUint32BE(id);
+ stream->writeUint32BE(volume);
+ stream->writeSint32BE(balance);
+
+ stream->writeByte(is3D ? 1 : 0);
+ stream->writeByte(isLooping ? 1 : 0);
+ stream->writeByte(isSpeech ? 1 : 0);
+
+ stream->writeSint32BE(x);
+ stream->writeSint32BE(y);
+
+ params3D.write(stream);
+}
+
+void SaveGameSnapshot::Sound::read(Common::ReadStream *stream) {
+ uint nameLen = stream->readUint32BE();
+
+ if (stream->eos() || stream->err() || nameLen > 256)
+ nameLen = 0;
+
+ name = stream->readString(0, nameLen);
+
+ id = stream->readUint32BE();
+ volume = stream->readUint32BE();
+ balance = stream->readSint32BE();
+
+ is3D = (stream->readByte() != 0);
+ isLooping = (stream->readByte() != 0);
+ isSpeech = (stream->readByte() != 0);
+
+ x = stream->readSint32BE();
+ y = stream->readSint32BE();
+
+ params3D.read(stream);
+}
+
+SaveGameSnapshot::SaveGameSnapshot() : roomNumber(0), screenNumber(0), direction(0), escOn(false), musicTrack(0), loadedAnimation(0),
+ animDisplayingFrame(0), listenerX(0), listenerY(0), listenerAngle(0) {
+}
+
+void SaveGameSnapshot::write(Common::WriteStream *stream) const {
+ stream->writeUint32BE(kSaveGameIdentifier);
+ stream->writeUint32BE(kSaveGameCurrentVersion);
+
+ stream->writeUint32BE(roomNumber);
+ stream->writeUint32BE(screenNumber);
+ stream->writeUint32BE(direction);
+
+ stream->writeByte(escOn ? 1 : 0);
+ stream->writeSint32BE(musicTrack);
+
+ stream->writeUint32BE(loadedAnimation);
+ stream->writeUint32BE(animDisplayingFrame);
+
+ pendingStaticAnimParams.write(stream);
+ pendingSoundParams3D.write(stream);
+
+ stream->writeSint32BE(listenerX);
+ stream->writeSint32BE(listenerY);
+ stream->writeSint32BE(listenerAngle);
+
+ stream->writeUint32BE(inventory.size());
+ stream->writeUint32BE(sounds.size());
+ stream->writeUint32BE(triggeredOneShots.size());
+
+ stream->writeUint32BE(variables.size());
+ stream->writeUint32BE(timers.size());
+
+ for (const InventoryItem &invItem : inventory)
+ invItem.write(stream);
+
+ for (const Sound &sound : sounds)
+ sound.write(stream);
+
+ for (const TriggeredOneShot &triggeredOneShot : triggeredOneShots)
+ triggeredOneShot.write(stream);
+
+ for (const Common::HashMap<uint32, int32>::Node &var : variables) {
+ stream->writeUint32BE(var._key);
+ stream->writeSint32BE(var._value);
+ }
+
+ for (const Common::HashMap<uint, uint32>::Node &timer : timers) {
+ stream->writeUint32BE(timer._key);
+ stream->writeUint32BE(timer._value);
+ }
+}
+
+LoadGameOutcome SaveGameSnapshot::read(Common::ReadStream *stream) {
+ uint32 saveIdentifier = stream->readUint32BE();
+ uint32 saveVersion = stream->readUint32BE();
+
+ if (stream->eos() || stream->err())
+ return kLoadGameOutcomeMissingVersion;
+
+ if (saveIdentifier != kSaveGameIdentifier)
+ return kLoadGameOutcomeInvalidVersion;
+
+ if (saveVersion > kSaveGameCurrentVersion)
+ return kLoadGameOutcomeSaveIsTooNew;
+
+ if (saveVersion < kSaveGameEarliestSupportedVersion)
+ return kLoadGameOutcomeSaveIsTooOld;
+
+
+ roomNumber = stream->readUint32BE();
+ screenNumber = stream->readUint32BE();
+ direction = stream->readUint32BE();
+
+ escOn = (stream->readByte() != 0);
+ musicTrack = stream->readSint32BE();
+
+ loadedAnimation = stream->readUint32BE();
+ animDisplayingFrame = stream->readUint32BE();
+
+ pendingStaticAnimParams.read(stream);
+ pendingSoundParams3D.read(stream);
+
+ listenerX = stream->readSint32BE();
+ listenerY = stream->readSint32BE();
+ listenerAngle = stream->readSint32BE();
+
+ uint numInventory = stream->readUint32BE();
+ uint numSounds = stream->readUint32BE();
+ uint numOneShots = stream->readUint32BE();
+
+ uint numVars = stream->readUint32BE();
+ uint numTimers = stream->readUint32BE();
+
+ if (stream->eos() || stream->err())
+ return kLoadGameOutcomeSaveDataCorrupted;
+
+ inventory.resize(numInventory);
+ sounds.resize(numSounds);
+ triggeredOneShots.resize(numOneShots);
+
+ for (uint i = 0; i < numInventory; i++)
+ inventory[i].read(stream);
+
+ for (uint i = 0; i < numSounds; i++)
+ sounds[i].read(stream);
+
+ for (uint i = 0; i < numOneShots; i++)
+ triggeredOneShots[i].read(stream);
+
+ for (uint i = 0; i < numVars; i++) {
+ uint32 key = stream->readUint32BE();
+ int32 value = stream->readSint32BE();
+
+ variables[key] = value;
+ }
+
+ for (uint i = 0; i < numTimers; i++) {
+ uint32 key = stream->readUint32BE();
+ uint32 value = stream->readUint32BE();
+
+ timers[key] = value;
+ }
+
+ if (stream->eos() || stream->err())
+ return kLoadGameOutcomeSaveDataCorrupted;
+
+ return kLoadGameOutcomeSucceeded;
+}
+
Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &rootFSNode, VCruiseGameID gameID)
: _system(system), _mixer(mixer), _roomNumber(1), _screenNumber(0), _direction(0), _haveHorizPanAnimations(false), _loadedRoomNumber(0), _activeScreenNumber(0),
_gameState(kGameStateBoot), _gameID(gameID), _havePendingScreenChange(false), _forceScreenChange(false), _havePendingReturnToIdleState(false), _havePendingCompletionCheck(false),
- _scriptNextInstruction(0), _escOn(false), _debugMode(false), _fastAnimationMode(false), _panoramaDirectionFlags(0),
+ _scriptNextInstruction(0), _escOn(false), _debugMode(false), _fastAnimationMode(false), _musicTrack(0), _panoramaDirectionFlags(0),
_loadedAnimation(0), _animPendingDecodeFrame(0), _animDisplayingFrame(0), _animFirstFrame(0), _animLastFrame(0), _animStopFrame(0),
_animStartTime(0), _animFramesDecoded(0), _animDecoderState(kAnimDecoderStateStopped),
_animPlayWhileIdle(false), _idleIsOnInteraction(false), _idleHaveClickInteraction(false), _idleHaveDragInteraction(false), _idleInteractionID(0), _haveIdleStaticAnimation(false),
@@ -1633,6 +1852,8 @@ void Runtime::changeToScreen(uint roomNumber, uint screenNumber) {
_havePendingReturnToIdleState = true;
_haveIdleStaticAnimation = false;
+
+ recordSaveGameSnapshot();
}
}
@@ -1916,7 +2137,11 @@ void Runtime::loadFrameData2(Common::SeekableReadStream *stream) {
}
void Runtime::changeMusicTrack(int track) {
+ if (track == _musicTrack)
+ return;
+
_musicPlayer.reset();
+ _musicTrack = track;
Common::String wavFileName = Common::String::format("Sfx/Music-%02i.wav", static_cast<int>(track));
Common::File *wavFile = new Common::File();
@@ -2652,136 +2877,185 @@ void Runtime::onKeyDown(Common::KeyCode keyCode) {
}
bool Runtime::canSave() const {
- return _gameState == kGameStateIdle;
+ return !!_saveGame;
}
bool Runtime::canLoad() const {
return _gameState == kGameStateIdle;
}
-void Runtime::saveGame(Common::WriteStream *stream) const {
- stream->writeUint32BE(kSaveGameIdentifier);
- stream->writeUint32BE(kSaveGameCurrentVersion);
+void Runtime::recordSaveGameSnapshot() {
+ _saveGame.reset();
- stream->writeUint32BE(_roomNumber);
- stream->writeUint32BE(_screenNumber);
- stream->writeUint32BE(_direction);
+ uint32 timeBase = g_system->getMillis();
- Common::Array<uint32> variableIDs;
- Common::Array<uint> timerIDs;
+ Common::SharedPtr<SaveGameSnapshot> snapshot(new SaveGameSnapshot());
- uint32 timeBase = g_system->getMillis();
+ _saveGame = snapshot;
+
+ for (const InventoryItem &inventoryItem : _inventory) {
+ SaveGameSnapshot::InventoryItem saveItem;
+ saveItem.itemID = inventoryItem.itemID;
+ saveItem.highlighted = inventoryItem.highlighted;
+
+ snapshot->inventory.push_back(saveItem);
+ }
- for (const Common::HashMap<uint32, int32>::Node &varNode : _variables)
- variableIDs.push_back(varNode._key);
+ snapshot->roomNumber = _roomNumber;
+ snapshot->screenNumber = _screenNumber;
+ snapshot->direction = _direction;
+
+ snapshot->pendingStaticAnimParams = _pendingStaticAnimParams;
+
+ snapshot->variables = _variables;
for (const Common::HashMap<uint, uint32>::Node &timerNode : _timers)
- timerIDs.push_back(timerNode._key);
+ snapshot->timers[timerNode._key] = timerNode._value - timeBase;
- Common::sort(variableIDs.begin(), variableIDs.end());
- Common::sort(timerIDs.begin(), timerIDs.end());
+ snapshot->escOn = _escOn;
- stream->writeUint32BE(variableIDs.size());
- stream->writeUint32BE(timerIDs.size());
+ snapshot->musicTrack = _musicTrack;
- for (uint32 variableKey : variableIDs) {
- Common::HashMap<uint32, int32>::const_iterator it = _variables.find(variableKey);
- assert(it != _variables.end());
+ snapshot->loadedAnimation = _loadedAnimation;
+ snapshot->animDisplayingFrame = _animDisplayingFrame;
- stream->writeUint32BE(variableKey);
- stream->writeSint32BE(it->_value);
- }
+ for (const Common::SharedPtr<SoundInstance> &soundPtr : _activeSounds) {
+ const SoundInstance &sound = *soundPtr;
+
+ SaveGameSnapshot::Sound saveSound;
+ saveSound.name = sound.name;
+
+ saveSound.id = sound.id;
- for (uint timerKey : timerIDs) {
- Common::HashMap<uint, uint32>::const_iterator it = _timers.find(timerKey);
- assert(it != _timers.end());
+ saveSound.volume = sound.volume;
+ saveSound.balance = sound.balance;
- stream->writeUint32BE(timerKey);
- stream->writeUint32BE(it->_value - timeBase);
+ // Skip ramp
+ if (sound.rampRatePerMSec != 0)
+ saveSound.volume = sound.rampEndVolume;
+
+ saveSound.is3D = sound.is3D;
+ saveSound.isLooping = sound.isLooping;
+ saveSound.isSpeech = sound.isSpeech;
+ saveSound.x = sound.x;
+ saveSound.y = sound.y;
+
+ saveSound.params3D = sound.params3D;
+
+ snapshot->sounds.push_back(saveSound);
}
- for (const InventoryItem &item : _inventory)
- stream->writeUint32BE(item.itemID);
+ snapshot->pendingSoundParams3D = _pendingSoundParams3D;
+
+ snapshot->triggeredOneShots = _triggeredOneShots;
+
+ snapshot->listenerX = _listenerX;
+ snapshot->listenerY = _listenerY;
+ snapshot->listenerAngle = _listenerAngle;
}
-Runtime::LoadGameOutcome Runtime::loadGame(Common::ReadStream *stream) {
- assert(canLoad());
+void Runtime::restoreSaveGameSnapshot() {
+ uint32 timeBase = g_system->getMillis();
- uint32 saveGameID = stream->readUint32BE();
- uint32 saveVersion = stream->readUint32BE();
+ for (uint i = 0; i < kNumInventorySlots && i < _saveGame->inventory.size(); i++) {
+ const SaveGameSnapshot::InventoryItem &saveItem = _saveGame->inventory[i];
- if (stream->err() || stream->eos())
- return kLoadGameOutcomeMissingVersion;
+ _inventory[i].itemID = saveItem.itemID;
+ _inventory[i].highlighted = saveItem.highlighted;
- if (saveGameID != kSaveGameIdentifier)
- return kLoadGameOutcomeInvalidVersion;
+ if (saveItem.itemID) {
+ Common::String itemFileName = getFileNameForItemGraphic(saveItem.itemID);
+ _inventory[i].graphic = loadGraphic(itemFileName, false);
+ } else {
+ _inventory[i].graphic.reset();
+ }
+ }
- if (saveVersion > kSaveGameCurrentVersion)
- return kLoadGameOutcomeSaveIsTooNew;
+ _roomNumber = _saveGame->roomNumber;
+ _screenNumber = _saveGame->screenNumber;
+ _direction = _saveGame->direction;
- if (saveVersion < kSaveGameEarliestSupportedVersion)
- return kLoadGameOutcomeSaveIsTooOld;
+ _pendingStaticAnimParams = _saveGame->pendingStaticAnimParams;
- uint32 timeBase = g_system->getMillis();
+ _variables.clear();
+ _variables = _saveGame->variables;
- uint32 roomNumber = stream->readUint32BE();
- uint32 screenNumber = stream->readUint32BE();
- uint32 direction = stream->readUint32BE();
+ _timers.clear();
- uint32 numVars = stream->readUint32BE();
- uint32 numTimers = stream->readUint32BE();
+ for (const Common::HashMap<uint, uint32>::Node &timerNode : _saveGame->timers)
+ _timers[timerNode._key] = timerNode._value + timeBase;
- if (stream->err() || stream->eos())
- return kLoadGameOutcomeSaveDataCorrupted;
+ _escOn = _saveGame->escOn;
- Common::HashMap<uint32, int32> vars;
- Common::HashMap<uint, uint32> timers;
+ changeMusicTrack(_saveGame->musicTrack);
- for (uint32 i = 0; i < numVars; i++) {
- uint32 varID = stream->readUint32BE();
- int32 varValue = stream->readSint32BE();
+ // Stop all sounds since the player instances are stored in the sound cache.
+ for (Common::SharedPtr<SoundInstance> &snd : _activeSounds)
+ stopSound(*snd);
- vars[varID] = varValue;
- }
+ _activeSounds.clear();
+
+ _pendingSoundParams3D = _saveGame->pendingSoundParams3D;
+
+ _triggeredOneShots = _saveGame->triggeredOneShots;
- for (uint32 i = 0; i < numTimers; i++) {
- uint timerID = stream->readUint32BE();
- uint32 timerValue = stream->readUint32BE();
+ _listenerX = _saveGame->listenerX;
+ _listenerY = _saveGame->listenerY;
+ _listenerAngle = _saveGame->listenerAngle;
- timers[timerID] = timerValue + timeBase;
+ for (const SaveGameSnapshot::Sound &sound : _saveGame->sounds) {
+ Common::SharedPtr<SoundInstance> si(new SoundInstance());
+
+ si->name = sound.name;
+ si->id = sound.id;
+ si->volume = sound.volume;
+ si->balance = sound.balance;
+ si->is3D = sound.is3D;
+ si->isLooping = sound.isLooping;
+ si->isSpeech = sound.isSpeech;
+ si->x = sound.x;
+ si->y = sound.y;
+ si->params3D = sound.params3D;
+
+ _activeSounds.push_back(si);
+
+ if (sound.isLooping)
+ triggerSound(true, *si, si->volume, si->balance, si->is3D, si->isSpeech);
}
- uint inventoryItems[kNumInventorySlots];
+ uint anim = _saveGame->loadedAnimation;
+ uint frame = _saveGame->animDisplayingFrame;
- for (uint i = 0; i < kNumInventorySlots; i++)
- inventoryItems[i] = stream->readUint32BE();
+ AnimationDef animDef;
+ animDef.animNum = anim;
+ animDef.firstFrame = frame;
+ animDef.lastFrame = frame;
- if (stream->err() || stream->eos())
- return kLoadGameOutcomeSaveDataCorrupted;
+ changeAnimation(animDef, false);
- if (direction >= kNumDirections)
- return kLoadGameOutcomeSaveDataCorrupted;
+ _gameState = kGameStateWaitingForAnimation;
+
+ _havePendingScreenChange = true;
+ _forceScreenChange = true;
+}
- // Load succeeded
- _variables = Common::move(vars);
- _timers = Common::move(timers);
+void Runtime::saveGame(Common::WriteStream *stream) const {
+ _saveGame->write(stream);
+}
- for (uint i = 0; i < kNumInventorySlots; i++) {
- _inventory[i].itemID = inventoryItems[i];
- _inventory[i].highlighted = false;
- _inventory[i].graphic.reset();
+LoadGameOutcome Runtime::loadGame(Common::ReadStream *stream) {
+ assert(canLoad());
- if (inventoryItems[i] != 0)
- _inventory[i].graphic = loadGraphic(getFileNameForItemGraphic(inventoryItems[i]), false);
+ Common::SharedPtr<SaveGameSnapshot> snapshot(new SaveGameSnapshot());
+ LoadGameOutcome outcome = snapshot->read(stream);
- drawInventory(i);
- }
+ if (outcome != kLoadGameOutcomeSucceeded)
+ return outcome;
- _direction = direction;
- changeToScreen(roomNumber, screenNumber);
- _havePendingReturnToIdleState = true;
+ _saveGame = snapshot;
+ restoreSaveGameSnapshot();
- return kLoadGameOutcomeSucceeded;
+ return outcome;
}
#ifdef PEEK_STACK
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index 67cfc8ec66d..4c8c908edc4 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -184,6 +184,9 @@ struct SoundParams3D {
// Not sure what this does. It's always shorter than the min range but after many tests, I've been
// unable to detect any level changes from altering this parameter.
uint unknownRange;
+
+ void write(Common::WriteStream *stream) const;
+ void read(Common::ReadStream *stream);
};
struct SoundCache {
@@ -220,7 +223,6 @@ struct SoundInstance {
bool isSpeech;
int32 x;
int32 y;
- int32 z;
SoundParams3D params3D;
@@ -235,6 +237,9 @@ struct TriggeredOneShot {
uint soundID;
uint uniqueSlot;
+
+ void write(Common::WriteStream *stream) const;
+ void read(Common::ReadStream *stream);
};
struct StaticAnimParams {
@@ -243,6 +248,9 @@ struct StaticAnimParams {
uint initialDelay;
uint repeatDelay;
bool lockInteractions;
+
+ void write(Common::WriteStream *stream) const;
+ void read(Common::ReadStream *stream);
};
struct StaticAnimation {
@@ -291,19 +299,85 @@ struct Fraction {
uint denominator;
};
-class Runtime {
-public:
- enum LoadGameOutcome {
- kLoadGameOutcomeSucceeded,
+enum LoadGameOutcome {
+ kLoadGameOutcomeSucceeded,
+
+ kLoadGameOutcomeSaveDataCorrupted,
+
+ kLoadGameOutcomeMissingVersion,
+ kLoadGameOutcomeInvalidVersion,
+ kLoadGameOutcomeSaveIsTooNew,
+ kLoadGameOutcomeSaveIsTooOld,
+};
+
+struct SaveGameSnapshot {
+ SaveGameSnapshot();
+
+ void write(Common::WriteStream *stream) const;
+ LoadGameOutcome read(Common::ReadStream *stream);
+
+ static const uint kSaveGameIdentifier = 0x53566372;
+ static const uint kSaveGameCurrentVersion = 2;
+ static const uint kSaveGameEarliestSupportedVersion = 2;
+
+ struct InventoryItem {
+ InventoryItem();
+
+ uint itemID;
+ bool highlighted;
+
+ void write(Common::WriteStream *stream) const;
+ void read(Common::ReadStream *stream);
+ };
+
+ struct Sound {
+ Sound();
- kLoadGameOutcomeSaveDataCorrupted,
+ Common::String name;
+ uint id;
+ uint volume;
+ int32 balance;
- kLoadGameOutcomeMissingVersion,
- kLoadGameOutcomeInvalidVersion,
- kLoadGameOutcomeSaveIsTooNew,
- kLoadGameOutcomeSaveIsTooOld,
+ bool is3D;
+ bool isLooping;
+ bool isSpeech;
+
+ int32 x;
+ int32 y;
+
+ SoundParams3D params3D;
+
+ void write(Common::WriteStream *stream) const;
+ void read(Common::ReadStream *stream);
};
+ uint roomNumber;
+ uint screenNumber;
+ uint direction;
+
+ bool escOn;
+ int musicTrack;
+
+ uint loadedAnimation;
+ uint animDisplayingFrame;
+
+ StaticAnimParams pendingStaticAnimParams;
+ SoundParams3D pendingSoundParams3D;
+
+ int32 listenerX;
+ int32 listenerY;
+ int32 listenerAngle;
+
+ Common::Array<InventoryItem> inventory;
+ Common::Array<Sound> sounds;
+ Common::Array<TriggeredOneShot> triggeredOneShots;
+
+ Common::HashMap<uint32, int32> variables;
+ Common::HashMap<uint, uint32> timers;
+};
+
+class Runtime {
+public:
Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &rootFSNode, VCruiseGameID gameID);
virtual ~Runtime();
@@ -324,6 +398,9 @@ public:
bool canSave() const;
bool canLoad() const;
+ void recordSaveGameSnapshot();
+ void restoreSaveGameSnapshot();
+
void saveGame(Common::WriteStream *stream) const;
LoadGameOutcome loadGame(Common::ReadStream *stream);
@@ -763,6 +840,7 @@ private:
Common::SharedPtr<Common::RandomSource> _rng;
Common::SharedPtr<AudioPlayer> _musicPlayer;
+ int _musicTrack;
SfxData _sfxData;
Common::SharedPtr<Video::AVIDecoder> _animDecoder;
@@ -838,14 +916,12 @@ private:
static const int kPanoramaPanningMarginX = 11;
static const int kPanoramaPanningMarginY = 11;
- static const uint kSaveGameIdentifier = 0x53566372;
- static const uint kSaveGameCurrentVersion = 2;
- static const uint kSaveGameEarliestSupportedVersion = 2;
-
static const uint kSoundCacheSize = 16;
Common::Pair<Common::String, Common::SharedPtr<SoundCache> > _soundCache[kSoundCacheSize];
uint _soundCacheIndex;
+
+ Common::SharedPtr<SaveGameSnapshot> _saveGame;
};
} // End of namespace VCruise
diff --git a/engines/vcruise/vcruise.cpp b/engines/vcruise/vcruise.cpp
index da4df120a48..4964e1ad39d 100644
--- a/engines/vcruise/vcruise.cpp
+++ b/engines/vcruise/vcruise.cpp
@@ -225,32 +225,32 @@ Common::Error VCruiseEngine::saveGameStream(Common::WriteStream *stream, bool is
}
Common::Error VCruiseEngine::loadGameStream(Common::SeekableReadStream *stream) {
- Runtime::LoadGameOutcome loadGameOutcome = _runtime->loadGame(stream);
+ LoadGameOutcome loadGameOutcome = _runtime->loadGame(stream);
switch (loadGameOutcome) {
- case Runtime::kLoadGameOutcomeSucceeded:
+ case kLoadGameOutcomeSucceeded:
return Common::Error(Common::kNoError);
- case Runtime::kLoadGameOutcomeSaveDataCorrupted: {
+ case kLoadGameOutcomeSaveDataCorrupted: {
GUI::MessageDialog dialog(_("Failed to load save, the save data appears to be damaged."));
dialog.runModal();
}
return Common::Error(Common::kReadingFailed);
- case Runtime::kLoadGameOutcomeMissingVersion: {
+ case kLoadGameOutcomeMissingVersion: {
GUI::MessageDialog dialog(_("Failed to read version information from save file."));
dialog.runModal();
}
return Common::Error(Common::kReadingFailed);
- case Runtime::kLoadGameOutcomeInvalidVersion: {
+ case kLoadGameOutcomeInvalidVersion: {
GUI::MessageDialog dialog(_("Failed to load save, the save file doesn't contain valid version information."));
dialog.runModal();
}
return Common::Error(Common::kReadingFailed);
- case Runtime::kLoadGameOutcomeSaveIsTooNew: {
+ case kLoadGameOutcomeSaveIsTooNew: {
GUI::MessageDialog dialog(_("Saved game was created with a newer version of ScummVM. Unable to load."));
dialog.runModal();
}
return Common::Error(Common::kReadingFailed);
- case Runtime::kLoadGameOutcomeSaveIsTooOld: {
+ case kLoadGameOutcomeSaveIsTooOld: {
GUI::MessageDialog dialog(_("Saved game was created with an earlier, incompatible version of ScummVM. Unable to load."));
dialog.runModal();
}
More information about the Scummvm-git-logs
mailing list