aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/sci/detection_tables.h65
-rw-r--r--engines/sci/engine/features.cpp74
-rw-r--r--engines/sci/engine/features.h44
-rw-r--r--engines/sci/engine/guest_additions.cpp866
-rw-r--r--engines/sci/engine/guest_additions.h245
-rw-r--r--engines/sci/engine/ksound.cpp18
-rw-r--r--engines/sci/engine/object.h4
-rw-r--r--engines/sci/engine/script_patches.cpp203
-rw-r--r--engines/sci/engine/selector.cpp14
-rw-r--r--engines/sci/engine/selector.h14
-rw-r--r--engines/sci/engine/state.cpp7
-rw-r--r--engines/sci/engine/state.h1
-rw-r--r--engines/sci/engine/vm.cpp41
-rw-r--r--engines/sci/engine/vm.h20
-rw-r--r--engines/sci/module.mk1
-rw-r--r--engines/sci/sci.cpp160
-rw-r--r--engines/sci/sci.h24
-rw-r--r--engines/sci/sound/audio32.cpp14
-rw-r--r--engines/sci/sound/audio32.h7
-rw-r--r--engines/sci/sound/soundcmd.cpp131
-rw-r--r--engines/sci/sound/soundcmd.h51
21 files changed, 1677 insertions, 327 deletions
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index 02ec9aa55b..c37dd4fffb 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -724,7 +724,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
#define GUIO_GK1_FLOPPY GUIO2(GUIO_NOSPEECH, \
GAMEOPTION_ORIGINAL_SAVELOAD)
-#define GUIO_GK1_CD GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, \
+#define GUIO_GK1_CD GUIO3(GUIO_LINKSPEECHTOSFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
GAMEOPTION_HIGH_RESOLUTION_GRAPHICS)
#define GUIO_GK1_MAC GUIO_GK1_FLOPPY
@@ -840,13 +841,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#undef GUIO_GK1_CD
#undef GUIO_GK1_MAC
-#define GUIO_GK2_DEMO GUIO6(GUIO_NOSUBTITLES, \
+#define GUIO_GK2_DEMO GUIO7(GUIO_NOSUBTITLES, \
GUIO_NOMUSIC, \
+ GUIO_NOSFX, \
GUIO_NOSPEECH, \
GUIO_NOMIDI, \
GUIO_NOLAUNCHLOAD, \
GUIO_NOASPECT)
-#define GUIO_GK2 GUIO5(GUIO_NOSUBTITLES, \
+#define GUIO_GK2 GUIO7(GUIO_NOSUBTITLES, \
+ GUIO_NOSFX, \
+ GUIO_NOSPEECHVOLUME, \
GUIO_NOMIDI, \
GUIO_NOASPECT, \
GAMEOPTION_ORIGINAL_SAVELOAD, \
@@ -1134,8 +1138,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
-#define GUIO_HOYLE5 GUIO3(GUIO_NOMIDI, \
+#define GUIO_HOYLE5 GUIO5(GUIO_NOMIDI, \
GUIO_NOLAUNCHLOAD, \
+ GUIO_LINKMUSICTOSFX, \
+ GUIO_LINKSPEECHTOSFX, \
GUIO_NOASPECT)
// Hoyle 5 (Hoyle Classic Games) - Windows demo
@@ -1803,11 +1809,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
-#define GUIO_KQ7_DEMO GUIO4(GUIO_NOSUBTITLES, \
- GUIO_NOSPEECH, \
+#define GUIO_KQ7_DEMO GUIO5(GUIO_NOSUBTITLES, \
GUIO_NOLAUNCHLOAD, \
+ GUIO_LINKMUSICTOSFX, \
+ GUIO_LINKSPEECHTOSFX, \
GUIO_NOASPECT)
-#define GUIO_KQ7 GUIO2(GUIO_NOASPECT, \
+#define GUIO_KQ7 GUIO4(GUIO_NOASPECT, \
+ GUIO_LINKMUSICTOSFX, \
+ GUIO_LINKSPEECHTOSFX, \
GAMEOPTION_ORIGINAL_SAVELOAD)
// King's Quest 7 - English Windows (from the King's Quest Collection)
@@ -2591,7 +2600,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
-#define GUIO_LSL6HIRES GUIO2(GUIO_NOASPECT, \
+#define GUIO_LSL6HIRES GUIO3(GUIO_NOASPECT, \
+ GUIO_LINKSPEECHTOSFX, \
GAMEOPTION_ORIGINAL_SAVELOAD)
// Larry 6 - English/German DOS CD - HIRES
@@ -2874,7 +2884,9 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
-#define GUIO_MOTHERGOOSEHIRES GUIO2(GUIO_NOASPECT, \
+#define GUIO_MOTHERGOOSEHIRES GUIO4(GUIO_NOSUBTITLES, \
+ GUIO_NOASPECT, \
+ GUIO_LINKSPEECHTOSFX, \
GAMEOPTION_ORIGINAL_SAVELOAD)
// Mixed-Up Mother Goose Deluxe - English Windows/DOS CD (supplied by markcoolio in bug report #2723810)
@@ -2907,9 +2919,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
-#define GUIO_PHANTASMAGORIA_DEMO GUIO4(GUIO_NOSUBTITLES, \
+#define GUIO_PHANTASMAGORIA_DEMO GUIO5(GUIO_NOSUBTITLES, \
GUIO_NOASPECT, \
GUIO_NOLAUNCHLOAD, \
+ GUIO_LINKSPEECHTOSFX, \
GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
#define GUIO_PHANTASMAGORIA GUIO_PHANTASMAGORIA_DEMO
#define GUIO_PHANTASMAGORIA_MAC GUIO_PHANTASMAGORIA_DEMO
@@ -3348,7 +3361,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#define GUIO_PQ4_FLOPPY GUIO2(GUIO_NOSPEECH, \
GAMEOPTION_ORIGINAL_SAVELOAD)
-#define GUIO_PQ4_CD GUIO2(GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, \
+#define GUIO_PQ4_CD GUIO3(GUIO_LINKSPEECHTOSFX, \
+ GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, \
GAMEOPTION_ORIGINAL_SAVELOAD)
// Police Quest 4 - English DOS CD (from the Police Quest Collection)
@@ -3394,11 +3408,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#undef GUIO_PQ4_FLOPPY
#undef GUIO_PQ4_CD
-#define GUIO_PQSWAT_DEMO GUIO4(GUIO_NOSUBTITLES, \
- GUIO_NOSPEECH, \
+#define GUIO_PQSWAT_DEMO GUIO6(GUIO_NOSUBTITLES, \
+ GUIO_NOMIDI, \
+ GUIO_LINKMUSICTOSFX, \
+ GUIO_LINKSPEECHTOSFX, \
GUIO_NOASPECT, \
GUIO_NOLAUNCHLOAD)
-#define GUIO_PQSWAT GUIO4(GUIO_NOMIDI, \
+#define GUIO_PQSWAT GUIO7(GUIO_NOSUBTITLES, \
+ GUIO_NOMIDI, \
+ GUIO_LINKMUSICTOSFX, \
+ GUIO_LINKSPEECHTOSFX, \
GUIO_NOASPECT, \
GAMEOPTION_ORIGINAL_SAVELOAD, \
GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
@@ -3797,7 +3816,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#define GUIO_QFG4_FLOPPY GUIO2(GUIO_NOSPEECH, \
GAMEOPTION_ORIGINAL_SAVELOAD)
-#define GUIO_QFG4_CD GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
+#define GUIO_QFG4_CD GUIO2(GUIO_LINKSPEECHTOSFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD)
// Quest for Glory 4 1.1 Floppy - English DOS (supplied by markcool in bug report #2723852)
// SCI interpreter version 2.000.000 (a guess?)
@@ -3920,12 +3940,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#endif // ENABLE_SCI3_GAMES
-#define GUIO_SHIVERS_DEMO GUIO5(GUIO_NOSUBTITLES, \
- GUIO_NOSPEECH, \
+#define GUIO_SHIVERS_DEMO GUIO6(GUIO_NOSUBTITLES, \
GUIO_NOMIDI, \
GUIO_NOLAUNCHLOAD, \
+ GUIO_LINKSPEECHTOSFX, \
+ GUIO_LINKMUSICTOSFX, \
GUIO_NOASPECT)
-#define GUIO_SHIVERS GUIO4(GUIO_NOMIDI, \
+#define GUIO_SHIVERS GUIO6(GUIO_NOMIDI, \
+ GUIO_LINKSPEECHTOSFX, \
+ GUIO_LINKMUSICTOSFX, \
GUIO_NOASPECT, \
GAMEOPTION_ORIGINAL_SAVELOAD, \
GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
@@ -4528,9 +4551,11 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
-#define GUIO_SQ6_DEMO GUIO2(GUIO_NOLAUNCHLOAD, \
+#define GUIO_SQ6_DEMO GUIO3(GUIO_NOLAUNCHLOAD, \
+ GUIO_LINKSPEECHTOSFX, \
GUIO_NOASPECT)
-#define GUIO_SQ6 GUIO3(GUIO_NOASPECT, \
+#define GUIO_SQ6 GUIO4(GUIO_LINKSPEECHTOSFX, \
+ GUIO_NOASPECT, \
GAMEOPTION_ORIGINAL_SAVELOAD, \
GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index 40d380195d..1085fec85b 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -534,6 +534,80 @@ SciVersion GameFeatures::detectSci21KernelType() {
}
#endif
+bool GameFeatures::supportsSpeechWithSubtitles() const {
+ switch (g_sci->getGameId()) {
+ case GID_SQ4:
+ case GID_FREDDYPHARKAS:
+ case GID_ECOQUEST:
+ case GID_LSL6:
+ case GID_LAURABOW2:
+ case GID_KQ6:
+#ifdef ENABLE_SCI32
+ // TODO: Hoyle5, SCI3
+ case GID_GK1:
+ case GID_KQ7:
+ case GID_LSL6HIRES:
+ case GID_PQ4:
+ case GID_QFG4:
+ case GID_SQ6:
+ case GID_TORIN:
+#endif
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool GameFeatures::audioVolumeSyncUsesGlobals() const {
+ switch (g_sci->getGameId()) {
+ case GID_GK1:
+ case GID_GK2:
+ case GID_LSL6HIRES:
+ case GID_PHANTASMAGORIA:
+ case GID_TORIN:
+ // TODO: SCI3
+ return true;
+ default:
+ return false;
+ }
+}
+
+MessageTypeSyncStrategy GameFeatures::getMessageTypeSyncStrategy() const {
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ return kMessageTypeSyncStrategyNone;
+ }
+
+ if (getSciVersion() == SCI_VERSION_1_1 && g_sci->isCD()) {
+ return kMessageTypeSyncStrategyDefault;
+ }
+
+#ifdef ENABLE_SCI32
+ switch (g_sci->getGameId()) {
+ // TODO: Hoyle5, SCI3
+ case GID_GK1:
+ case GID_KQ7:
+ case GID_MOTHERGOOSEHIRES:
+ case GID_PHANTASMAGORIA:
+ case GID_PQ4:
+ case GID_QFG4:
+ case GID_TORIN:
+ return kMessageTypeSyncStrategyDefault;
+
+ case GID_LSL6HIRES:
+ return kMessageTypeSyncStrategyLSL6Hires;
+
+ case GID_SHIVERS:
+ return kMessageTypeSyncStrategyShivers;
+
+ default:
+ break;
+ }
+#endif
+
+ return kMessageTypeSyncStrategyNone;
+}
+
bool GameFeatures::autoDetectMoveCountType() {
// Look up the script address
reg_t addr = getDetectionAddr("Motion", SELECTOR(doit));
diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h
index 8f84bbacad..044111a43e 100644
--- a/engines/sci/engine/features.h
+++ b/engines/sci/engine/features.h
@@ -40,6 +40,16 @@ enum PseudoMouseAbilityType {
kPseudoMouseAbilityTrue
};
+enum MessageTypeSyncStrategy {
+ kMessageTypeSyncStrategyNone,
+ kMessageTypeSyncStrategyDefault
+#ifdef ENABLE_SCI32
+ ,
+ kMessageTypeSyncStrategyLSL6Hires,
+ kMessageTypeSyncStrategyShivers
+#endif
+};
+
class GameFeatures {
public:
GameFeatures(SegManager *segMan, Kernel *kernel);
@@ -148,6 +158,40 @@ public:
#endif
/**
+ * If true, the current game supports simultaneous speech & subtitles.
+ */
+ bool supportsSpeechWithSubtitles() const;
+
+ /**
+ * If true, the game supports changing text speed.
+ */
+ bool supportsTextSpeed() const {
+ switch (g_sci->getGameId()) {
+#ifdef ENABLE_SCI32
+ case GID_GK1:
+ case GID_SQ6:
+ return true;
+#endif
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ /**
+ * If true, audio volume sync between the game and ScummVM is done by
+ * monitoring and setting game global variables.
+ */
+ bool audioVolumeSyncUsesGlobals() const;
+
+ /**
+ * The strategy that should be used when synchronising the message type
+ * (text/speech/text+speech) between the game and ScummVM.
+ */
+ MessageTypeSyncStrategy getMessageTypeSyncStrategy() const;
+
+ /**
* Applies to all versions before 0.000.502
* Old SCI versions used to interpret the third DrawPic() parameter inversely,
* with the opposite default value (obviously).
diff --git a/engines/sci/engine/guest_additions.cpp b/engines/sci/engine/guest_additions.cpp
new file mode 100644
index 0000000000..a2c64239a5
--- /dev/null
+++ b/engines/sci/engine/guest_additions.cpp
@@ -0,0 +1,866 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "audio/mixer.h"
+#include "common/config-manager.h"
+#include "common/gui_options.h"
+#include "sci/engine/features.h"
+#include "sci/engine/guest_additions.h"
+#include "sci/engine/kernel.h"
+#include "sci/engine/state.h"
+#include "sci/engine/vm.h"
+#ifdef ENABLE_SCI32
+#include "sci/graphics/frameout.h"
+#endif
+#include "sci/sound/music.h"
+#include "sci/sci.h"
+
+namespace Sci {
+
+enum {
+ kSoundsMusicType = 0,
+ kSoundsSoundType = 1
+};
+
+enum {
+ kMessageTypeSubtitles = 1,
+ kMessageTypeSpeech = 2
+};
+
+enum {
+ kLSL6HiresUIVolumeMax = 13,
+ kLSL6HiresSubtitleFlag = 105
+};
+
+
+GuestAdditions::GuestAdditions(EngineState *state, GameFeatures *features) :
+ _state(state),
+ _features(features),
+ _segMan(state->_segMan),
+ _messageTypeSynced(false) {}
+
+#pragma mark -
+
+void GuestAdditions::syncSoundSettings() const {
+#ifdef ENABLE_SCI32
+ if (_features->audioVolumeSyncUsesGlobals())
+ syncAudioVolumeGlobalsFromScummVM();
+ else
+#endif
+ syncMasterVolumeFromScummVM();
+}
+
+void GuestAdditions::syncAudioOptionsFromScummVM() const {
+#ifdef ENABLE_SCI32
+ if (_features->supportsTextSpeed()) {
+ syncTextSpeedFromScummVM();
+ }
+#endif
+ syncMessageTypeFromScummVM();
+}
+
+void GuestAdditions::reset() {
+ _messageTypeSynced = false;
+}
+
+void GuestAdditions::invokeSelector(const reg_t objId, const Selector selector, const int argc, const StackPtr argv) const {
+ ::Sci::invokeSelector(_state, objId, selector, 0, _state->_executionStack.back().sp, argc, argv);
+}
+
+bool GuestAdditions::shouldSyncAudio() const {
+ const SciGameId gameId = g_sci->getGameId();
+ Common::List<ExecStack>::const_iterator it;
+ for (it = _state->_executionStack.begin(); it != _state->_executionStack.end(); ++it) {
+ const ExecStack &call = *it;
+ const Common::String objName = _segMan->getObjectName(call.sendp);
+
+ if (getSciVersion() < SCI_VERSION_2 && (objName == "TheMenuBar" ||
+ objName == "MenuBar")) {
+ // SCI16 with menu bar
+ return true;
+ } else if (objName == "volumeSlider") {
+ // SCI16 with icon bar, QFG4, Hoyle5
+ return true;
+ } else if (gameId == GID_MOTHERGOOSE256 && objName == "soundBut") {
+ return true;
+ } else if (gameId == GID_SLATER && objName == "volButton") {
+ return true;
+ } else if (gameId == GID_LSL6 && objName == "menuBar") {
+ return true;
+#ifdef ENABLE_SCI32
+ } else if ((gameId == GID_GK1 || gameId == GID_SQ6) && (objName == "musicBar" ||
+ objName == "soundBar")) {
+ return true;
+ } else if (gameId == GID_PQ4 && (objName == "increaseVolume" ||
+ objName == "decreaseVolume")) {
+ return true;
+ } else if (gameId == GID_KQ7 && (objName == "volumeUp" ||
+ objName == "volumeDown")) {
+ return true;
+ } else if (gameId == GID_LSL6HIRES && (objName == "hiResMenu" ||
+ objName == "volumeDial")) {
+ return true;
+ } else if (gameId == GID_MOTHERGOOSEHIRES && objName == "MgButtonBar") {
+ return true;
+ } else if (gameId == GID_PQSWAT && (objName == "volumeDownButn" ||
+ objName == "volumeUpButn")) {
+ return true;
+ } else if (gameId == GID_SHIVERS && objName == "spVolume") {
+ return true;
+ } else if (gameId == GID_GK2 && objName == "soundSlider") {
+ return true;
+ } else if (gameId == GID_PHANTASMAGORIA && (objName == "midiVolDown" ||
+ objName == "midiVolUp" ||
+ objName == "dacVolDown" ||
+ objName == "dacVolUp")) {
+ return true;
+ } else if (gameId == GID_TORIN && (objName == "oMusicScroll" ||
+ objName == "oSFXScroll" ||
+ objName == "oAudioScroll")) {
+ return true;
+#endif
+ }
+ }
+
+ return false;
+}
+
+#pragma mark -
+#pragma mark Hooks
+
+void GuestAdditions::sciEngineRunGameHook() {
+ _messageTypeSynced = true;
+}
+
+void GuestAdditions::writeVarHook(const int type, const int index, const reg_t value) {
+ if (type == VAR_GLOBAL) {
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ if (_features->audioVolumeSyncUsesGlobals() && shouldSyncAudio()) {
+ syncAudioVolumeGlobalsToScummVM(index, value);
+ } else if (g_sci->getGameId() == GID_GK1) {
+ syncGK1StartupVolumeFromScummVM(index, value);
+ }
+
+ if (_features->supportsTextSpeed()) {
+ syncTextSpeedToScummVM(index, value);
+ }
+ }
+#endif
+ syncMessageTypeToScummVM(index, value);
+ }
+}
+
+bool GuestAdditions::kDoSoundMasterVolumeHook(const int volume) const {
+ if (!_features->audioVolumeSyncUsesGlobals() && shouldSyncAudio()) {
+ syncMasterVolumeToScummVM(volume);
+ return true;
+ }
+ return false;
+}
+
+#ifdef ENABLE_SCI32
+void GuestAdditions::sendSelectorHook(const reg_t sendObj, Selector &selector, reg_t *argp) {
+ if (_features->getMessageTypeSyncStrategy() == kMessageTypeSyncStrategyLSL6Hires) {
+ syncMessageTypeToScummVMUsingLSL6HiresStrategy(sendObj, selector, argp);
+ }
+}
+
+bool GuestAdditions::audio32SetVolumeHook(const int16 channelIndex, int16 volume) const {
+ if (!_features->audioVolumeSyncUsesGlobals() && shouldSyncAudio()) {
+ volume = volume * Audio::Mixer::kMaxMixerVolume / Audio32::kMaxVolume;
+ if (Common::checkGameGUIOption(GUIO_LINKMUSICTOSFX, ConfMan.get("guioptions"))) {
+ ConfMan.setInt("music_volume", volume);
+ }
+ ConfMan.setInt("sfx_volume", volume);
+ ConfMan.setInt("speech_volume", volume);
+ g_engine->syncSoundSettings();
+ return true;
+ }
+
+ return false;
+}
+
+void GuestAdditions::kDoSoundSetVolumeHook(const reg_t soundObj, const int16 volume) const {
+ if (g_sci->getGameId() == GID_GK1 && shouldSyncAudio()) {
+ syncGK1AudioVolumeToScummVM(soundObj, volume);
+ }
+}
+#endif
+
+#pragma mark -
+#pragma mark Message type sync
+
+void GuestAdditions::syncMessageTypeFromScummVM() const {
+ switch (_features->getMessageTypeSyncStrategy()) {
+ case kMessageTypeSyncStrategyDefault:
+ syncMessageTypeFromScummVMUsingDefaultStrategy();
+ break;
+
+#ifdef ENABLE_SCI32
+ case kMessageTypeSyncStrategyShivers:
+ syncMessageTypeFromScummVMUsingShiversStrategy();
+ break;
+
+ case kMessageTypeSyncStrategyLSL6Hires:
+ syncMessageTypeFromScummVMUsingLSL6HiresStrategy();
+ break;
+#endif
+ case kMessageTypeSyncStrategyNone:
+ break;
+ }
+}
+
+void GuestAdditions::syncMessageTypeFromScummVMUsingDefaultStrategy() const {
+ uint8 value = 0;
+ if (ConfMan.getBool("subtitles")) {
+ value |= kMessageTypeSubtitles;
+ }
+ if (!ConfMan.getBool(("speech_mute"))) {
+ value |= kMessageTypeSpeech;
+ }
+
+ if (value == kMessageTypeSubtitles + kMessageTypeSpeech && !_features->supportsSpeechWithSubtitles()) {
+ value &= ~kMessageTypeSubtitles;
+ }
+
+ if (value) {
+ _state->variables[VAR_GLOBAL][kGlobalVarMessageType] = make_reg(0, value);
+ }
+
+ if (g_sci->getGameId() == GID_GK1) {
+ if (value == kMessageTypeSubtitles) {
+ _state->variables[VAR_GLOBAL][kGlobalVarGK1NarratorMode] = NULL_REG;
+ } else if (value == kMessageTypeSpeech) {
+ _state->variables[VAR_GLOBAL][kGlobalVarGK1NarratorMode] = TRUE_REG;
+ }
+ }
+}
+
+#ifdef ENABLE_SCI32
+void GuestAdditions::syncMessageTypeFromScummVMUsingShiversStrategy() const {
+ if (ConfMan.getBool("subtitles")) {
+ _state->variables[VAR_GLOBAL][kGlobalVarShiversFlags] |= 256;
+ } else {
+ _state->variables[VAR_GLOBAL][kGlobalVarShiversFlags] &= ~256;
+ }
+}
+
+void GuestAdditions::syncMessageTypeFromScummVMUsingLSL6HiresStrategy() const {
+ // LSL6hires synchronisation happens in send_selector, except when
+ // restoring a game, where it happens here
+ if (_state->variables[VAR_GLOBAL][kGlobalVarLSL6HiresGameFlags].isNull()) {
+ return;
+ }
+
+ reg_t params[] = { make_reg(0, kLSL6HiresSubtitleFlag) };
+ Selector selector;
+ reg_t restore;
+
+ if (ConfMan.getBool("subtitles")) {
+ restore = TRUE_REG;
+ selector = SELECTOR(clear);
+ } else {
+ restore = NULL_REG;
+ selector = SELECTOR(set);
+ }
+
+ // Attempting to show or hide the ScrollWindow used for subtitles
+ // directly (by invoking `show` or `hide`) causes the game to crash with
+ // an error about passing an invalid ScrollWindow ID. Fortunately, the
+ // game scripts store a flag that restores the window when a game is
+ // restored
+ _state->variables[VAR_GLOBAL][kGlobalVarLSL6HiresRestoreTextWindow] = restore;
+ invokeSelector(_state->variables[VAR_GLOBAL][kGlobalVarLSL6HiresGameFlags], selector, 1, params);
+}
+#endif
+
+void GuestAdditions::syncMessageTypeToScummVM(const int index, const reg_t value) {
+ switch (_features->getMessageTypeSyncStrategy()) {
+ case kMessageTypeSyncStrategyDefault:
+ syncMessageTypeToScummVMUsingDefaultStrategy(index, value);
+ break;
+
+#ifdef ENABLE_SCI32
+ case kMessageTypeSyncStrategyShivers:
+ syncMessageTypeToScummVMUsingShiversStrategy(index, value);
+ break;
+
+ case kMessageTypeSyncStrategyLSL6Hires:
+ // LSL6hires synchronisation happens via send_selector
+#endif
+ case kMessageTypeSyncStrategyNone:
+ break;
+ }
+}
+
+void GuestAdditions::syncMessageTypeToScummVMUsingDefaultStrategy(const int index, const reg_t value) {
+ if (index == kGlobalVarMessageType) {
+ // ScummVM audio options haven't been applied yet. Use this set call
+ // as a trigger to apply defaults from ScummVM, ignoring the default
+ // value that was just received from the game scripts
+ if (!_messageTypeSynced || _state->variables[VAR_GLOBAL][kGlobalVarQuit] == TRUE_REG) {
+ _messageTypeSynced = true;
+ syncAudioOptionsFromScummVM();
+ return;
+ }
+
+ ConfMan.setBool("subtitles", value.toSint16() & kMessageTypeSubtitles);
+ ConfMan.setBool("speech_mute", !(value.toSint16() & kMessageTypeSpeech));
+ }
+}
+
+#ifdef ENABLE_SCI32
+void GuestAdditions::syncMessageTypeToScummVMUsingShiversStrategy(const int index, const reg_t value) {
+ if (index == kGlobalVarShiversFlags) {
+ // ScummVM audio options haven't been applied yet, so apply them
+ // and ignore the default value that was just received from the
+ // game scripts
+ if (!_messageTypeSynced || _state->variables[VAR_GLOBAL][kGlobalVarQuit] == TRUE_REG) {
+ _messageTypeSynced = true;
+ syncAudioOptionsFromScummVM();
+ return;
+ }
+
+ ConfMan.setBool("subtitles", value.toUint16() & 256);
+ }
+}
+
+void GuestAdditions::syncMessageTypeToScummVMUsingLSL6HiresStrategy(const reg_t sendObj, Selector &selector, reg_t *argp) {
+ if (_state->variables[VAR_GLOBAL][kGlobalVarLSL6HiresGameFlags] == sendObj &&
+ (selector == SELECTOR(clear) || selector == SELECTOR(set))) {
+
+ if (argp[1].toUint16() == kLSL6HiresSubtitleFlag) {
+ if (_messageTypeSynced) {
+ ConfMan.setBool("subtitles", selector == SELECTOR(clear));
+ } else if (ConfMan.getBool("subtitles")) {
+ selector = SELECTOR(clear);
+ argp[-1].setOffset(selector);
+ _messageTypeSynced = true;
+ } else {
+ selector = SELECTOR(set);
+ argp[-1].setOffset(selector);
+ _messageTypeSynced = true;
+ }
+ }
+ }
+}
+#endif
+
+#pragma mark -
+#pragma mark Master volume sync
+
+void GuestAdditions::syncMasterVolumeFromScummVM() const {
+ const int16 musicVolume = (ConfMan.getInt("music_volume") + 1) * MUSIC_MASTERVOLUME_MAX / Audio::Mixer::kMaxMixerVolume;
+
+ // When the volume changes from the ScummVM launcher, ScummVM automatically
+ // adjusts the software mixer in Engine::syncSoundSettings, but MIDI may not
+ // run through the ScummVM mixer so its master volume must be adjusted
+ // explicitly
+ if (g_sci->_soundCmd) {
+ g_sci->_soundCmd->setMasterVolume(ConfMan.getBool("mute") ? 0 : musicVolume);
+ }
+
+#ifdef ENABLE_SCI32
+ const int16 sfxVolume = (ConfMan.getInt("sfx_volume") + 1) * Audio32::kMaxVolume / Audio::Mixer::kMaxMixerVolume;
+
+ // Volume was changed from ScummVM during the game, so resync the
+ // in-game UI
+ syncInGameUI(musicVolume, sfxVolume);
+#endif
+}
+
+void GuestAdditions::syncMasterVolumeToScummVM(const int16 masterVolume) const {
+ const int scummVMVolume = masterVolume * Audio::Mixer::kMaxMixerVolume / MUSIC_MASTERVOLUME_MAX;
+ ConfMan.setInt("music_volume", scummVMVolume);
+
+ if (Common::checkGameGUIOption(GUIO_LINKMUSICTOSFX, ConfMan.get("guioptions"))) {
+ ConfMan.setInt("sfx_volume", scummVMVolume);
+ if (Common::checkGameGUIOption(GUIO_LINKSPEECHTOSFX, ConfMan.get("guioptions"))) {
+ ConfMan.setInt("speech_volume", scummVMVolume);
+ }
+ }
+
+ // In SCI32, digital audio volume is controlled separately by
+ // kDoAudioVolume
+ // TODO: In SCI16, the volume slider only changed the music volume.
+ // Is this non-standard behavior better, or just wrong?
+ if (getSciVersion() < SCI_VERSION_2) {
+ ConfMan.setInt("sfx_volume", scummVMVolume);
+ ConfMan.setInt("speech_volume", scummVMVolume);
+ }
+ g_engine->syncSoundSettings();
+}
+
+#ifdef ENABLE_SCI32
+#pragma mark -
+#pragma mark Globals volume sync
+
+void GuestAdditions::syncAudioVolumeGlobalsFromScummVM() const {
+ // On muting: Setting the music volume to zero when mute is enabled is done
+ // only for the games that use MIDI for music playback, since MIDI playback
+ // does not always run through the ScummVM mixer. Games that use digital
+ // audio for music do not need any extra code since that always runs
+ // straight through the audio mixer, which gets muted directly
+ switch (g_sci->getGameId()) {
+ case GID_GK1: {
+ const int16 musicVolume = (ConfMan.getInt("music_volume") + 1) * MUSIC_VOLUME_MAX / Audio::Mixer::kMaxMixerVolume;
+ const int16 dacVolume = (ConfMan.getInt("sfx_volume") + 1) * Audio32::kMaxVolume / Audio::Mixer::kMaxMixerVolume;
+ syncGK1VolumeFromScummVM(musicVolume, dacVolume);
+ syncGK1UI();
+ break;
+ }
+
+ case GID_GK2: {
+ const int16 musicVolume = (ConfMan.getInt("music_volume") + 1) * Audio32::kMaxVolume / Audio::Mixer::kMaxMixerVolume;
+ syncGK2VolumeFromScummVM(musicVolume);
+ syncGK2UI();
+ break;
+ }
+
+ case GID_LSL6HIRES: {
+ const int16 musicVolume = (ConfMan.getInt("music_volume") + 1) * kLSL6HiresUIVolumeMax / Audio::Mixer::kMaxMixerVolume;
+ syncLSL6HiresVolumeFromScummVM(musicVolume);
+ syncLSL6HiresUI(musicVolume);
+ break;
+ }
+
+ case GID_PHANTASMAGORIA: {
+ reg_t &musicGlobal = _state->variables[VAR_GLOBAL][kGlobalVarPhant1MusicVolume];
+ reg_t &dacGlobal = _state->variables[VAR_GLOBAL][kGlobalVarPhant1DACVolume];
+
+ const int16 oldMusicVolume = musicGlobal.toSint16();
+ const int16 oldDacVolume = dacGlobal.toSint16();
+
+ const int16 musicVolume = (ConfMan.getInt("music_volume") + 1) * MUSIC_MASTERVOLUME_MAX / Audio::Mixer::kMaxMixerVolume;
+ const int16 dacVolume = (ConfMan.getInt("sfx_volume") + 1) * Audio32::kMaxVolume / Audio::Mixer::kMaxMixerVolume;
+
+ g_sci->_soundCmd->setMasterVolume(ConfMan.getBool("mute") ? 0 : musicVolume);
+
+ // Phant1 has a fragile volume UI. Global volumes need to be set during
+ // UI updates to move the volume bars to the correct position
+ syncPhant1UI(oldMusicVolume, musicVolume, musicGlobal, oldDacVolume, dacVolume, dacGlobal);
+ break;
+ }
+
+ case GID_TORIN: {
+ const int16 musicVolume = (ConfMan.getInt("music_volume") + 1) * 100 / Audio::Mixer::kMaxMixerVolume;
+ const int16 sfxVolume = (ConfMan.getInt("sfx_volume") + 1) * 100 / Audio::Mixer::kMaxMixerVolume;
+ const int16 speechVolume = (ConfMan.getInt("speech_volume") + 1) * 100 / Audio::Mixer::kMaxMixerVolume;
+ syncTorinVolumeFromScummVM(musicVolume, sfxVolume, speechVolume);
+ syncTorinUI(musicVolume, sfxVolume, speechVolume);
+ break;
+ }
+
+ default:
+ error("Trying to sync audio volume globals in a game with no implementation");
+ }
+}
+
+void GuestAdditions::syncGK1StartupVolumeFromScummVM(const int index, const reg_t value) const {
+ if (index == kGlobalVarGK1Music1 || index == kGlobalVarGK1Music2 ||
+ index == kGlobalVarGK1DAC1 || index == kGlobalVarGK1DAC2 ||
+ index == kGlobalVarGK1DAC3) {
+
+ int16 volume;
+ Selector selector;
+
+ switch (readSelectorValue(_segMan, value, SELECTOR(type))) {
+ case kSoundsMusicType: {
+ volume = (ConfMan.getInt("music_volume") + 1) * MUSIC_VOLUME_MAX / Audio::Mixer::kMaxMixerVolume;
+ selector = SELECTOR(musicVolume);
+ break;
+ }
+
+ case kSoundsSoundType: {
+ volume = (ConfMan.getInt("sound_volume") + 1) * MUSIC_VOLUME_MAX / Audio::Mixer::kMaxMixerVolume;
+ selector = SELECTOR(soundVolume);
+ break;
+ }
+
+ default:
+ error("Unknown sound type");
+ }
+
+ writeSelectorValue(_segMan, value, selector, volume);
+ writeSelectorValue(_segMan, value, selector, volume);
+ }
+}
+
+void GuestAdditions::syncGK1VolumeFromScummVM(const int16 musicVolume, const int16 dacVolume) const {
+ const reg_t soundsId = _state->variables[VAR_GLOBAL][kGlobalVarSounds];
+ if (!soundsId.isNull()) {
+ List *sounds = _segMan->lookupList(readSelector(_segMan, soundsId, SELECTOR(elements)));
+ reg_t soundId = sounds->first;
+ while (!soundId.isNull()) {
+ Node *sound = _segMan->lookupNode(soundId);
+ const int16 type = readSelectorValue(_segMan, sound->value, SELECTOR(type));
+ int16 volume;
+
+ if (type == kSoundsMusicType) {
+ volume = ConfMan.getBool("mute") ? 0 : musicVolume;
+ writeSelectorValue(_segMan, sound->value, SELECTOR(musicVolume), musicVolume);
+ } else if (type == kSoundsSoundType) {
+ volume = dacVolume;
+ writeSelectorValue(_segMan, sound->value, SELECTOR(soundVolume), dacVolume);
+ } else {
+ error("Unknown sound type %d", type);
+ }
+
+ // `setVolume` will set the `vol` property on the sound object;
+ // if it did not do this, an invocation of the `setVol` selector
+ // would need to be here (though doing so would result in
+ // recursion, so don't)
+ g_sci->_soundCmd->setVolume(sound->value, volume);
+ soundId = sound->succ;
+ }
+ }
+}
+
+void GuestAdditions::syncGK2VolumeFromScummVM(const int16 musicVolume) const {
+ _state->variables[VAR_GLOBAL][kGlobalVarGK2MusicVolume] = make_reg(0, musicVolume);
+
+ // Calling `setVol` on all sounds is necessary to propagate the volume
+ // change to existing sounds, and matches how game scripts propagate
+ // volume changes when the in-game music slider is moved
+ const reg_t soundsId = _state->variables[VAR_GLOBAL][kGlobalVarSounds];
+ if (!soundsId.isNull()) {
+ List *sounds = _segMan->lookupList(readSelector(_segMan, soundsId, SELECTOR(elements)));
+ reg_t soundId = sounds->first;
+ while (!soundId.isNull()) {
+ Node *sound = _segMan->lookupNode(soundId);
+ reg_t params[] = { make_reg(0, musicVolume) };
+ invokeSelector(sound->value, SELECTOR(setVol), 1, params);
+ soundId = sound->succ;
+ }
+ }
+}
+
+void GuestAdditions::syncLSL6HiresVolumeFromScummVM(const int16 musicVolume) const {
+ _state->variables[VAR_GLOBAL][kGlobalVarLSL6HiresMusicVolume] = make_reg(0, musicVolume);
+ g_sci->_soundCmd->setMasterVolume(ConfMan.getBool("mute") ? 0 : (musicVolume * MUSIC_MASTERVOLUME_MAX / kLSL6HiresUIVolumeMax));
+}
+
+void GuestAdditions::syncTorinVolumeFromScummVM(const int16 musicVolume, const int16 sfxVolume, const int16 speechVolume) const {
+ _state->variables[VAR_GLOBAL][kGlobalVarTorinMusicVolume] = make_reg(0, musicVolume);
+ _state->variables[VAR_GLOBAL][kGlobalVarTorinSFXVolume] = make_reg(0, sfxVolume);
+ _state->variables[VAR_GLOBAL][kGlobalVarTorinSpeechVolume] = make_reg(0, speechVolume);
+
+ // Calling `reSyncVol` on all sounds is necessary to propagate the
+ // volume change to existing sounds, and matches how game scripts
+ // propagate volume changes when the in-game volume sliders are moved
+ const reg_t soundsId = _state->variables[VAR_GLOBAL][kGlobalVarSounds];
+ if (!soundsId.isNull()) {
+ const Selector selector = SELECTOR(reSyncVol);
+ List *sounds = _segMan->lookupList(readSelector(_segMan, soundsId, SELECTOR(elements)));
+ reg_t soundId = sounds->first;
+ while (!soundId.isNull()) {
+ Node *sound = _segMan->lookupNode(soundId);
+ const reg_t &soundObj = sound->value;
+
+ if (_segMan->isHeapObject(soundObj) && lookupSelector(_segMan, soundObj, selector, nullptr, nullptr) != kSelectorNone) {
+ invokeSelector(sound->value, SELECTOR(reSyncVol));
+ }
+ soundId = sound->succ;
+ }
+ }
+}
+
+void GuestAdditions::syncAudioVolumeGlobalsToScummVM(const int index, const reg_t value) const {
+ switch (g_sci->getGameId()) {
+ case GID_GK2:
+ if (index == kGlobalVarGK2MusicVolume) {
+ const int16 musicVolume = value.toSint16() * Audio::Mixer::kMaxMixerVolume / Audio32::kMaxVolume;
+ ConfMan.setInt("music_volume", musicVolume);
+ }
+ break;
+
+ case GID_LSL6HIRES:
+ if (index == kGlobalVarLSL6HiresMusicVolume) {
+ const int16 musicVolume = value.toSint16() * Audio::Mixer::kMaxMixerVolume / kLSL6HiresUIVolumeMax;
+ ConfMan.setInt("music_volume", musicVolume);
+ }
+ break;
+
+ case GID_PHANTASMAGORIA:
+ if (index == kGlobalVarPhant1MusicVolume) {
+ const int16 musicVolume = value.toSint16() * Audio::Mixer::kMaxMixerVolume / MUSIC_MASTERVOLUME_MAX;
+ ConfMan.setInt("music_volume", musicVolume);
+ } else if (index == kGlobalVarPhant1DACVolume) {
+ const int16 dacVolume = value.toSint16() * Audio::Mixer::kMaxMixerVolume / Audio32::kMaxVolume;
+ ConfMan.setInt("sfx_volume", dacVolume);
+ ConfMan.setInt("speech_volume", dacVolume);
+ }
+ break;
+
+ case GID_TORIN:
+ if (index == kGlobalVarTorinMusicVolume ||
+ index == kGlobalVarTorinSFXVolume ||
+ index == kGlobalVarTorinSpeechVolume) {
+
+ const int16 volume = value.toSint16() * Audio::Mixer::kMaxMixerVolume / 100;
+
+ switch (index) {
+ case kGlobalVarTorinMusicVolume:
+ ConfMan.setInt("music_volume", volume);
+ break;
+ case kGlobalVarTorinSFXVolume:
+ ConfMan.setInt("sfx_volume", volume);
+ break;
+ case kGlobalVarTorinSpeechVolume:
+ ConfMan.setInt("speech_volume", volume);
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void GuestAdditions::syncGK1AudioVolumeToScummVM(const reg_t soundObj, int16 volume) const {
+ const Common::String objName = _segMan->getObjectName(soundObj);
+ volume = volume * Audio::Mixer::kMaxMixerVolume / MUSIC_VOLUME_MAX;
+
+ // Using highest-numbered sound objects to sync only after all slots
+ // have been set by the volume slider
+ if (objName == "gkMusic2") {
+ ConfMan.setInt("music_volume", volume);
+ g_engine->syncSoundSettings();
+ } else if (objName == "gkSound3") {
+ ConfMan.setInt("sfx_volume", volume);
+ ConfMan.setInt("speech_volume", volume);
+ g_engine->syncSoundSettings();
+ }
+}
+
+#pragma mark -
+#pragma mark Audio UI sync
+
+void GuestAdditions::syncInGameUI(const int16 musicVolume, const int16 sfxVolume) const {
+ if (_state->abortScriptProcessing != kAbortNone) {
+ // Attempting to update a UI that is in the process of being destroyed
+ // will result in a crash
+ return;
+ }
+
+ switch (g_sci->getGameId()) {
+ case GID_PQ4:
+ syncPQ4UI(musicVolume);
+ break;
+
+ case GID_PQSWAT:
+ syncPQSWATUI();
+ break;
+
+ case GID_QFG4:
+ syncQFG4UI(musicVolume);
+ break;
+
+ case GID_SHIVERS:
+ syncShivers1UI(sfxVolume);
+ break;
+
+ case GID_SQ6:
+ syncSQ6UI();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void GuestAdditions::syncGK1UI() const {
+ const reg_t bars[] = { _segMan->findObjectByName("musicBar"),
+ _segMan->findObjectByName("soundBar") };
+
+ for (int i = 0; i < ARRAYSIZE(bars); ++i) {
+ const reg_t barId = bars[i];
+ if (!barId.isNull()) {
+ // Resetting the position to 0 causes the bar to refresh its
+ // position when it next draws
+ writeSelectorValue(_segMan, barId, SELECTOR(position), 0);
+
+ // The `signal` property indicates bar visibility (for some
+ // reason, the normal `-info-` flag is not used)
+ if (readSelectorValue(_segMan, barId, SELECTOR(signal)) & 0x20) {
+ // `show` pulls a new value from the underlying sound object
+ // and refreshes the bar rendering
+ invokeSelector(barId, SELECTOR(show));
+ }
+ }
+ }
+}
+
+void GuestAdditions::syncGK2UI() const {
+ const reg_t sliderId = _segMan->findObjectByName("soundSlider");
+ if (!sliderId.isNull() && _segMan->getObject(sliderId)->isInserted()) {
+ const reg_t oldAcc = _state->r_acc;
+ invokeSelector(sliderId, SELECTOR(initialOff));
+ writeSelector(_segMan, sliderId, SELECTOR(x), _state->r_acc);
+ _state->r_acc = oldAcc;
+ }
+}
+
+void GuestAdditions::syncLSL6HiresUI(const int16 musicVolume) const {
+ const reg_t musicDialId = _segMan->findObjectByName("volumeDial");
+ if (!musicDialId.isNull()) {
+ writeSelectorValue(_segMan, musicDialId, SELECTOR(curPos), musicVolume);
+ writeSelectorValue(_segMan, musicDialId, SELECTOR(cel), musicVolume);
+ reg_t params[] = { make_reg(0, musicVolume) };
+ invokeSelector(musicDialId, SELECTOR(update), 1, params);
+ if (_segMan->getObject(musicDialId)->isInserted()) {
+ g_sci->_gfxFrameout->kernelUpdateScreenItem(musicDialId);
+ }
+ }
+}
+
+void GuestAdditions::syncPhant1UI(const int16 oldMusicVolume, const int16 musicVolume, reg_t &musicGlobal, const int16 oldDacVolume, const int16 dacVolume, reg_t &dacGlobal) const {
+ const reg_t buttonId = _segMan->findObjectByName("dacVolUp");
+ if (buttonId.isNull() || !_segMan->getObject(buttonId)->isInserted()) {
+ // No inserted dacVolUp button means the control panel with the
+ // volume controls is not visible and we can just update the values
+ // and leave
+ musicGlobal.setOffset(musicVolume);
+ dacGlobal.setOffset(dacVolume);
+ return;
+ }
+
+ reg_t thermo = _segMan->findObjectByName("midiVolThermo");
+ if (!thermo.isNull()) {
+ int count = ABS(musicVolume - oldMusicVolume);
+ const int stepSize = (musicVolume > oldMusicVolume ? 1 : -1);
+ while (count--) {
+ musicGlobal.incOffset(stepSize);
+ invokeSelector(thermo, SELECTOR(doit));
+ }
+ }
+
+ thermo = _segMan->findObjectByName("dacVolThermo");
+ if (!thermo.isNull()) {
+ int count = ABS(dacVolume - oldDacVolume) / 8;
+ const int stepSize = (dacVolume > oldDacVolume ? 8 : -8);
+ while (count--) {
+ dacGlobal.incOffset(stepSize);
+ invokeSelector(thermo, SELECTOR(doit));
+ }
+ }
+}
+
+void GuestAdditions::syncPQ4UI(const int16 musicVolume) const {
+ const SegmentId segment = _segMan->getScriptSegment(9, SCRIPT_GET_DONT_LOAD);
+ if (segment != 0 && _segMan->getScript(segment)->getLocalsCount() > 2) {
+ const reg_t barId = _segMan->getScript(segment)->getLocalsBegin()[2];
+ if (!barId.isNull()) {
+ reg_t params[] = { make_reg(0, musicVolume) };
+ invokeSelector(barId, SELECTOR(setSize), 1, params);
+ }
+ }
+}
+
+void GuestAdditions::syncPQSWATUI() const {
+ const reg_t barId = _segMan->findObjectByName("volumeLed");
+ if (!barId.isNull() && _segMan->getObject(barId)->isInserted()) {
+ invokeSelector(barId, SELECTOR(displayValue));
+ }
+}
+
+void GuestAdditions::syncQFG4UI(const int16 musicVolume) const {
+ const reg_t sliderId = _segMan->findObjectByName("volumeSlider");
+ if (!sliderId.isNull()) {
+ const int16 yPosition = 84 - musicVolume * 34 / 10;
+ writeSelectorValue(_segMan, sliderId, SELECTOR(y), yPosition);
+
+ // There does not seem to be any good way to learn whether the
+ // volume slider is visible (and thus eligible for
+ // kUpdateScreenItem)
+ const reg_t planeId = readSelector(_segMan, sliderId, SELECTOR(plane));
+ if (g_sci->_gfxFrameout->getPlanes().findByObject(planeId) != nullptr) {
+ g_sci->_gfxFrameout->kernelUpdateScreenItem(sliderId);
+ }
+ }
+}
+
+void GuestAdditions::syncShivers1UI(const int16 dacVolume) const {
+ const reg_t sliderId = _segMan->findObjectByName("spVolume");
+ if (!sliderId.isNull()) {
+ const int16 xPosition = dacVolume * 78 / Audio32::kMaxVolume + 32;
+ writeSelectorValue(_segMan, sliderId, SELECTOR(x), xPosition);
+ if (_segMan->getObject(sliderId)->isInserted()) {
+ g_sci->_gfxFrameout->kernelUpdateScreenItem(sliderId);
+ }
+ }
+}
+
+void GuestAdditions::syncSQ6UI() const {
+ const reg_t bars[] = { _segMan->findObjectByName("musicBar"),
+ _segMan->findObjectByName("soundBar") };
+ for (int i = 0; i < ARRAYSIZE(bars); ++i) {
+ const reg_t barId = bars[i];
+ if (!barId.isNull()) {
+ invokeSelector(barId, SELECTOR(show));
+ }
+ }
+}
+
+void GuestAdditions::syncTorinUI(const int16 musicVolume, const int16 sfxVolume, const int16 speechVolume) const {
+ const reg_t sliders[] = { _segMan->findObjectByName("oMusicScroll"),
+ _segMan->findObjectByName("oSFXScroll"),
+ _segMan->findObjectByName("oAudioScroll") };
+ const int16 values[] = { musicVolume, sfxVolume, speechVolume };
+ for (int i = 0; i < ARRAYSIZE(sliders); ++i) {
+ const reg_t sliderId = sliders[i];
+ if (!sliderId.isNull()) {
+ reg_t params[] = { make_reg(0, values[i]) };
+ invokeSelector(sliderId, SELECTOR(setPos), 1, params);
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Talk speed sync
+
+void GuestAdditions::syncTextSpeedFromScummVM() const {
+ const int16 textSpeed = 8 - (ConfMan.getInt("talkspeed") + 1) * 8 / 255;
+
+ _state->variables[VAR_GLOBAL][kGlobalVarTextSpeed] = make_reg(0, textSpeed);
+
+ if (g_sci->getGameId() == GID_GK1) {
+ const reg_t textBarId = _segMan->findObjectByName("textBar");
+ if (!textBarId.isNull()) {
+ // Resetting the bar position to 0 causes the game to retrieve the
+ // new text speed value and re-render
+ writeSelectorValue(_segMan, textBarId, SELECTOR(position), 0);
+ }
+ }
+}
+
+void GuestAdditions::syncTextSpeedToScummVM(const int index, const reg_t value) const {
+ if (index == kGlobalVarTextSpeed) {
+ ConfMan.setInt("talkspeed", (8 - value.toSint16()) * 255 / 8);
+ }
+}
+
+#endif
+
+} // End of namespace Sci
diff --git a/engines/sci/engine/guest_additions.h b/engines/sci/engine/guest_additions.h
new file mode 100644
index 0000000000..a6875d43ad
--- /dev/null
+++ b/engines/sci/engine/guest_additions.h
@@ -0,0 +1,245 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SCI_ENGINE_GUEST_ADDITIONS_H
+#define SCI_ENGINE_GUEST_ADDITIONS_H
+
+#include "sci/engine/vm_types.h"
+
+namespace Sci {
+
+struct EngineState;
+class GameFeatures;
+class SegManager;
+
+/**
+ * The GuestAdditions class hooks into the SCI virtual machine to provide
+ * enhanced interactions between the ScummVM GUI and the game engine. Currently,
+ * this enhanced functionality encompasses synchronisation of audio volumes and
+ * other audio-related settings.
+ *
+ * Some parts of the audio sync are applied as script patches using the normal
+ * ScriptPatcher mechanism. These patches are designed to prevent the game from
+ * resetting to a default volume when starting up or loading a save.
+ */
+class GuestAdditions {
+public:
+ GuestAdditions(EngineState *state, GameFeatures *features);
+
+ /**
+ * Synchronises audio volume settings from ScummVM to the game. Called
+ * whenever the ScummVM launcher is dismissed.
+ */
+ void syncSoundSettings() const;
+
+ /**
+ * Synchronises all audio settings from ScummVM to the game. Called when the
+ * game is first started, and when save games are loaded.
+ */
+ void syncAudioOptionsFromScummVM() const;
+
+ /**
+ * Clears audio settings synchronisation state.
+ */
+ void reset();
+
+private:
+ EngineState *_state;
+ GameFeatures *_features;
+ SegManager *_segMan;
+
+ /**
+ * Convenience function for invoking selectors that reduces boilerplate code
+ * required by Sci::invokeSelector.
+ */
+ void invokeSelector(const reg_t objId, const Selector selector, const int argc = 0, const StackPtr argv = nullptr) const;
+
+ /**
+ * Determines whether the current stack contains calls from audio controls
+ * that indicate a user-initiated change of audio settings.
+ */
+ bool shouldSyncAudio() const;
+
+#pragma mark -
+#pragma mark Hooks
+
+public:
+ /**
+ * Guest additions hook for SciEngine::runGame.
+ */
+ void sciEngineRunGameHook();
+
+ /**
+ * Guest additions hook for write_var.
+ */
+ void writeVarHook(const int type, const int index, const reg_t value);
+
+ /**
+ * Guest additions hook for kDoSoundMasterVolume.
+ *
+ * @returns true if the default action should be prevented
+ */
+ bool kDoSoundMasterVolumeHook(const int volume) const;
+
+#ifdef ENABLE_SCI32
+ /**
+ * Guest additions hook for send_selector.
+ */
+ void sendSelectorHook(const reg_t sendObj, Selector &selector, reg_t *argp);
+
+ /**
+ * Guest additions hook for Audio32::setVolume.
+ *
+ * @returns true if the default action should be prevented
+ */
+ bool audio32SetVolumeHook(const int16 channelIndex, const int16 volume) const;
+
+ /**
+ * Guest additions hook for kDoSoundSetVolume.
+ */
+ void kDoSoundSetVolumeHook(const reg_t soundObj, const int16 volume) const;
+#endif
+
+#pragma mark -
+#pragma mark Message type sync
+
+private:
+ /**
+ * true if the message type (text/speech/text+speech) has been synchronised
+ * from ScummVM to the game.
+ */
+ bool _messageTypeSynced;
+
+ /**
+ * Synchronises the message type (speech/text/speech+text) from a ScummVM to
+ * a game.
+ */
+ void syncMessageTypeFromScummVM() const;
+
+ void syncMessageTypeFromScummVMUsingDefaultStrategy() const;
+#ifdef ENABLE_SCI32
+ void syncMessageTypeFromScummVMUsingShiversStrategy() const;
+ void syncMessageTypeFromScummVMUsingLSL6HiresStrategy() const;
+#endif
+
+ /**
+ * Synchronises the message type (speech/text/speech+text) from a game to
+ * ScummVM.
+ */
+ void syncMessageTypeToScummVM(const int index, const reg_t value);
+
+ void syncMessageTypeToScummVMUsingDefaultStrategy(const int index, const reg_t value);
+#ifdef ENABLE_SCI32
+ void syncMessageTypeToScummVMUsingShiversStrategy(const int index, const reg_t value);
+ void syncMessageTypeToScummVMUsingLSL6HiresStrategy(const reg_t sendObj, Selector &selector, reg_t *argp);
+#endif
+
+#pragma mark -
+#pragma mark Master volume sync
+
+private:
+ /**
+ * Synchronises audio volume settings from ScummVM to the game, for games
+ * that do not store volume themselves and just call to the kernel.
+ */
+ void syncMasterVolumeFromScummVM() const;
+
+ /**
+ * Synchronises audio volume settings from the game to ScummVM, for games
+ * that do not store volume themselves and just call to the kernel.
+ */
+ void syncMasterVolumeToScummVM(const int16 masterVolume) const;
+
+#ifdef ENABLE_SCI32
+#pragma mark -
+#pragma mark Globals volume sync
+
+private:
+ /**
+ * Synchronises audio volume settings from ScummVM to the game, for games
+ * that store volumes in globals.
+ */
+ void syncAudioVolumeGlobalsFromScummVM() const;
+
+ /**
+ * Synchronises audio volume settings from ScummVM to GK1 at game startup
+ * time.
+ */
+ void syncGK1StartupVolumeFromScummVM(const int index, const reg_t value) const;
+
+ void syncGK1VolumeFromScummVM(const int16 musicVolume, const int16 dacVolume) const;
+ void syncGK2VolumeFromScummVM(const int16 musicVolume) const;
+ void syncLSL6HiresVolumeFromScummVM(const int16 musicVolume) const;
+ void syncTorinVolumeFromScummVM(const int16 musicVolume, const int16 sfxVolume, const int16 speechVolume) const;
+
+ /**
+ * Synchronises audio volume settings from a game to ScummVM, for games
+ * that store volumes in globals.
+ */
+ void syncAudioVolumeGlobalsToScummVM(const int index, const reg_t value) const;
+
+ /**
+ * Synchronises audio volume settings from GK1 to ScummVM.
+ */
+ void syncGK1AudioVolumeToScummVM(const reg_t soundObj, const int16 volume) const;
+
+#pragma mark -
+#pragma mark Audio UI sync
+
+private:
+ /**
+ * Synchronises the in-game control panel UI in response to a change of
+ * volume from the ScummVM GUI. The values of the volume parameters passed
+ * to this function are game-specific.
+ */
+ void syncInGameUI(const int16 musicVolume, const int16 sfxVolume) const;
+
+ void syncGK1UI() const;
+ void syncGK2UI() const;
+ void syncLSL6HiresUI(const int16 musicVolume) const;
+ void syncPhant1UI(const int16 oldMusicVolume, const int16 musicVolume, reg_t &musicGlobal, const int16 oldDacVolume, const int16 dacVolume, reg_t &dacGlobal) const;
+ void syncPQ4UI(const int16 musicVolume) const;
+ void syncPQSWATUI() const;
+ void syncQFG4UI(const int16 musicVolume) const;
+ void syncShivers1UI(const int16 dacVolume) const;
+ void syncSQ6UI() const;
+ void syncTorinUI(const int16 musicVolume, const int16 sfxVolume, const int16 speechVolume) const;
+
+#pragma mark -
+#pragma mark Talk speed sync
+
+private:
+ /**
+ * Synchronises text speed settings from ScummVM to a game.
+ */
+ void syncTextSpeedFromScummVM() const;
+
+ /**
+ * Synchronises text speed settings from a game to ScummVM.
+ */
+ void syncTextSpeedToScummVM(const int index, const reg_t value) const;
+#endif
+};
+
+} // End of namespace Sci
+
+#endif // SCI_ENGINE_GUEST_ADDITIONS_H
diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp
index 4a5e4f3df6..e7cad21d67 100644
--- a/engines/sci/engine/ksound.cpp
+++ b/engines/sci/engine/ksound.cpp
@@ -46,7 +46,7 @@ reg_t kDoSound(EngineState *s, int argc, reg_t *argv) {
error("not supposed to call this");
}
-#define CREATE_DOSOUND_FORWARD(_name_) reg_t k##_name_(EngineState *s, int argc, reg_t *argv) { return g_sci->_soundCmd->k##_name_(argc, argv, s->r_acc); }
+#define CREATE_DOSOUND_FORWARD(_name_) reg_t k##_name_(EngineState *s, int argc, reg_t *argv) { return g_sci->_soundCmd->k##_name_(s, argc, argv); }
CREATE_DOSOUND_FORWARD(DoSoundInit)
CREATE_DOSOUND_FORWARD(DoSoundPlay)
@@ -77,21 +77,21 @@ reg_t kDoSoundPhantasmagoriaMac(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
case 0:
- return g_sci->_soundCmd->kDoSoundMasterVolume(argc - 1, argv + 1, s->r_acc);
+ return g_sci->_soundCmd->kDoSoundMasterVolume(s, argc - 1, argv + 1);
case 2:
- return g_sci->_soundCmd->kDoSoundInit(argc - 1, argv + 1, s->r_acc);
+ return g_sci->_soundCmd->kDoSoundInit(s, argc - 1, argv + 1);
case 3:
- return g_sci->_soundCmd->kDoSoundDispose(argc - 1, argv + 1, s->r_acc);
+ return g_sci->_soundCmd->kDoSoundDispose(s, argc - 1, argv + 1);
case 4:
- return g_sci->_soundCmd->kDoSoundPlay(argc - 1, argv + 1, s->r_acc);
+ return g_sci->_soundCmd->kDoSoundPlay(s, argc - 1, argv + 1);
case 5:
- return g_sci->_soundCmd->kDoSoundStop(argc - 1, argv + 1, s->r_acc);
+ return g_sci->_soundCmd->kDoSoundStop(s, argc - 1, argv + 1);
case 8:
- return g_sci->_soundCmd->kDoSoundSetVolume(argc - 1, argv + 1, s->r_acc);
+ return g_sci->_soundCmd->kDoSoundSetVolume(s, argc - 1, argv + 1);
case 9:
- return g_sci->_soundCmd->kDoSoundSetLoop(argc - 1, argv + 1, s->r_acc);
+ return g_sci->_soundCmd->kDoSoundSetLoop(s, argc - 1, argv + 1);
case 10:
- return g_sci->_soundCmd->kDoSoundUpdateCues(argc - 1, argv + 1, s->r_acc);
+ return g_sci->_soundCmd->kDoSoundUpdateCues(s, argc - 1, argv + 1);
}
error("Unknown kDoSound Phantasmagoria Mac subop %d", argv[0].toUint16());
diff --git a/engines/sci/engine/object.h b/engines/sci/engine/object.h
index 61f942c04a..dca7e97e81 100644
--- a/engines/sci/engine/object.h
+++ b/engines/sci/engine/object.h
@@ -179,6 +179,10 @@ public:
_infoSelectorSci3 &= ~flag;
}
}
+
+ bool isInserted() const {
+ return getInfoSelector().toUint16() & kInfoFlagViewInserted;
+ }
#endif
reg_t getNameSelector() const {
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index f5ec2d300c..97e53a0742 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -1079,12 +1079,29 @@ static const uint16 gk2InvScrollPatch[] = {
PATCH_END
};
+// The init code that runs when GK2 starts up unconditionally resets the
+// music volume to 63, but the game should always use the volume stored in
+// ScummVM.
+// Applies to at least: English 1.00 CD
+static const uint16 gk2VolumeResetSignature[] = {
+ SIG_MAGICDWORD,
+ 0x35, 0x3f, // ldi $3f
+ 0xa1, 0x4c, // sag $4c (music volume)
+ SIG_END
+};
+
+static const uint16 gk2VolumeResetPatch[] = {
+ 0x33, 0x02, // jmp 2 [past volume changes]
+ PATCH_END
+};
+
// script, description, signature patch
static const SciScriptPatcherEntry gk2Signatures[] = {
+ { true, 0, "disable volume reset on startup", 1, gk2VolumeResetSignature, gk2VolumeResetPatch },
+ { true, 23, "inventory starts scroll down in the wrong direction", 1, gk2InvScrollSignature, gk2InvScrollPatch },
{ true, 64990, "increase number of save games", 1, sci2NumSavesSignature1, sci2NumSavesPatch1 },
{ true, 64990, "increase number of save games", 1, sci2NumSavesSignature2, sci2NumSavesPatch2 },
{ true, 64990, "disable change directory button", 1, sci2ChangeDirSignature, sci2ChangeDirPatch },
- { true, 23, "inventory starts scroll down in the wrong direction", 1, gk2InvScrollSignature, gk2InvScrollPatch },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -2295,8 +2312,30 @@ static const uint16 larry6HiresPatchSetScale[] = {
PATCH_END
};
+// The init code that runs when LSL6hires starts up unconditionally resets the
+// master music volume to 12 (and the volume dial to 11), but the game should
+// always use the volume stored in ScummVM.
+// Applies to at least: English CD
+static const uint16 larry6HiresSignatureVolumeReset[] = {
+ SIG_MAGICDWORD,
+ 0x38, SIG_UINT16(0x221), // pushi $221 (masterVolume)
+ 0x78, // push1
+ 0x39, 0x0c, // push $0c
+ 0x81, 0x01, // lag $01
+ 0x4a, SIG_UINT16(0x06), // send $6
+ 0x35, 0x0b, // ldi $0b
+ 0xa1, 0xc2, // sag $c2
+ SIG_END
+};
+
+static const uint16 larry6HiresPatchVolumeReset[] = {
+ 0x32, PATCH_UINT16(12), // jmp 12 [past volume changes]
+ PATCH_END
+};
+
// script, description, signature patch
static const SciScriptPatcherEntry larry6HiresSignatures[] = {
+ { true, 71, "disable volume reset on startup", 1, larry6HiresSignatureVolumeReset, larry6HiresPatchVolumeReset },
{ true, 270, "fix incorrect setScale call", 1, larry6HiresSignatureSetScale, larry6HiresPatchSetScale },
{ true, 64990, "increase number of save games", 1, sci2NumSavesSignature1, sci2NumSavesPatch1 },
{ true, 64990, "increase number of save games", 1, sci2NumSavesSignature2, sci2NumSavesPatch2 },
@@ -3151,9 +3190,42 @@ static const SciScriptPatcherEntry mothergooseHiresSignatures[] = {
#pragma mark -
#pragma mark Phantasmagoria
+// Phantasmagoria persists audio volumes in the save games, but ScummVM manages
+// game volumes through the launcher, so stop the game from overwriting the
+// ScummVM volumes with volumes from save games.
+// Applies to at least: English CD
+static const uint16 phant1SignatureSavedVolume[] = {
+ 0x7a, // push2
+ 0x39, 0x08, // pushi 8
+ 0x38, SIG_UINT16(0x20b), // push $20b (readWord)
+ 0x76, // push0
+ 0x72, SIG_UINT16(0x13c), // lofsa $13c (PREF.DAT)
+ 0x4a, SIG_UINT16(0x04), // send 4
+ SIG_MAGICDWORD,
+ 0xa1, 0xbc, // sag $bc
+ 0x36, // push
+ 0x43, 0x76, SIG_UINT16(0x04), // callk DoAudio[76], 4
+ 0x7a, // push2
+ 0x76, // push0
+ 0x38, SIG_UINT16(0x20b), // push $20b (readWord)
+ 0x76, // push0
+ 0x72, SIG_UINT16(0x13c), // lofsa $13c (PREF.DAT)
+ 0x4a, SIG_UINT16(0x04), // send 4
+ 0xa1, 0xbb, // sag $bb
+ 0x36, // push
+ 0x43, 0x75, SIG_UINT16(0x04), // callk DoSound[75], 4
+ SIG_END
+};
+
+static const uint16 phant1PatchSavedVolume[] = {
+ 0x32, PATCH_UINT16(36), // jmp [to prefFile::close]
+ PATCH_END
+};
+
// script, description, signature patch
static const SciScriptPatcherEntry phantasmagoriaSignatures[] = {
{ true, 901, "invalid array construction", 1, sci21IntArraySignature, sci21IntArrayPatch },
+ { true, 1111, "ignore audio settings from save game", 1, phant1SignatureSavedVolume, phant1PatchSavedVolume },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -3312,6 +3384,50 @@ static const SciScriptPatcherEntry pq4Signatures[] = {
SCI_SIGNATUREENTRY_TERMINATOR
};
+#pragma mark -
+#pragma mark Police Quest: SWAT
+
+// The init code that runs when PQ:SWAT starts up unconditionally resets the
+// master sound volume to 127, but the game should always use the volume stored
+// in ScummVM.
+// Applies to at least: English CD
+static const uint16 pqSwatSignatureVolumeReset1[] = {
+ SIG_MAGICDWORD,
+ 0x38, SIG_UINT16(0x21a), // pushi $21a (masterVolume)
+ 0x78, // push1
+ 0x39, 0x7f, // push $7f
+ 0x54, SIG_UINT16(0x06), // self 6
+ SIG_END
+};
+
+static const uint16 pqSwatPatchVolumeReset1[] = {
+ 0x32, PATCH_UINT16(6), // jmp 6 [past volume reset]
+ PATCH_END
+};
+
+// pqInitCode::doit
+static const uint16 pqSwatSignatureVolumeReset2[] = {
+ SIG_MAGICDWORD,
+ 0x38, SIG_UINT16(0x21a), // pushi $21a (masterVolume)
+ 0x78, // push1
+ 0x39, 0x0f, // pushi $f
+ 0x81, 0x01, // lag 1
+ 0x4a, SIG_UINT16(0x06), // send 6
+ SIG_END
+};
+
+static const uint16 pqSwatPatchVolumeReset2[] = {
+ 0x32, PATCH_UINT16(8), // jmp 8 [past volume reset]
+ PATCH_END
+};
+
+// script, description, signature patch
+static const SciScriptPatcherEntry pqSwatSignatures[] = {
+ { true, 0, "disable volume reset on startup", 1, pqSwatSignatureVolumeReset1, pqSwatPatchVolumeReset1 },
+ { true, 1, "disable volume reset on startup", 1, pqSwatSignatureVolumeReset2, pqSwatPatchVolumeReset2 },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
#endif
// ===========================================================================
@@ -4284,8 +4400,41 @@ static const SciScriptPatcherEntry qfg3Signatures[] = {
#pragma mark -
#pragma mark Quest for Glory 4
+// The init code that runs when QFG4 starts up unconditionally resets the
+// master music volume to 15, but the game should always use the volume stored
+// in ScummVM.
+// Applies to at least: English floppy
+static const uint16 qfg4SignatureVolumeReset[] = {
+ SIG_MAGICDWORD,
+ 0x38, SIG_UINT16(0x215), // pushi $215 (masterVolume)
+ 0x78, // push1
+ 0x39, 0x0f, // pushi $f
+ 0x81, 0x01, // lag 1 (Glory object)
+ 0x4a, SIG_UINT16(0x06), // send 6
+ SIG_END
+};
+
+// Same as above, but with a different masterVolume selector.
+// Applies to at least: English CD
+static const uint16 qfg4CDSignatureVolumeReset[] = {
+ SIG_MAGICDWORD,
+ 0x38, SIG_UINT16(0x217), // pushi $217 (masterVolume)
+ 0x78, // push1
+ 0x39, 0x0f, // pushi $f
+ 0x81, 0x01, // lag 1 (Glory object)
+ 0x4a, SIG_UINT16(0x06), // send 6
+ SIG_END
+};
+
+static const uint16 qfg4PatchVolumeReset[] = {
+ 0x32, PATCH_UINT16(8), // jmp 8 [past volume changes]
+ PATCH_END
+};
+
// script, description, signature patch
static const SciScriptPatcherEntry qfg4Signatures[] = {
+ { true, 1, "disable volume reset on startup (floppy)", 1, qfg4SignatureVolumeReset, qfg4PatchVolumeReset },
+ { true, 1, "disable volume reset on startup (CD)", 1, qfg4CDSignatureVolumeReset, qfg4PatchVolumeReset },
{ true, 64990, "increase number of save games", 1, sci2NumSavesSignature1, sci2NumSavesPatch1 },
{ true, 64990, "increase number of save games", 1, sci2NumSavesSignature2, sci2NumSavesPatch2 },
{ true, 64990, "disable change directory button", 1, sci2ChangeDirSignature, sci2ChangeDirPatch },
@@ -4948,8 +5097,57 @@ static const SciScriptPatcherEntry sq6Signatures[] = {
#pragma mark -
#pragma mark Torins Passage
+// The init code that runs when Torin starts up unconditionally resets the
+// master music volume to defaults, but the game should always use the volume
+// stored in ScummVM.
+// Applies to at least: English CD
+static const uint16 torinVolumeResetSignature1[] = {
+ SIG_MAGICDWORD,
+ 0x35, 0x28, // ldi $28
+ 0xa1, 0xe3, // sag $e3 (music volume)
+ 0x35, 0x3c, // ldi $3c
+ 0xa1, 0xe4, // sag $e4 (sfx volume)
+ 0x35, 0x64, // ldi $64
+ 0xa1, 0xe5, // sag $e5 (speech volume)
+ SIG_END
+};
+
+static const uint16 torinVolumeResetPatch1[] = {
+ 0x33, 0x0a, // jmp [past volume resets]
+ PATCH_END
+};
+
+// The init code that runs when Torin starts up unconditionally resets the
+// master music volume to values stored in torin.prf, but the game should always
+// use the volume stored in ScummVM.
+// Applies to at least: English CD
+static const uint16 torinVolumeResetSignature2[] = {
+ SIG_MAGICDWORD,
+ 0x38, SIG_UINT16(0x20b), // pushi $020b
+ 0x76, // push0
+ SIG_ADDTOOFFSET(6), // advance file stream
+ 0xa1, 0xe3, // sag $e3 (music volume)
+ SIG_ADDTOOFFSET(10), // advance file stream
+ 0xa1, 0xe4, // sag $e4 (sfx volume)
+ SIG_ADDTOOFFSET(10), // advance file stream
+ 0xa1, 0xe5, // sag $e5 (speech volume)
+ SIG_END
+};
+
+static const uint16 torinVolumeResetPatch2[] = {
+ PATCH_ADDTOOFFSET(10), // advance file stream
+ 0x18, 0x18, // waste bytes
+ PATCH_ADDTOOFFSET(10), // advance file stream
+ 0x18, 0x18, // waste bytes
+ PATCH_ADDTOOFFSET(10), // advance file stream
+ 0x18, 0x18, // waste bytes
+ PATCH_END
+};
+
// script, description, signature patch
static const SciScriptPatcherEntry torinSignatures[] = {
+ { true, 64000, "disable volume reset on startup 1/2", 1, torinVolumeResetSignature1, torinVolumeResetPatch1 },
+ { true, 64000, "disable volume reset on startup 2/2", 1, torinVolumeResetSignature2, torinVolumeResetPatch2 },
{ true, 64990, "increase number of save games", 1, sci2NumSavesSignature1, sci2NumSavesPatch1 },
{ true, 64990, "increase number of save games", 1, sci2NumSavesSignature2, sci2NumSavesPatch2 },
SCI_SIGNATUREENTRY_TERMINATOR
@@ -5469,6 +5667,9 @@ void ScriptPatcher::processScript(uint16 scriptNr, SciSpan<byte> scriptData) {
case GID_PQ4:
signatureTable = pq4Signatures;
break;
+ case GID_PQSWAT:
+ signatureTable = pqSwatSignatures;
+ break;
#endif
case GID_QFG1:
signatureTable = qfg1egaSignatures;
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index 2bc4051a79..8a1a9e76d4 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -202,6 +202,20 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(magnifier);
FIND_SELECTOR(frameOut);
FIND_SELECTOR(casts);
+ FIND_SELECTOR(setVol);
+ FIND_SELECTOR(reSyncVol);
+ FIND_SELECTOR(set);
+ FIND_SELECTOR(clear);
+ FIND_SELECTOR(curPos);
+ FIND_SELECTOR(update);
+ FIND_SELECTOR(show);
+ FIND_SELECTOR(position);
+ FIND_SELECTOR(musicVolume);
+ FIND_SELECTOR(soundVolume);
+ FIND_SELECTOR(initialOff);
+ FIND_SELECTOR(setPos);
+ FIND_SELECTOR(setSize);
+ FIND_SELECTOR(displayValue);
#endif
}
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index 8d1edeb489..d8c0bf711b 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -158,6 +158,20 @@ struct SelectorCache {
Selector magnifier;
Selector frameOut;
Selector casts; // needed for sync'ing screen items/planes with scripts, when our save/restore code is patched in (see GfxFrameout::syncWithScripts)
+ Selector setVol; // for GK2 volume sync on restore
+ Selector reSyncVol; // for Torin volume sync on restore
+ Selector set; // for LSL6hires subtitle sync
+ Selector clear; // for LSL6hires subtitle sync
+ Selector curPos; // for LSL6hires volume sync
+ Selector update; // for LSL6hires volume sync
+ Selector show; // for GK1 volume sync
+ Selector position; // for GK1 volume sync
+ Selector musicVolume; // for GK1 volume sync
+ Selector soundVolume; // for GK1 volume sync
+ Selector initialOff; // for GK2 volume sync
+ Selector setPos; // for Torin volume sync
+ Selector setSize; // for PQ4 volume sync
+ Selector displayValue; // for PQ:SWAT volume sync
#endif
};
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index 76df322b30..6e2229727f 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -19,14 +19,12 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
-
#include "common/system.h"
#include "sci/sci.h" // for INCLUDE_OLDGFX
#include "sci/debug.h" // for g_debug_sleeptime_factor
-#include "sci/event.h"
-
#include "sci/engine/file.h"
+#include "sci/engine/guest_additions.h"
#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
@@ -84,6 +82,8 @@ void EngineState::reset(bool isRestoring) {
_memorySegmentSize = 0;
_fileHandles.resize(5);
abortScriptProcessing = kAbortNone;
+ } else {
+ g_sci->_guestAdditions->reset();
}
// reset delayed restore game functionality
@@ -120,7 +120,6 @@ void EngineState::reset(bool isRestoring) {
scriptGCInterval = GC_INTERVAL;
_videoState.reset();
- _syncedAudioOptions = false;
}
void EngineState::speedThrottler(uint32 neededSleep) {
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index 5297a176d3..dd6b728716 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -214,7 +214,6 @@ public:
// TODO: Excise video code from the state manager
VideoState _videoState;
- bool _syncedAudioOptions;
/**
* Resets the engine state.
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 3535b278c0..7e514a2dc6 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -27,7 +27,13 @@
#include "sci/sci.h"
#include "sci/console.h"
#include "sci/resource.h"
+#ifdef ENABLE_SCI32
+#include "audio/mixer.h"
+#include "sci/sound/audio32.h"
+#include "sci/sound/music.h"
+#endif
#include "sci/engine/features.h"
+#include "sci/engine/guest_additions.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#include "sci/engine/object.h"
@@ -199,36 +205,7 @@ static void write_var(EngineState *s, int type, int index, reg_t value) {
s->variables[type][index] = value;
-#ifdef ENABLE_SCI32
- if (type == VAR_GLOBAL && getSciVersion() >= SCI_VERSION_2 && g_sci->getEngineState()->_syncedAudioOptions) {
-
- switch (g_sci->getGameId()) {
- case GID_LSL6HIRES:
- if (index == kGlobalVarLSL6HiresTextSpeed) {
- ConfMan.setInt("talkspeed", (14 - value.toSint16()) * 255 / 13);
- }
- break;
- default:
- if (index == kGlobalVarTextSpeed) {
- ConfMan.setInt("talkspeed", (8 - value.toSint16()) * 255 / 8);
- }
- }
- }
-#endif
-
- if (type == VAR_GLOBAL && index == kGlobalVarMessageType) {
- // The game is trying to change its speech/subtitle settings
- if (!g_sci->getEngineState()->_syncedAudioOptions || s->variables[VAR_GLOBAL][kGlobalVarQuit] == TRUE_REG) {
- // ScummVM audio options haven't been applied yet, so apply them.
- // We also force the ScummVM audio options when loading a game from
- // the launcher.
- g_sci->syncIngameAudioOptions();
- g_sci->getEngineState()->_syncedAudioOptions = true;
- } else {
- // Update ScummVM's audio options
- g_sci->updateScummVMAudioOptions();
- }
- }
+ g_sci->_guestAdditions->writeVarHook(type, index, value);
}
}
@@ -311,6 +288,10 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
if (argc > 0x800) // More arguments than the stack could possibly accomodate for
error("send_selector(): More than 0x800 arguments to function call");
+#ifdef ENABLE_SCI32
+ g_sci->_guestAdditions->sendSelectorHook(send_obj, selector, argp);
+#endif
+
SelectorType selectorType = lookupSelector(s->_segMan, send_obj, selector, &varp, &funcp);
if (selectorType == kSelectorNone)
error("Send to invalid selector 0x%x of object at %04x:%04x", 0xffff & selector, PRINT_REG(send_obj));
diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h
index 0444cf5ffc..1ac6fed0b6 100644
--- a/engines/sci/engine/vm.h
+++ b/engines/sci/engine/vm.h
@@ -142,16 +142,32 @@ enum GlobalVar {
kGlobalVarCurrentRoom = 2,
kGlobalVarSpeed = 3, // SCI16
kGlobalVarQuit = 4,
+ kGlobalVarSounds = 8,
kGlobalVarPlanes = 10, // SCI32
kGlobalVarCurrentRoomNo = 11,
kGlobalVarPreviousRoomNo = 12,
kGlobalVarNewRoomNo = 13,
kGlobalVarScore = 15,
+ kGlobalVarGK2MusicVolume = 76, // 0 to 127
kGlobalVarFastCast = 84, // SCI16
kGlobalVarMessageType = 90,
kGlobalVarTextSpeed = 94, // SCI32; 0 is fastest, 8 is slowest
- kGlobalVarLSL6HiresTextSpeed = 167, // 1 is fastest, 14 is slowest
- kGlobalVarShivers1Score = 349
+ kGlobalVarGK1Music1 = 102, // 0 to 127
+ kGlobalVarGK1Music2 = 103, // 0 to 127
+ kGlobalVarLSL6HiresGameFlags = 137,
+ kGlobalVarGK1NarratorMode = 166, // 0 for text, 1 for speech
+ kGlobalVarPhant1MusicVolume = 187, // 0 to 15
+ kGlobalVarPhant1DACVolume = 188, // 0 to 127
+ kGlobalVarLSL6HiresMusicVolume = 194, // 0 to 13
+ kGlobalVarGK1DAC1 = 207, // 0 to 127
+ kGlobalVarGK1DAC2 = 208, // 0 to 127
+ kGlobalVarLSL6HiresRestoreTextWindow = 210,
+ kGlobalVarGK1DAC3 = 211, // 0 to 127
+ kGlobalVarShiversFlags = 211,
+ kGlobalVarTorinMusicVolume = 227, // 0 to 100
+ kGlobalVarTorinSFXVolume = 228, // 0 to 100
+ kGlobalVarTorinSpeechVolume = 229, // 0 to 100
+ kGlobalVarShivers1Score = 349
};
/** Number of kernel calls in between gcs; should be < 50000 */
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index eb2c6a148b..7decff6e7a 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -12,6 +12,7 @@ MODULE_OBJS := \
engine/features.o \
engine/file.o \
engine/gc.o \
+ engine/guest_additions.o \
engine/kernel.o \
engine/kevent.o \
engine/kfile.o \
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 34930a648e..c8e500b700 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -33,6 +33,7 @@
#include "sci/event.h"
#include "sci/engine/features.h"
+#include "sci/engine/guest_additions.h"
#include "sci/engine/message.h"
#include "sci/engine/object.h"
#include "sci/engine/state.h"
@@ -94,6 +95,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam
_video32 = nullptr;
_gfxCursor32 = nullptr;
#endif
+ _guestAdditions = nullptr;
_features = 0;
_resMan = 0;
_gamestate = 0;
@@ -202,6 +204,7 @@ SciEngine::~SciEngine() {
delete _kernel;
delete _vocabulary;
delete _console;
+ delete _guestAdditions;
delete _features;
delete _gfxMacIconBar;
@@ -280,6 +283,7 @@ Common::Error SciEngine::run() {
_vocabulary = new Vocabulary(_resMan, false);
_gamestate = new EngineState(segMan);
+ _guestAdditions = new GuestAdditions(_gamestate, _features);
_eventMan = new EventManager(_resMan->detectFontExtended());
#ifdef ENABLE_SCI32
if (getSciVersion() >= SCI_VERSION_2_1_EARLY) {
@@ -331,7 +335,7 @@ Common::Error SciEngine::run() {
_soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, _features->detectDoSoundType());
syncSoundSettings();
- syncIngameAudioOptions();
+ _guestAdditions->syncAudioOptionsFromScummVM();
// Patch in our save/restore code, so that dialogs are replaced
patchGameSaveRestore();
@@ -834,14 +838,14 @@ void SciEngine::runGame() {
if (DebugMan.isDebugChannelEnabled(kDebugLevelOnStartup))
_console->attach();
- _gamestate->_syncedAudioOptions = false;
+ _guestAdditions->reset();
do {
_gamestate->_executionStackPosChanged = false;
run_vm(_gamestate);
exitGame();
- _gamestate->_syncedAudioOptions = true;
+ _guestAdditions->sciEngineRunGameHook();
if (_gamestate->abortScriptProcessing == kAbortRestartGame) {
_gamestate->_segMan->resetSegMan();
@@ -854,7 +858,7 @@ void SciEngine::runGame() {
if (_gfxMenu)
_gfxMenu->reset();
_gamestate->abortScriptProcessing = kAbortNone;
- _gamestate->_syncedAudioOptions = false;
+ _guestAdditions->reset();
} else if (_gamestate->abortScriptProcessing == kAbortLoadGame) {
_gamestate->abortScriptProcessing = kAbortNone;
_gamestate->_executionStack.clear();
@@ -865,8 +869,7 @@ void SciEngine::runGame() {
_gamestate->abortScriptProcessing = kAbortNone;
syncSoundSettings();
- syncIngameAudioOptions();
- // Games do not set their audio settings when loading
+ _guestAdditions->syncAudioOptionsFromScummVM();
} else {
break; // exit loop
}
@@ -1055,150 +1058,7 @@ void SciEngine::pauseEngineIntern(bool pause) {
void SciEngine::syncSoundSettings() {
Engine::syncSoundSettings();
-
- bool mute = false;
- if (ConfMan.hasKey("mute"))
- mute = ConfMan.getBool("mute");
-
- int soundVolumeMusic = (mute ? 0 : ConfMan.getInt("music_volume"));
-
- if (_gamestate && _soundCmd) {
- int vol = (soundVolumeMusic + 1) * MUSIC_MASTERVOLUME_MAX / Audio::Mixer::kMaxMixerVolume;
- _soundCmd->setMasterVolume(vol);
- }
-}
-
-void SciEngine::syncIngameAudioOptions() {
- bool useGlobal90 = false;
-
- // Sync the in-game speech/subtitles settings for SCI1.1 CD games
- if (isCD()) {
- switch (getSciVersion()) {
- case SCI_VERSION_1_1:
- // All SCI1.1 CD games use global 90
- useGlobal90 = true;
- break;
-#ifdef ENABLE_SCI32
- case SCI_VERSION_2:
- case SCI_VERSION_2_1_EARLY:
- case SCI_VERSION_2_1_MIDDLE:
- case SCI_VERSION_2_1_LATE:
- // Only use global 90 for some specific games, not all SCI32 games used this method
- switch (_gameId) {
- case GID_KQ7: // SCI2.1
- case GID_GK1: // SCI2
- case GID_GK2: // SCI2.1
- case GID_SQ6: // SCI2.1
- case GID_TORIN: // SCI2.1
- case GID_QFG4: // SCI2.1
- case GID_PQ4: // SCI2
- case GID_PHANTASMAGORIA: // SCI2.1
- case GID_MOTHERGOOSEHIRES: // SCI2.1
- useGlobal90 = true;
- break;
- case GID_LSL6: // SCI2.1
- // TODO: Uses gameFlags array
- break;
- // Shivers does not use global 90
- // Police Quest: SWAT does not use global 90
- //
- // TODO: Unknown at the moment:
- // LSL7, Lighthouse, RAMA, Phantasmagoria 2
- default:
- return;
- }
- break;
-#endif // ENABLE_SCI32
- default:
- return;
- }
-
- bool subtitlesOn = ConfMan.getBool("subtitles");
- bool speechOn = !ConfMan.getBool("speech_mute");
-
-#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2) {
- GlobalVar index;
- uint16 textSpeed;
-
- switch (g_sci->getGameId()) {
- case GID_LSL6HIRES:
- index = kGlobalVarLSL6HiresTextSpeed;
- textSpeed = 14 - ConfMan.getInt("talkspeed") * 14 / 255 + 1;
- break;
- default:
- index = kGlobalVarTextSpeed;
- textSpeed = 8 - ConfMan.getInt("talkspeed") * 8 / 255;
- }
-
- _gamestate->variables[VAR_GLOBAL][index] = make_reg(0, textSpeed);
- }
-#endif
-
- if (useGlobal90) {
- if (subtitlesOn && !speechOn) {
- _gamestate->variables[VAR_GLOBAL][kGlobalVarMessageType] = make_reg(0, 1); // subtitles
- } else if (!subtitlesOn && speechOn) {
- _gamestate->variables[VAR_GLOBAL][kGlobalVarMessageType] = make_reg(0, 2); // speech
- } else if (subtitlesOn && speechOn) {
- // Is it a game that supports simultaneous speech and subtitles?
- switch (_gameId) {
- case GID_SQ4:
- case GID_FREDDYPHARKAS:
- case GID_ECOQUEST:
- case GID_LSL6:
- case GID_LAURABOW2:
- case GID_KQ6:
-#ifdef ENABLE_SCI32
- // Unsure about Gabriel Knight 2
- case GID_KQ7: // SCI2.1
- case GID_GK1: // SCI2
- case GID_SQ6: // SCI2.1, SQ6 seems to always use subtitles anyway
- case GID_TORIN: // SCI2.1
- case GID_QFG4: // SCI2.1
- case GID_PQ4: // SCI2
- // Phantasmagoria does not support simultaneous speech + subtitles
- // Mixed Up Mother Goose Deluxe does not support simultaneous speech + subtitles
-#endif // ENABLE_SCI32
- _gamestate->variables[VAR_GLOBAL][kGlobalVarMessageType] = make_reg(0, 3); // speech + subtitles
- break;
- default:
- // Game does not support speech and subtitles, set it to speech
- _gamestate->variables[VAR_GLOBAL][kGlobalVarMessageType] = make_reg(0, 2); // speech
- }
- }
- }
- }
-}
-
-void SciEngine::updateScummVMAudioOptions() {
- // Update ScummVM's speech/subtitles settings for SCI1.1 CD games,
- // depending on the in-game settings
- if ((isCD() && getSciVersion() == SCI_VERSION_1_1) ||
- getSciVersion() >= SCI_VERSION_2) {
-
- uint16 ingameSetting = _gamestate->variables[VAR_GLOBAL][kGlobalVarMessageType].getOffset();
-
- switch (ingameSetting) {
- case 1:
- // subtitles
- ConfMan.setBool("subtitles", true);
- ConfMan.setBool("speech_mute", true);
- break;
- case 2:
- // speech
- ConfMan.setBool("subtitles", false);
- ConfMan.setBool("speech_mute", false);
- break;
- case 3:
- // speech + subtitles
- ConfMan.setBool("subtitles", true);
- ConfMan.setBool("speech_mute", false);
- break;
- default:
- break;
- }
- }
+ _guestAdditions->syncSoundSettings();
}
void SciEngine::loadMacExecutable() {
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index f5ccc19d7c..744a1b4aed 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -62,6 +62,7 @@ class Vocabulary;
class ResourceManager;
class Kernel;
class GameFeatures;
+class GuestAdditions;
class Console;
class AudioPlayer;
class SoundCommandParser;
@@ -263,28 +264,6 @@ public:
uint32 getTickCount();
void setTickCount(const uint32 ticks);
- /**
- * Syncs the audio options of the ScummVM launcher (speech, subtitles or
- * both) with the in-game audio options of certain CD game versions. For
- * some games, this allows simultaneous playing of speech and subtitles,
- * even if the original games didn't support this feature.
- *
- * SCI1.1 games which support simultaneous speech and subtitles:
- * - EcoQuest 1 CD
- * - Leisure Suit Larry 6 CD
- * SCI1.1 games which don't support simultaneous speech and subtitles,
- * and we add this functionality in ScummVM:
- * - Space Quest 4 CD
- * - Freddy Pharkas CD
- * - Laura Bow 2 CD
- * SCI1.1 games which don't support simultaneous speech and subtitles,
- * and we haven't added any extra functionality in ScummVM because extra
- * script patches are needed:
- * - King's Quest 6 CD
- */
- void syncIngameAudioOptions();
- void updateScummVMAudioOptions();
-
const SciGameId &getGameId() const { return _gameId; }
const char *getGameIdStr() const;
int getResourceVersion() const;
@@ -398,6 +377,7 @@ public:
Sync *_sync;
SoundCommandParser *_soundCmd;
GameFeatures *_features;
+ GuestAdditions *_guestAdditions;
opcode_format (*_opcode_formats)[4];
diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index 69a83ebc8e..e9e90e41a7 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -36,6 +36,8 @@
#include "common/types.h" // for Flag::NO
#include "engine.h" // for Engine, g_engine
#include "sci/engine/features.h" // for GameFeatures
+#include "sci/engine/guest_additions.h" // for GuestAdditions
+#include "sci/engine/state.h" // for EngineState
#include "sci/engine/vm_types.h" // for reg_t, make_reg, NULL_REG
#include "sci/resource.h" // for ResourceId, ResourceType::kResour...
#include "sci/sci.h" // for SciEngine, g_sci, getSciVersion
@@ -118,6 +120,9 @@ Audio32::Audio32(ResourceManager *resMan) :
}
_useModifiedAttenuation = g_sci->_features->usesModifiedAudioAttenuation();
+ // The mixer stream type is given as `kSFXSoundType` so that audio from
+ // Audio32 will be mixed at the same standard volume as the video players
+ // (which must use `kSFXSoundType` as well).
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
}
@@ -980,7 +985,7 @@ reg_t Audio32::kernelPlay(const bool autoPlay, const int argc, const reg_t *cons
int16 Audio32::getVolume(const int16 channelIndex) const {
if (channelIndex < 0 || channelIndex >= _numActiveChannels) {
- return _mixer->getChannelVolume(_handle) * kMaxVolume / Audio::Mixer::kMaxChannelVolume;
+ return (_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) + 1) * kMaxVolume / Audio::Mixer::kMaxMixerVolume;
}
Common::StackLock lock(_mutex);
@@ -990,10 +995,9 @@ int16 Audio32::getVolume(const int16 channelIndex) const {
void Audio32::setVolume(const int16 channelIndex, int16 volume) {
volume = MIN<int16>(kMaxVolume, volume);
if (channelIndex == kAllChannels) {
- ConfMan.setInt("sfx_volume", volume * Audio::Mixer::kMaxChannelVolume / kMaxVolume);
- ConfMan.setInt("speech_volume", volume * Audio::Mixer::kMaxChannelVolume / kMaxVolume);
- _mixer->setChannelVolume(_handle, volume * Audio::Mixer::kMaxChannelVolume / kMaxVolume);
- g_engine->syncSoundSettings();
+ if (!g_sci->_guestAdditions->audio32SetVolumeHook(channelIndex, volume)) {
+ setMasterVolume(volume);
+ }
} else if (channelIndex != kNoExistingChannel) {
Common::StackLock lock(_mutex);
getChannel(channelIndex).volume = volume;
diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h
index 9130cbe687..8a1d8cfb43 100644
--- a/engines/sci/sound/audio32.h
+++ b/engines/sci/sound/audio32.h
@@ -504,6 +504,13 @@ public:
}
/**
+ * Sets the master volume for digital audio playback.
+ */
+ void setMasterVolume(const int16 volume) {
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volume * Audio::Mixer::kMaxChannelVolume / kMaxVolume);
+ }
+
+ /**
* Initiate an immediate fade of the given channel.
*/
bool fadeChannel(const int16 channelIndex, const int16 targetVolume, const int16 speed, const int16 steps, const bool stopAfterFade);
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index b9a764c93a..e0cbb97d19 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -29,6 +29,7 @@
#include "sci/sound/soundcmd.h"
#include "sci/engine/features.h"
+#include "sci/engine/guest_additions.h"
#include "sci/engine/kernel.h"
#include "sci/engine/object.h"
#include "sci/engine/selector.h"
@@ -57,10 +58,10 @@ SoundCommandParser::~SoundCommandParser() {
delete _music;
}
-reg_t SoundCommandParser::kDoSoundInit(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundInit(EngineState *s, int argc, reg_t *argv) {
debugC(kDebugLevelSound, "kDoSound(init): %04x:%04x", PRINT_REG(argv[0]));
processInitSound(argv[0]);
- return acc;
+ return s->r_acc;
}
int SoundCommandParser::getSoundResourceId(reg_t obj) {
@@ -153,13 +154,13 @@ void SoundCommandParser::processInitSound(reg_t obj) {
}
}
-reg_t SoundCommandParser::kDoSoundPlay(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundPlay(EngineState *s, int argc, reg_t *argv) {
debugC(kDebugLevelSound, "kDoSound(play): %04x:%04x", PRINT_REG(argv[0]));
bool playBed = false;
if (argc >= 2 && !argv[1].isNull())
playBed = true;
processPlaySound(argv[0], playBed);
- return acc;
+ return s->r_acc;
}
void SoundCommandParser::processPlaySound(reg_t obj, bool playBed) {
@@ -225,10 +226,10 @@ void SoundCommandParser::processPlaySound(reg_t obj, bool playBed) {
musicSlot->fadeStep = 0;
}
-reg_t SoundCommandParser::kDoSoundDispose(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundDispose(EngineState *s, int argc, reg_t *argv) {
debugC(kDebugLevelSound, "kDoSound(dispose): %04x:%04x", PRINT_REG(argv[0]));
processDisposeSound(argv[0]);
- return acc;
+ return s->r_acc;
}
void SoundCommandParser::processDisposeSound(reg_t obj) {
@@ -248,10 +249,10 @@ void SoundCommandParser::processDisposeSound(reg_t obj) {
writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped);
}
-reg_t SoundCommandParser::kDoSoundStop(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundStop(EngineState *s, int argc, reg_t *argv) {
debugC(kDebugLevelSound, "kDoSound(stop): %04x:%04x", PRINT_REG(argv[0]));
processStopSound(argv[0], false);
- return acc;
+ return s->r_acc;
}
void SoundCommandParser::processStopSound(reg_t obj, bool sampleFinishedPlaying) {
@@ -282,7 +283,7 @@ void SoundCommandParser::processStopSound(reg_t obj, bool sampleFinishedPlaying)
_music->soundStop(musicSlot);
}
-reg_t SoundCommandParser::kDoSoundPause(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundPause(EngineState *s, int argc, reg_t *argv) {
if (argc == 1)
debugC(kDebugLevelSound, "kDoSound(pause): %04x:%04x", PRINT_REG(argv[0]));
else
@@ -333,7 +334,7 @@ reg_t SoundCommandParser::kDoSoundPause(int argc, reg_t *argv, reg_t acc) {
if (!musicSlot) {
// This happens quite frequently
debugC(kDebugLevelSound, "kDoSound(pause): Slot not found (%04x:%04x)", PRINT_REG(obj));
- return acc;
+ return s->r_acc;
}
#ifdef ENABLE_SCI32
@@ -354,17 +355,17 @@ reg_t SoundCommandParser::kDoSoundPause(int argc, reg_t *argv, reg_t acc) {
#endif
_music->soundToggle(musicSlot, shouldPause);
}
- return acc;
+ return s->r_acc;
}
// SCI0 only command
// It's called right after restoring a game - it's responsible to kick off playing music again
// we don't need this at all, so we don't do anything here
-reg_t SoundCommandParser::kDoSoundResumeAfterRestore(int argc, reg_t *argv, reg_t acc) {
- return acc;
+reg_t SoundCommandParser::kDoSoundResumeAfterRestore(EngineState *s, int argc, reg_t *argv) {
+ return s->r_acc;
}
-reg_t SoundCommandParser::kDoSoundMute(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundMute(EngineState *s, int argc, reg_t *argv) {
uint16 previousState = _music->soundGetSoundOn();
if (argc > 0) {
debugC(kDebugLevelSound, "kDoSound(mute): %d", argv[0].toUint16());
@@ -374,37 +375,33 @@ reg_t SoundCommandParser::kDoSoundMute(int argc, reg_t *argv, reg_t acc) {
return make_reg(0, previousState);
}
-reg_t SoundCommandParser::kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc) {
- acc = make_reg(0, _music->soundGetMasterVolume());
+reg_t SoundCommandParser::kDoSoundMasterVolume(EngineState *s, int argc, reg_t *argv) {
+ s->r_acc = make_reg(0, _music->soundGetMasterVolume());
if (argc > 0) {
debugC(kDebugLevelSound, "kDoSound(masterVolume): %d", argv[0].toSint16());
int vol = CLIP<int16>(argv[0].toSint16(), 0, MUSIC_MASTERVOLUME_MAX);
- vol = vol * Audio::Mixer::kMaxMixerVolume / MUSIC_MASTERVOLUME_MAX;
- ConfMan.setInt("music_volume", vol);
- // In SCI32, digital audio volume is controlled separately by
- // kDoAudioVolume
- if (_soundVersion < SCI_VERSION_2_1_EARLY) {
- ConfMan.setInt("sfx_volume", vol);
+
+ if (!g_sci->_guestAdditions->kDoSoundMasterVolumeHook(vol)) {
+ setMasterVolume(vol);
}
- g_engine->syncSoundSettings();
}
- return acc;
+ return s->r_acc;
}
-reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundFade(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
// The object can be null in several SCI0 games (e.g. Camelot, KQ1, KQ4, MUMG).
// Check bugs #3035149, #3036942 and #3578335.
// In this case, we just ignore the call.
if (obj.isNull() && argc == 1)
- return acc;
+ return s->r_acc;
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
debugC(kDebugLevelSound, "kDoSound(fade): Slot not found (%04x:%04x)", PRINT_REG(obj));
- return acc;
+ return s->r_acc;
}
int volume = musicSlot->volume;
@@ -412,7 +409,7 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) {
#ifdef ENABLE_SCI32
if (_soundVersion >= SCI_VERSION_2_1_EARLY && musicSlot->isSample) {
g_sci->_audio32->fadeChannel(ResourceId(kResourceTypeAudio, musicSlot->resourceId), musicSlot->soundObj, argv[1].toSint16(), argv[2].toSint16(), argv[3].toSint16(), argc > 4 ? (bool)argv[4].toSint16() : false);
- return acc;
+ return s->r_acc;
}
#endif
@@ -420,7 +417,7 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) {
if (musicSlot->status != kSoundPlaying) {
debugC(kDebugLevelSound, "kDoSound(fade): %04x:%04x fading requested, but sound is currently not playing", PRINT_REG(obj));
writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- return acc;
+ return s->r_acc;
}
switch (argc) {
@@ -439,7 +436,7 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) {
// Check if the song is already at the requested volume. If it is, don't
// perform any fading. Happens for example during the intro of Longbow.
if (musicSlot->fadeTo == musicSlot->volume)
- return acc;
+ return s->r_acc;
// Sometimes we get objects in that position, so fix the value (refer to workarounds.cpp)
if (!argv[1].getSegment())
@@ -464,14 +461,14 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) {
}
debugC(kDebugLevelSound, "kDoSound(fade): %04x:%04x to %d, step %d, ticker %d", PRINT_REG(obj), musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep);
- return acc;
+ return s->r_acc;
}
-reg_t SoundCommandParser::kDoSoundGetPolyphony(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundGetPolyphony(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, _music->soundGetVoices()); // Get the number of voices
}
-reg_t SoundCommandParser::kDoSoundUpdate(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundUpdate(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
debugC(kDebugLevelSound, "kDoSound(update): %04x:%04x", PRINT_REG(argv[0]));
@@ -479,7 +476,7 @@ reg_t SoundCommandParser::kDoSoundUpdate(int argc, reg_t *argv, reg_t acc) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
warning("kDoSound(update): Slot not found (%04x:%04x)", PRINT_REG(obj));
- return acc;
+ return s->r_acc;
}
musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
@@ -489,12 +486,12 @@ reg_t SoundCommandParser::kDoSoundUpdate(int argc, reg_t *argv, reg_t acc) {
int16 objPrio = readSelectorValue(_segMan, obj, SELECTOR(priority));
if (objPrio != musicSlot->priority)
_music->soundSetPriority(musicSlot, objPrio);
- return acc;
+ return s->r_acc;
}
-reg_t SoundCommandParser::kDoSoundUpdateCues(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundUpdateCues(EngineState *s, int argc, reg_t *argv) {
processUpdateCues(argv[0]);
- return acc;
+ return s->r_acc;
}
void SoundCommandParser::processUpdateCues(reg_t obj) {
@@ -608,7 +605,7 @@ void SoundCommandParser::processUpdateCues(reg_t obj) {
}
}
-reg_t SoundCommandParser::kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundSendMidi(EngineState *s, int argc, reg_t *argv) {
// The 4 parameter variant of this call is used in at least LSL1VGA, room
// 110 (Lefty's bar), to distort the music when Larry is drunk and stands
// up - bug #3614447.
@@ -637,13 +634,13 @@ reg_t SoundCommandParser::kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc) {
// if so, allow it
//_music->sendMidiCommand(_midiCommand);
warning("kDoSound(sendMidi): Slot not found (%04x:%04x)", PRINT_REG(obj));
- return acc;
+ return s->r_acc;
}
_music->sendMidiCommand(musicSlot, midiCommand);
- return acc;
+ return s->r_acc;
}
-reg_t SoundCommandParser::kDoSoundGlobalReverb(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundGlobalReverb(EngineState *s, int argc, reg_t *argv) {
byte prevReverb = _music->getCurrentReverb();
byte reverb = argv[0].toUint16() & 0xF;
@@ -656,7 +653,7 @@ reg_t SoundCommandParser::kDoSoundGlobalReverb(int argc, reg_t *argv, reg_t acc)
return make_reg(0, prevReverb);
}
-reg_t SoundCommandParser::kDoSoundSetHold(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundSetHold(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
debugC(kDebugLevelSound, "doSoundSetHold: %04x:%04x, %d", PRINT_REG(argv[0]), argv[1].toUint16());
@@ -664,24 +661,24 @@ reg_t SoundCommandParser::kDoSoundSetHold(int argc, reg_t *argv, reg_t acc) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
warning("kDoSound(setHold): Slot not found (%04x:%04x)", PRINT_REG(obj));
- return acc;
+ return s->r_acc;
}
// Set the special hold marker ID where the song should be looped at.
musicSlot->hold = argv[1].toSint16();
- return acc;
+ return s->r_acc;
}
-reg_t SoundCommandParser::kDoSoundGetAudioCapability(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundGetAudioCapability(EngineState *s, int argc, reg_t *argv) {
// Tests for digital audio support
return make_reg(0, 1);
}
-reg_t SoundCommandParser::kDoSoundStopAll(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundStopAll(EngineState *s, int argc, reg_t *argv) {
// TODO: this can't be right, this gets called in kq1 - e.g. being in witch house, getting the note
// now the point jingle plays and after a messagebox they call this - and would stop the background effects with it
// this doesn't make sense, so i disable it for now
- return acc;
+ return s->r_acc;
Common::StackLock(_music->_mutex);
@@ -697,10 +694,10 @@ reg_t SoundCommandParser::kDoSoundStopAll(int argc, reg_t *argv, reg_t acc) {
(*i)->dataInc = 0;
_music->soundStop(*i);
}
- return acc;
+ return s->r_acc;
}
-reg_t SoundCommandParser::kDoSoundSetVolume(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundSetVolume(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
int16 value = argv[1].toSint16();
@@ -711,7 +708,7 @@ reg_t SoundCommandParser::kDoSoundSetVolume(int argc, reg_t *argv, reg_t acc) {
// the drum sounds of the energizer bunny at the beginning), so this is
// normal behavior.
//warning("cmdSetSoundVolume: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return acc;
+ return s->r_acc;
}
debugC(kDebugLevelSound, "kDoSound(setVolume): %d", value);
@@ -724,15 +721,20 @@ reg_t SoundCommandParser::kDoSoundSetVolume(int argc, reg_t *argv, reg_t acc) {
_music->soundSetVolume(musicSlot, value);
}
#endif
+
if (musicSlot->volume != value) {
musicSlot->volume = value;
_music->soundSetVolume(musicSlot, value);
writeSelectorValue(_segMan, obj, SELECTOR(vol), value);
+#ifdef ENABLE_SCI32
+ g_sci->_guestAdditions->kDoSoundSetVolumeHook(obj, value);
+#endif
}
- return acc;
+
+ return s->r_acc;
}
-reg_t SoundCommandParser::kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundSetPriority(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
int16 value = argv[1].toSint16();
@@ -741,7 +743,7 @@ reg_t SoundCommandParser::kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc)
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
debugC(kDebugLevelSound, "kDoSound(setPriority): Slot not found (%04x:%04x)", PRINT_REG(obj));
- return acc;
+ return s->r_acc;
}
if (value == -1) {
@@ -759,10 +761,10 @@ reg_t SoundCommandParser::kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc)
_music->soundSetPriority(musicSlot, value);
}
- return acc;
+ return s->r_acc;
}
-reg_t SoundCommandParser::kDoSoundSetLoop(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundSetLoop(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
int16 value = argv[1].toSint16();
@@ -780,7 +782,7 @@ reg_t SoundCommandParser::kDoSoundSetLoop(int argc, reg_t *argv, reg_t acc) {
} else {
// Doesn't really matter
}
- return acc;
+ return s->r_acc;
}
#ifdef ENABLE_SCI32
@@ -805,13 +807,13 @@ reg_t SoundCommandParser::kDoSoundSetLoop(int argc, reg_t *argv, reg_t acc) {
}
#endif
- return acc;
+ return s->r_acc;
}
-reg_t SoundCommandParser::kDoSoundSuspend(int argc, reg_t *argv, reg_t acc) {
+reg_t SoundCommandParser::kDoSoundSuspend(EngineState *s, int argc, reg_t *argv) {
// TODO
warning("kDoSound(suspend): STUB");
- return acc;
+ return s->r_acc;
}
void SoundCommandParser::updateSci0Cues() {
@@ -879,6 +881,17 @@ void SoundCommandParser::setMasterVolume(int vol) {
_music->soundSetMasterVolume(vol);
}
+#ifdef ENABLE_SCI32
+void SoundCommandParser::setVolume(const reg_t obj, const int volume) {
+ MusicEntry *slot = _music->getSlot(obj);
+ if (slot != nullptr) {
+ slot->volume = volume;
+ writeSelectorValue(_segMan, obj, SELECTOR(vol), volume);
+ _music->soundSetVolume(slot, volume);
+ }
+}
+#endif
+
void SoundCommandParser::pauseAll(bool pause) {
_music->pauseAll(pause);
}
diff --git a/engines/sci/sound/soundcmd.h b/engines/sci/sound/soundcmd.h
index 5bb7cf2cb1..928c9b1acb 100644
--- a/engines/sci/sound/soundcmd.h
+++ b/engines/sci/sound/soundcmd.h
@@ -46,7 +46,7 @@ public:
SoundCommandParser(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, AudioPlayer *audio, SciVersion soundVersion);
~SoundCommandParser();
- //reg_t parseCommand(int argc, reg_t *argv, reg_t acc);
+ //reg_t parseCommand(EngineState *s, int argc, reg_t *argv);
// Functions used for game state loading
void clearPlayList();
@@ -56,6 +56,9 @@ public:
// Functions used for the ScummVM menus
void setMasterVolume(int vol);
void pauseAll(bool pause);
+#ifdef ENABLE_SCI32
+ void setVolume(const reg_t obj, const int vol);
+#endif
// Debug console functions
void startNewSound(int number);
@@ -78,29 +81,29 @@ public:
*/
void updateSci0Cues();
- reg_t kDoSoundInit(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundPlay(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundRestore(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundMute(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundPause(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundResumeAfterRestore(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundStop(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundStopAll(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundDispose(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundFade(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundGetPolyphony(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundUpdate(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundUpdateCues(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundGlobalReverb(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundSetHold(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundDummy(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundGetAudioCapability(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundSetVolume(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundSetLoop(int argc, reg_t *argv, reg_t acc);
- reg_t kDoSoundSuspend(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundPlay(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundRestore(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundMute(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundPause(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundResumeAfterRestore(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundStop(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundStopAll(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundDispose(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundMasterVolume(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundFade(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundGetPolyphony(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundUpdate(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundUpdateCues(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundSendMidi(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundGlobalReverb(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundSetHold(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundDummy(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundGetAudioCapability(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundSetVolume(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundSetPriority(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundSetLoop(EngineState *s, int argc, reg_t *argv);
+ reg_t kDoSoundSuspend(EngineState *s, int argc, reg_t *argv);
private:
//typedef Common::Array<MusicEntryCommand *> SoundCommandContainer;