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

csnover csnover at users.noreply.github.com
Mon Jul 31 02:35:25 CEST 2017


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

Summary:
a79bd8b645 SCI32: Add uninitialized read workaround for Phant2
d2b26b2c21 SCI32: Update Phant2 GUIOs to reflect game features
ad05bfedd8 SCI: Expose API for retrieving multiple objects with the same name
b13777da8c SCI32: Implement guest additions volume sync for Phant2
ba4c28418f SCI32: Minor code deduplication
c8a06bed40 SCI32: Fix Phant2 game name
d02e62a769 SCI32: Indicate that LSL7 and Torin use the same save/load code
06686c09f0 SCI32: Fix breakpoints on reading/writing selectors in SCI3
e4b3478d99 SCI: Show inherited methods when viewing an object in the debugger
325fd1a3ef SCI32: Deduplicate guest additions save/load code
49e8f05714 SCI32: Implement Phant2 save/load integration
5c3a2cf16a SCI32: Add load from launcher support for Phant2
caa8293fce SCI32: Make sure audio is not paused from the future


Commit: a79bd8b6457078d2cffc663fa8eb12ea75fbeb07
    https://github.com/scummvm/scummvm/commit/a79bd8b6457078d2cffc663fa8eb12ea75fbeb07
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:10:50-05:00

Commit Message:
SCI32: Add uninitialized read workaround for Phant2

Changed paths:
    engines/sci/engine/workarounds.cpp


diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index 28cfe76..1bbca75 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -361,6 +361,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
 	{ GID_PEPPER,         -1,   894,  0,            "Package", "doVerb",                          NULL,     3, { WORKAROUND_FAKE,   0 } }, // using the hand on the book in the inventory - bug #5154
 	{ GID_PEPPER,        150,   928,  0,           "Narrator", "startText",                       NULL,     0, { WORKAROUND_FAKE,   0 } }, // happens during the non-interactive demo of Pepper
 	{ GID_PHANTASMAGORIA, -1, 64921, -1,              "Print", "addEdit",                         NULL,     1, { WORKAROUND_FAKE,   0 } }, // When trying to use the game debugger's flag setting command
+	{ GID_PHANTASMAGORIA2,-1, 64926, -1,              "Thumb", "action",                          NULL,     1, { WORKAROUND_FAKE,   0 } }, // When dragging one of the volume sliders and releasing the mouse button over the +/- buttons
 	{ GID_PQ4,            -1,    25,  0,         "iconToggle", "select",                          NULL,     1, { WORKAROUND_FAKE,   0 } }, // when toggling the icon bar to auto-hide or not
 	{ GID_PQ4,           170,   170, -1,        "hideAndSeek", "handleEvent",                     NULL,     1, { WORKAROUND_FAKE,   0 } }, // when clicking to move right while still moving left during the Emo shootout - bug #9847
 	{ GID_PQ4,           275, 64964, -1,              "DPath", "init",                            NULL,     1, { WORKAROUND_FAKE,   0 } }, // when Sherry walks out of the morgue on day 3


Commit: d2b26b2c21d5b644e4b961bddd3d8ed9ec7cfb7b
    https://github.com/scummvm/scummvm/commit/d2b26b2c21d5b644e4b961bddd3d8ed9ec7cfb7b
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:10:50-05:00

Commit Message:
SCI32: Update Phant2 GUIOs to reflect game features

Changed paths:
    engines/sci/detection_tables.h


diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index 3650de5..ed7fe0d 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -3124,11 +3124,13 @@ static const struct ADGameDescription SciGameDescriptions[] = {
 #undef GUIO_PHANTASMAGORIA
 #undef GUIO_PHANTASMAGORIA_MAC
 
-// TODO: Correct GUIOs
-#define GUIO_PHANTASMAGORIA2 GUIO5(GUIO_NOASPECT, \
+#define GUIO_PHANTASMAGORIA2 GUIO8(GUIO_NOSUBTITLES, \
+                                   GUIO_LINKMUSICTOSFX, \
+                                   GUIO_LINKSPEECHTOSFX, \
                                    GUIO_NOMIDI, \
-                                   GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+                                   GUIO_NOASPECT, \
                                    GAMEOPTION_ORIGINAL_SAVELOAD, \
+                                   GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
                                    GAMEOPTION_HQ_VIDEO)
 
 	// Some versions of Phantasmagoria 2 were heavily censored.


Commit: ad05bfedd85b4ca560166bf95a0a2ec46df52dc8
    https://github.com/scummvm/scummvm/commit/ad05bfedd85b4ca560166bf95a0a2ec46df52dc8
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:10:50-05:00

Commit Message:
SCI: Expose API for retrieving multiple objects with the same name

This is needed by Phant2 guest additions to find the correct
slider for the music volume, since the sliders have no unique name.

Changed paths:
    engines/sci/engine/seg_manager.cpp
    engines/sci/engine/seg_manager.h


diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index 3cf9d08..aa70d5f 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -284,12 +284,11 @@ const char *SegManager::getObjectName(reg_t pos) {
 	return name;
 }
 
-reg_t SegManager::findObjectByName(const Common::String &name, int index) {
+Common::Array<reg_t> SegManager::findObjectsByName(const Common::String &name) {
 	Common::Array<reg_t> result;
-	uint i;
 
 	// Now all values are available; iterate over all objects.
-	for (i = 0; i < _heap.size(); i++) {
+	for (uint i = 0; i < _heap.size(); i++) {
 		const SegmentObj *mobj = _heap[i];
 
 		if (!mobj)
@@ -320,12 +319,18 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) {
 		}
 	}
 
+	return result;
+}
+
+reg_t SegManager::findObjectByName(const Common::String &name, int index) {
+	Common::Array<reg_t> result = findObjectsByName(name);
+
 	if (result.empty())
 		return NULL_REG;
 
 	if (result.size() > 1 && index < 0) {
 		debug("findObjectByName(%s): multiple matches:", name.c_str());
-		for (i = 0; i < result.size(); i++)
+		for (uint i = 0; i < result.size(); i++)
 			debug("  %3x: [%04x:%04x]", i, PRINT_REG(result[i]));
 		return NULL_REG; // Ambiguous
 	}
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index 916b813..d13edf0 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -411,6 +411,11 @@ public:
 	const char *getObjectName(reg_t pos);
 
 	/**
+	 * Finds the addresses of all objects with the given name.
+	 */
+	Common::Array<reg_t> findObjectsByName(const Common::String &name);
+
+	/**
 	 * Find the address of an object by its name. In case multiple objects
 	 * with the same name occur, the optional index parameter can be used
 	 * to distinguish between them. If index is -1, then if there is a


Commit: b13777da8cca2585b6658a53457b2043cc3e8ffc
    https://github.com/scummvm/scummvm/commit/b13777da8cca2585b6658a53457b2043cc3e8ffc
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:10:50-05:00

Commit Message:
SCI32: Implement guest additions volume sync for Phant2

Changed paths:
    engines/sci/engine/features.cpp
    engines/sci/engine/guest_additions.cpp
    engines/sci/engine/guest_additions.h
    engines/sci/engine/selector.cpp
    engines/sci/engine/selector.h
    engines/sci/engine/vm.h


diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index e64cf9c..03e8019 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -576,6 +576,7 @@ bool GameFeatures::audioVolumeSyncUsesGlobals() const {
 	case GID_LSL6HIRES:
 	case GID_LSL7:
 	case GID_PHANTASMAGORIA:
+	case GID_PHANTASMAGORIA2:
 	case GID_TORIN:
 		// TODO: SCI3
 		return true;
diff --git a/engines/sci/engine/guest_additions.cpp b/engines/sci/engine/guest_additions.cpp
index 0f4818d..28b0bca 100644
--- a/engines/sci/engine/guest_additions.cpp
+++ b/engines/sci/engine/guest_additions.cpp
@@ -141,6 +141,8 @@ bool GuestAdditions::shouldSyncAudioToScummVM() const {
 													objName == "dacVolDown" ||
 													objName == "dacVolUp")) {
 			return true;
+		} else if (gameId == GID_PHANTASMAGORIA2 && objName == "foo2") {
+			return true;
 		} else if ((gameId == GID_LSL7 || gameId == GID_TORIN) && (objName == "oMusicScroll" ||
 																   objName == "oSFXScroll" ||
 																   objName == "oAudioScroll")) {
@@ -819,6 +821,13 @@ void GuestAdditions::syncAudioVolumeGlobalsFromScummVM() const {
 		break;
 	}
 
+	case GID_PHANTASMAGORIA2: {
+		const int16 masterVolume = (ConfMan.getInt("sfx_volume") + 1) * Audio32::kMaxVolume / Audio::Mixer::kMaxMixerVolume;
+		syncPhant2VolumeFromScummVM(masterVolume);
+		syncPhant2UI(masterVolume);
+		break;
+	}
+
 	case GID_LSL7:
 	case GID_TORIN: {
 		const int16 musicVolume  = (ConfMan.getInt("music_volume") + 1)  * 100 / Audio::Mixer::kMaxMixerVolume;
@@ -917,6 +926,16 @@ void GuestAdditions::syncLSL6HiresVolumeFromScummVM(const int16 musicVolume) con
 	g_sci->_soundCmd->setMasterVolume(ConfMan.getBool("mute") ? 0 : (musicVolume * MUSIC_MASTERVOLUME_MAX / kLSL6HiresUIVolumeMax));
 }
 
+void GuestAdditions::syncPhant2VolumeFromScummVM(const int16 musicVolume) const {
+	_state->variables[VAR_GLOBAL][kGlobalVarPhant2MasterVolume] = make_reg(0, musicVolume);
+
+	const reg_t soundsId = _state->variables[VAR_GLOBAL][kGlobalVarSounds];
+	if (!soundsId.isNull()) {
+		reg_t params[] = { make_reg(0, SELECTOR(setVol)), make_reg(0, musicVolume) };
+		invokeSelector(soundsId, SELECTOR(eachElementDo), 2, params);
+	}
+}
+
 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);
@@ -969,6 +988,13 @@ void GuestAdditions::syncAudioVolumeGlobalsToScummVM(const int index, const reg_
 		}
 		break;
 
+	case GID_PHANTASMAGORIA2:
+		if (index == kGlobalVarPhant2MasterVolume) {
+			const int16 masterVolume = value.toSint16() * Audio::Mixer::kMaxMixerVolume / Audio32::kMaxVolume;
+			ConfMan.setInt("sfx_volume", masterVolume);
+		}
+		break;
+
 	case GID_LSL7:
 	case GID_TORIN:
 		if (index == kGlobalVarTorinMusicVolume ||
@@ -1129,6 +1155,25 @@ void GuestAdditions::syncPhant1UI(const int16 oldMusicVolume, const int16 musicV
 	}
 }
 
+void GuestAdditions::syncPhant2UI(const int16 musicVolume) const {
+	const reg_t musicVolumeScript = _segMan->findObjectByName("foo2");
+	Common::Array<reg_t> scrollBars = _segMan->findObjectsByName("P2ScrollBar");
+	for (uint i = 0; i < scrollBars.size(); ++i) {
+		if (readSelector(_segMan, scrollBars[i], SELECTOR(client)) == musicVolumeScript) {
+			// P2ScrollBar objects may exist without actually being on-screen;
+			// the easiest way to tell seems to be to look to see if it has
+			// non-null pointers to subviews. (The game will correctly set the
+			// position of the scrollbar when it first becomes visible, so this
+			// is fine.)
+			if (!readSelector(_segMan, scrollBars[i], SELECTOR(physicalBar)).isNull()) {
+				reg_t params[] = { make_reg(0, musicVolume), make_reg(0, 1) };
+				invokeSelector(scrollBars[i], SELECTOR(move), 2, params);
+				break;
+			}
+		}
+	}
+}
+
 void GuestAdditions::syncMGDXUI(const int16 musicVolume) const {
 	const reg_t sliderId = _segMan->findObjectByName("icon1");
 	if (!sliderId.isNull()) {
diff --git a/engines/sci/engine/guest_additions.h b/engines/sci/engine/guest_additions.h
index 79f498a..fd77909 100644
--- a/engines/sci/engine/guest_additions.h
+++ b/engines/sci/engine/guest_additions.h
@@ -321,6 +321,7 @@ private:
 
 	void syncGK2VolumeFromScummVM(const int16 musicVolume) const;
 	void syncLSL6HiresVolumeFromScummVM(const int16 musicVolume) const;
+	void syncPhant2VolumeFromScummVM(const int16 masterVolume) const;
 	void syncTorinVolumeFromScummVM(const int16 musicVolume, const int16 sfxVolume, const int16 speechVolume) const;
 
 	/**
@@ -350,6 +351,7 @@ private:
 	void syncLSL6HiresUI(const int16 musicVolume) const;
 	void syncMGDXUI(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 syncPhant2UI(const int16 masterVolume) const;
 	void syncPQ4UI(const int16 musicVolume) const;
 	void syncPQSWATUI() const;
 	void syncQFG4UI(const int16 musicVolume) const;
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index 7740557..889590f 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -219,6 +219,9 @@ void Kernel::mapSelectors() {
 	FIND_SELECTOR(displayValue);
 	FIND_SELECTOR2(new_, "new");
 	FIND_SELECTOR(mainCel);
+	FIND_SELECTOR(move);
+	FIND_SELECTOR(eachElementDo);
+	FIND_SELECTOR(physicalBar);
 #endif
 }
 
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index b223262..850a5dc 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -176,6 +176,9 @@ struct SelectorCache {
 	Selector displayValue; // for PQ:SWAT volume sync
 	Selector new_; // for Torin/LSL7 save/load patching
 	Selector mainCel; // for MGDX volume sync
+	Selector move; // for Phant2 volume sync
+	Selector eachElementDo; // for Phant2 volume sync
+	Selector physicalBar; // for Phant2 volume sync
 #endif
 };
 
diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h
index 1ac6fed..e92929e 100644
--- a/engines/sci/engine/vm.h
+++ b/engines/sci/engine/vm.h
@@ -167,6 +167,9 @@ enum GlobalVar {
 	kGlobalVarTorinMusicVolume     = 227, // 0 to 100
 	kGlobalVarTorinSFXVolume       = 228, // 0 to 100
 	kGlobalVarTorinSpeechVolume    = 229, // 0 to 100
+	// Phant2 labels its volume slider as "music volume" but it is actually
+	// a master volume that affects both music *and* sound effects
+	kGlobalVarPhant2MasterVolume   = 236, // 0 to 127
 	kGlobalVarShivers1Score        = 349
 };
 


Commit: ba4c28418fcc0bf384619a325aaa73726cf00875
    https://github.com/scummvm/scummvm/commit/ba4c28418fcc0bf384619a325aaa73726cf00875
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:10:50-05:00

Commit Message:
SCI32: Minor code deduplication

Changed paths:
    engines/sci/engine/guest_additions.cpp


diff --git a/engines/sci/engine/guest_additions.cpp b/engines/sci/engine/guest_additions.cpp
index 28b0bca..a508b51 100644
--- a/engines/sci/engine/guest_additions.cpp
+++ b/engines/sci/engine/guest_additions.cpp
@@ -954,7 +954,7 @@ void GuestAdditions::syncTorinVolumeFromScummVM(const int16 musicVolume, const i
 			const reg_t &soundObj = sound->value;
 
 			if (_segMan->isHeapObject(soundObj) && lookupSelector(_segMan, soundObj, selector, nullptr, nullptr) != kSelectorNone) {
-				invokeSelector(sound->value, SELECTOR(reSyncVol));
+				invokeSelector(sound->value, selector);
 			}
 			soundId = sound->succ;
 		}


Commit: c8a06bed4064377474c78ac58907a3a8609780e4
    https://github.com/scummvm/scummvm/commit/c8a06bed4064377474c78ac58907a3a8609780e4
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:10:50-05:00

Commit Message:
SCI32: Fix Phant2 game name

The game was originally titled "Phantasmagoria: A Puzzle of Flesh",
but the publisher now seem to be titling it "Phantasmagoria 2: A
Puzzle of Flesh". It was never referred to using roman numerals in
any release as far as I can find (the French "Fatal Obsessions"
release was just "Phantasmagoria: Obsessions Fatales").

Changed paths:
    engines/sci/detection.cpp


diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index fe95b0c..17a1ece 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -126,7 +126,7 @@ static const PlainGameDescriptor s_sciGameTitles[] = {
 	// === SCI3 games =========================================================
 	{"lsl7",            "Leisure Suit Larry 7: Love for Sail!"},
 	{"lighthouse",      "Lighthouse: The Dark Being"},
-	{"phantasmagoria2", "Phantasmagoria II: A Puzzle of Flesh"},
+	{"phantasmagoria2", "Phantasmagoria 2: A Puzzle of Flesh"},
 	//{"shivers2",        "Shivers II: Harvest of Souls"},	// Not SCI
 	{"rama",            "RAMA"},
 	{0, 0}


Commit: d02e62a76940c3b35c24872033d2c9ee1386f45b
    https://github.com/scummvm/scummvm/commit/d02e62a76940c3b35c24872033d2c9ee1386f45b
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:10:50-05:00

Commit Message:
SCI32: Indicate that LSL7 and Torin use the same save/load code

Changed paths:
    engines/sci/engine/guest_additions.h


diff --git a/engines/sci/engine/guest_additions.h b/engines/sci/engine/guest_additions.h
index fd77909..68bb7d1 100644
--- a/engines/sci/engine/guest_additions.h
+++ b/engines/sci/engine/guest_additions.h
@@ -183,7 +183,7 @@ private:
 	void patchGameSaveRestoreSCI32(Script &script) const;
 
 	/**
-	 * Patches the ScummVM save/load dialogue into Torin.
+	 * Patches the ScummVM save/load dialogue into Torin/LSL7.
 	 */
 	void patchGameSaveRestoreTorin(Script &script) const;
 
@@ -194,7 +194,7 @@ private:
 	reg_t promptSaveRestoreDefault(EngineState *s, int argc, reg_t *argv) const;
 
 	/**
-	 * Prompts for a save game and returns it to game scripts using Torin's
+	 * Prompts for a save game and returns it to game scripts using Torin/LSL7's
 	 * custom NewGame class semantics.
 	 */
 	reg_t promptSaveRestoreTorin(EngineState *s, int argc, reg_t *argv) const;


Commit: 06686c09f00700ac07b5503aad41e02696cfdb16
    https://github.com/scummvm/scummvm/commit/06686c09f00700ac07b5503aad41e02696cfdb16
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:10:50-05:00

Commit Message:
SCI32: Fix breakpoints on reading/writing selectors in SCI3

Changed paths:
    engines/sci/engine/scriptdebug.cpp


diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index 1b3f186..f31a43c 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -950,11 +950,21 @@ void debugPropertyAccess(Object *obj, reg_t objp, unsigned int index, reg_t curV
 	const Object *var_container = obj;
 	if (!obj->isClass() && getSciVersion() != SCI_VERSION_3)
 		var_container = segMan->getObject(obj->getSuperClassSelector());
-	if ((index >> 1) >= var_container->getVarCount()) {
-		// TODO: error, warning, debug?
-		return;
+
+	uint16 varSelector;
+	if (getSciVersion() == SCI_VERSION_3) {
+		varSelector = index;
+	} else {
+		index >>= 1;
+
+		if (index >= var_container->getVarCount()) {
+			// TODO: error, warning, debug?
+			return;
+		}
+
+		varSelector = var_container->getVarSelector(index);
 	}
-	uint16 varSelector = var_container->getVarSelector(index >> 1);
+
 	if (g_sci->checkSelectorBreakpoint(breakpointType, objp, varSelector)) {
 		// checkSelectorBreakpoint has already triggered the breakpoint.
 		// We just output the relevant data here.


Commit: e4b3478d9905e42b96430fb311b92293f295fc78
    https://github.com/scummvm/scummvm/commit/e4b3478d9905e42b96430fb311b92293f295fc78
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:10:50-05:00

Commit Message:
SCI: Show inherited methods when viewing an object in the debugger

Changed paths:
    engines/sci/engine/scriptdebug.cpp


diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index f31a43c..cfe0ebe 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -1194,10 +1194,22 @@ bool printObject(reg_t pos) {
 		con->debugPrintf("\n");
 	}
 	con->debugPrintf("  -- methods:\n");
-	for (i = 0; i < obj->getMethodCount(); i++) {
-		reg_t fptr = obj->getFunction(i);
-		con->debugPrintf("    [%03x] %s = %04x:%04x\n", obj->getFuncSelector(i), g_sci->getKernel()->getSelectorName(obj->getFuncSelector(i)).c_str(), PRINT_REG(fptr));
-	}
+	Common::Array<Selector> foundMethods;
+	const Object *protoObj = obj;
+	do {
+		for (i = 0; i < protoObj->getMethodCount(); i++) {
+			const Selector selector = protoObj->getFuncSelector(i);
+			if (Common::find(foundMethods.begin(), foundMethods.end(), selector) == foundMethods.end()) {
+				reg_t fptr = protoObj->getFunction(i);
+				con->debugPrintf("    [%03x] ", selector);
+				if (protoObj != obj) {
+					con->debugPrintf("%s::", s->_segMan->getObjectName(protoObj->getPos()));
+				}
+				con->debugPrintf("%s = %04x:%04x\n", g_sci->getKernel()->getSelectorName(selector).c_str(), PRINT_REG(fptr));
+				foundMethods.push_back(selector);
+			}
+		}
+	} while ((protoObj = s->_segMan->getObject(protoObj->getSuperClassSelector())));
 
 	Script *scr = s->_segMan->getScriptIfLoaded(pos.getSegment());
 	if (scr)


Commit: 325fd1a3efdbeaaf01a61a9905d40cc91ad9f6c3
    https://github.com/scummvm/scummvm/commit/325fd1a3efdbeaaf01a61a9905d40cc91ad9f6c3
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:10:50-05:00

Commit Message:
SCI32: Deduplicate guest additions save/load code

Changed paths:
    engines/sci/engine/guest_additions.cpp
    engines/sci/engine/guest_additions.h


diff --git a/engines/sci/engine/guest_additions.cpp b/engines/sci/engine/guest_additions.cpp
index a508b51..6b9b98d 100644
--- a/engines/sci/engine/guest_additions.cpp
+++ b/engines/sci/engine/guest_additions.cpp
@@ -421,70 +421,65 @@ reg_t GuestAdditions::kScummVMSaveLoad(EngineState *s, int argc, reg_t *argv) co
 }
 
 reg_t GuestAdditions::promptSaveRestoreDefault(EngineState *s, int argc, reg_t *argv) const {
-	const bool isSave = (argc > 0);
-	int saveNo;
+	return make_reg(0, runSaveRestore(argc > 0, argc > 0 ? argv[0] : NULL_REG, s->_delayedRestoreGameId));
+}
+
+reg_t GuestAdditions::promptSaveRestoreTorin(EngineState *s, int argc, reg_t *argv) const {
+	const bool isSave = (argc > 0 && (bool)argv[0].toSint16());
 
+	reg_t descriptionId = NULL_REG;
 	if (isSave) {
-		GUI::SaveLoadChooser dialog(_("Save game:"), _("Save"), true);
-		saveNo = dialog.runModalWithCurrentTarget();
-		if (saveNo != -1) {
-			reg_t descriptionId;
-			if (_segMan->isObject(argv[0])) {
-				descriptionId = readSelector(_segMan, argv[0], SELECTOR(data));
-			} else {
-				descriptionId = argv[0];
-			}
-			SciArray &description = *_segMan->lookupArray(descriptionId);
-			Common::String descriptionString = dialog.getResultString();
-			if (descriptionString.empty())
-				descriptionString = dialog.createDefaultSaveDescription(saveNo - 1);
-			description.fromString(descriptionString);
-		}
-	} else {
-		if (s->_delayedRestoreGameId != -1) {
-			saveNo = s->_delayedRestoreGameId;
-		} else {
-			GUI::SaveLoadChooser dialog(_("Restore game:"), _("Restore"), false);
-			saveNo = dialog.runModalWithCurrentTarget();
-		}
+		_segMan->allocateArray(kArrayTypeString, 0, &descriptionId);
 	}
 
-	if (saveNo > 0) {
-		// The autosave slot in ScummVM takes up slot 0, but in SCI the first
-		// non-autosave save game number needs to be 0, so reduce the save
-		// number here to match what would come from the normal SCI save/restore
-		// dialog. There is additional special code for handling the autosave
-		// game inside of kRestoreGame32.
-		--saveNo;
+	const int saveNo = runSaveRestore(isSave, descriptionId, s->_delayedRestoreGameId);
+
+	if (saveNo != -1) {
+		assert(s->variablesMax[VAR_LOCAL] > 2);
+		writeSelector(_segMan, s->variables[VAR_LOCAL][1], SELECTOR(data), descriptionId);
+		s->variables[VAR_LOCAL][2] = make_reg(0, saveNo);
+		s->variables[VAR_LOCAL][3] = make_reg(0, isSave ? 1 : 0);
+	} else if (isSave) {
+		_segMan->freeArray(descriptionId);
 	}
 
-	return make_reg(0, saveNo);
+	return make_reg(0, saveNo != -1);
 }
 
-reg_t GuestAdditions::promptSaveRestoreTorin(EngineState *s, int argc, reg_t *argv) const {
-	const bool isSave = (argc > 0 && (bool)argv[0].toSint16());
+int GuestAdditions::runSaveRestore(const bool isSave, reg_t outDescription, const int forcedSaveNo) const {
 	int saveNo;
+	Common::String descriptionString;
 
-	if (isSave) {
-		GUI::SaveLoadChooser dialog(_("Save game:"), _("Save"), true);
+	if (!isSave && forcedSaveNo != -1) {
+		saveNo = forcedSaveNo;
+	} else {
+		const char *title;
+		const char *action;
+		if (isSave) {
+			title = _("Save game:");
+			action = _("Save");
+		} else {
+			title = _("Restore game:");
+			action = _("Restore");
+		}
+
+		GUI::SaveLoadChooser dialog(title, action, isSave);
 		saveNo = dialog.runModalWithCurrentTarget();
 		if (saveNo != -1) {
-			reg_t descriptionId = s->variables[VAR_LOCAL][1];
-			reg_t dataId;
-			SciArray &description = *_segMan->allocateArray(kArrayTypeString, 0, &dataId);
-			Common::String descriptionString = dialog.getResultString();
-			if (descriptionString.empty())
+			descriptionString = dialog.getResultString();
+			if (descriptionString.empty()) {
 				descriptionString = dialog.createDefaultSaveDescription(saveNo - 1);
-			description.fromString(descriptionString);
-			writeSelector(_segMan, descriptionId, SELECTOR(data), dataId);
+			}
 		}
-	} else {
-		if (s->_delayedRestoreGameId != -1) {
-			saveNo = s->_delayedRestoreGameId;
-		} else {
-			GUI::SaveLoadChooser dialog(_("Restore game:"), _("Restore"), false);
-			saveNo = dialog.runModalWithCurrentTarget();
+	}
+
+	assert(!isSave || !outDescription.isNull());
+	if (!outDescription.isNull()) {
+		if (_segMan->isObject(outDescription)) {
+			outDescription = readSelector(_segMan, outDescription, SELECTOR(data));
 		}
+		SciArray &description = *_segMan->lookupArray(outDescription);
+		description.fromString(descriptionString);
 	}
 
 	if (saveNo > 0) {
@@ -496,13 +491,7 @@ reg_t GuestAdditions::promptSaveRestoreTorin(EngineState *s, int argc, reg_t *ar
 		--saveNo;
 	}
 
-	if (saveNo != -1) {
-		assert(s->variablesMax[VAR_LOCAL] > 2);
-		s->variables[VAR_LOCAL][2] = make_reg(0, saveNo);
-		s->variables[VAR_LOCAL][3] = make_reg(0, isSave ? 1 : 0);
-	}
-
-	return make_reg(0, saveNo != -1);
+	return saveNo;
 }
 
 #endif
diff --git a/engines/sci/engine/guest_additions.h b/engines/sci/engine/guest_additions.h
index 68bb7d1..657fa52 100644
--- a/engines/sci/engine/guest_additions.h
+++ b/engines/sci/engine/guest_additions.h
@@ -198,6 +198,17 @@ private:
 	 * custom NewGame class semantics.
 	 */
 	reg_t promptSaveRestoreTorin(EngineState *s, int argc, reg_t *argv) const;
+
+	/**
+	 * Prompts the user to save or load a game.
+	 *
+	 * @param isSave If true, the prompt is for saving.
+	 * @param outDescription Will be filled with the save game description.
+	 * Optional for loads, required for saves.
+	 * @param forcedSaveNo During delayed restore, force the returned save game
+	 * number to this value.
+	 */
+	int runSaveRestore(const bool isSave, const reg_t outDescription, const int forcedSaveNo = -1) const;
 #endif
 
 #pragma mark -


Commit: 49e8f057144b0a212af380525717a96a152569bc
    https://github.com/scummvm/scummvm/commit/49e8f057144b0a212af380525717a96a152569bc
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:10:50-05:00

Commit Message:
SCI32: Implement Phant2 save/load integration

Changed paths:
    engines/sci/engine/guest_additions.cpp
    engines/sci/engine/guest_additions.h
    engines/sci/engine/kernel_tables.h
    engines/sci/engine/selector.cpp
    engines/sci/engine/selector.h
    engines/sci/engine/vm.h


diff --git a/engines/sci/engine/guest_additions.cpp b/engines/sci/engine/guest_additions.cpp
index 6b9b98d..373a1e9 100644
--- a/engines/sci/engine/guest_additions.cpp
+++ b/engines/sci/engine/guest_additions.cpp
@@ -233,6 +233,8 @@ void GuestAdditions::instantiateScriptHook(Script &script, const bool ignoreDela
 		script.getScriptNumber() == 64866) {
 
 		patchGameSaveRestoreTorin(script);
+	} else if (g_sci->getGameId() == GID_PHANTASMAGORIA2 && script.getScriptNumber() == 64978) {
+		patchGameSaveRestorePhant2(script);
 	} else if (script.getScriptNumber() == 64990) {
 		// 64990 is the system script containing SRDialog. This script is used
 		// by the main Game object, but it is not loaded immediately, so we wait
@@ -412,7 +414,31 @@ void GuestAdditions::patchGameSaveRestoreTorin(Script &script) const {
 	}
 }
 
+void GuestAdditions::patchGameSaveRestorePhant2(Script &script) const {
+	const ObjMap &objects = script.getObjectMap();
+	for (ObjMap::const_iterator it = objects.begin(); it != objects.end(); ++it) {
+		const Object &obj = it->_value;
+
+		if (strncmp(_segMan->derefString(obj.getNameSelector()), "srGetGame", 9) != 0) {
+			continue;
+		}
+
+		int methodIndex = obj.funcSelectorPosition(SELECTOR(init));
+		if (methodIndex == -1) {
+			continue;
+		}
+
+		byte *scriptData = const_cast<byte *>(script.getBuf(obj.getFunction(methodIndex).getOffset()));
+		memcpy(scriptData, SRDialogPatch, sizeof(SRDialogPatch));
+		break;
+	}
+}
+
 reg_t GuestAdditions::kScummVMSaveLoad(EngineState *s, int argc, reg_t *argv) const {
+	if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
+		return promptSaveRestorePhant2(s, argc, argv);
+	}
+
 	if (g_sci->getGameId() == GID_LSL7 || g_sci->getGameId() == GID_TORIN) {
 		return promptSaveRestoreTorin(s, argc, argv);
 	}
@@ -446,6 +472,29 @@ reg_t GuestAdditions::promptSaveRestoreTorin(EngineState *s, int argc, reg_t *ar
 	return make_reg(0, saveNo != -1);
 }
 
+reg_t GuestAdditions::promptSaveRestorePhant2(EngineState *s, int argc, reg_t *argv) const {
+	assert(argc == 2);
+	const bool isSave = argv[1].toSint16() == 0;
+	const int saveNo = runSaveRestore(isSave, argv[0], s->_delayedRestoreGameId);
+
+	// Clear the highlighted state of the button so if the same control panel is
+	// opened again it does not appear to be opened to the save/load panels
+	reg_t button;
+	if (isSave) {
+		button = _segMan->findObjectByName("saveButton");
+	} else {
+		button = _segMan->findObjectByName("loadButton");
+	}
+	writeSelectorValue(_segMan, button, SELECTOR(cel), 0);
+
+	// This causes the control panel to quit its internal event loop and hide
+	// itself
+	const reg_t controlPanel = s->variables[VAR_GLOBAL][kGlobalVarPhant2ControlPanel];
+	writeSelector(_segMan, controlPanel, SELECTOR(scratch), TRUE_REG);
+
+	return make_reg(0, saveNo);
+}
+
 int GuestAdditions::runSaveRestore(const bool isSave, reg_t outDescription, const int forcedSaveNo) const {
 	int saveNo;
 	Common::String descriptionString;
diff --git a/engines/sci/engine/guest_additions.h b/engines/sci/engine/guest_additions.h
index 657fa52..eed1ee3 100644
--- a/engines/sci/engine/guest_additions.h
+++ b/engines/sci/engine/guest_additions.h
@@ -188,6 +188,11 @@ private:
 	void patchGameSaveRestoreTorin(Script &script) const;
 
 	/**
+	 * Patches the ScummVM save/load dialogue into Phant2.
+	 */
+	void patchGameSaveRestorePhant2(Script &script) const;
+
+	/**
 	 * Prompts for a save game and returns it to game scripts using default
 	 * SRDialog game class semantics.
 	 */
@@ -200,6 +205,12 @@ private:
 	reg_t promptSaveRestoreTorin(EngineState *s, int argc, reg_t *argv) const;
 
 	/**
+	 * Prompts for a save game and returns it to game scripts using Phant2's
+	 * custom ControlPanel class semantics.
+	 */
+	reg_t promptSaveRestorePhant2(EngineState *s, int argc, reg_t *argv) const;
+
+	/**
 	 * Prompts the user to save or load a game.
 	 *
 	 * @param isSave If true, the prompt is for saving.
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index bd3184a..c653564 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -779,7 +779,7 @@ static SciKernelMapEntry s_kernelMap[] = {
 	{ MAP_CALL(Said),              SIG_EVERYWHERE,           "[r0]",                  NULL,            NULL },
 #ifdef ENABLE_SCI32
 	{ "SaveGame", kSaveGame32,     SIG_THRU_SCI21EARLY, SIGFOR_ALL, "ri[r0][r0]",     NULL,            NULL },
-	{ MAP_CALL(ScummVMSaveLoad),   SIG_SCI32, SIGFOR_ALL,    "([iro])([ro0])",        NULL,            NULL },
+	{ MAP_CALL(ScummVMSaveLoad),   SIG_SCI32, SIGFOR_ALL,    "([iro])([iro])",        NULL,            NULL },
 #endif
 	{ MAP_CALL(SaveGame),          SIG_SCI16, SIGFOR_ALL,    "[r0]i[r0](r0)",         NULL,            NULL },
 	{ MAP_CALL(ScriptID),          SIG_EVERYWHERE,           "[io](i)",               NULL,            NULL },
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index 889590f..e99f734 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -222,6 +222,8 @@ void Kernel::mapSelectors() {
 	FIND_SELECTOR(move);
 	FIND_SELECTOR(eachElementDo);
 	FIND_SELECTOR(physicalBar);
+	FIND_SELECTOR(init);
+	FIND_SELECTOR(scratch);
 #endif
 }
 
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index 850a5dc..d26a0d7 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -179,6 +179,8 @@ struct SelectorCache {
 	Selector move; // for Phant2 volume sync
 	Selector eachElementDo; // for Phant2 volume sync
 	Selector physicalBar; // for Phant2 volume sync
+	Selector init; // for Phant2 save/load patching
+	Selector scratch; // for Phant2 save/load patching
 #endif
 };
 
diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h
index e92929e..a8ac7b1 100644
--- a/engines/sci/engine/vm.h
+++ b/engines/sci/engine/vm.h
@@ -170,6 +170,7 @@ enum GlobalVar {
 	// Phant2 labels its volume slider as "music volume" but it is actually
 	// a master volume that affects both music *and* sound effects
 	kGlobalVarPhant2MasterVolume   = 236, // 0 to 127
+	kGlobalVarPhant2ControlPanel   = 250,
 	kGlobalVarShivers1Score        = 349
 };
 


Commit: 5c3a2cf16afe29d950df4b6511b2d6c999561608
    https://github.com/scummvm/scummvm/commit/5c3a2cf16afe29d950df4b6511b2d6c999561608
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:21:55-05:00

Commit Message:
SCI32: Add load from launcher support for Phant2

Adding a hook into kPlayDuck to skip the intro video feels kind of
crappy, but it seemed simpler, consistent with the other hooks for
launch loading, and therefore preferable versus hot-patching the
script or messing with PC in the VM or something.

Changed paths:
    engines/sci/engine/guest_additions.cpp
    engines/sci/engine/guest_additions.h
    engines/sci/engine/kvideo.cpp
    engines/sci/engine/selector.cpp
    engines/sci/engine/selector.h


diff --git a/engines/sci/engine/guest_additions.cpp b/engines/sci/engine/guest_additions.cpp
index 373a1e9..17ae74e 100644
--- a/engines/sci/engine/guest_additions.cpp
+++ b/engines/sci/engine/guest_additions.cpp
@@ -261,12 +261,25 @@ bool GuestAdditions::kGetEventHook() const {
 }
 
 bool GuestAdditions::kWaitHook() const {
-	if (_state->_delayedRestoreGameId != -1) {
+	if (_state->_delayedRestoreGameId != -1 &&
+		// kWait cannot be used in Phant2 for delayed restore because it is
+		// called during the fade-in of music in the intro room, before graphics
+		// are fully initialized, which causes "Click to continue" text to be
+		// brokenly drawn over the game and then crashes the engine on the next
+		// room transition
+		g_sci->getGameId() != GID_PHANTASMAGORIA2) {
+
 		return g_sci->_guestAdditions->restoreFromLauncher();
 	}
 	return false;
 }
 
+#ifdef ENABLE_SCI32
+bool GuestAdditions::kPlayDuckPlayHook() const {
+	return _state->_delayedRestoreGameId != -1;
+}
+#endif
+
 #pragma mark -
 #pragma mark Integrated save & restore
 
@@ -575,7 +588,19 @@ bool GuestAdditions::restoreFromLauncher() const {
 
 		_restoring = true;
 
-		if (g_sci->getGameId() == GID_SHIVERS) {
+		// Any events queued up before the game restore can cause accidental
+		// input into the game if they are not flushed (this is particularly
+		// noticeable in Phant2, where the game will display "Click to continue"
+		// for one frame if the user clicked during startup)
+		g_sci->getEventManager()->flushEvents();
+
+		if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
+			// Phantasmagoria 2 moves the function that actually restores
+			// a game, and uses a property of the main game object when picking
+			// the save game to restore
+			writeSelectorValue(_segMan, g_sci->getGameObject(), SELECTOR(num), _state->_delayedRestoreGameId - kSaveIdShift);
+			invokeSelector(g_sci->getGameObject(), SELECTOR(reallyRestore));
+		} else if (g_sci->getGameId() == GID_SHIVERS) {
 			// Shivers accepts the save game number as a parameter to
 			// `SHIVERS::restore`
 			reg_t args[] = { make_reg(0, _state->_delayedRestoreGameId - kSaveIdShift) };
diff --git a/engines/sci/engine/guest_additions.h b/engines/sci/engine/guest_additions.h
index eed1ee3..cfa1139 100644
--- a/engines/sci/engine/guest_additions.h
+++ b/engines/sci/engine/guest_additions.h
@@ -146,6 +146,13 @@ public:
 	 */
 	bool kWaitHook() const;
 
+#ifdef ENABLE_SCI32
+	/**
+	 * Guest additions hook for kPlayDuck(Play).
+	 */
+	bool kPlayDuckPlayHook() const;
+#endif
+
 #pragma mark -
 #pragma mark Integrated save & restore
 
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 37e4eee..feb98f2 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -42,6 +42,7 @@
 #include "video/qt_decoder.h"
 #include "sci/video/seq_decoder.h"
 #ifdef ENABLE_SCI32
+#include "sci/engine/guest_additions.h"
 #include "sci/graphics/frameout.h"
 #include "sci/graphics/video32.h"
 #include "sci/video/robot_decoder.h"
@@ -484,6 +485,9 @@ reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv) {
 }
 
 reg_t kPlayDuckPlay(EngineState *s, int argc, reg_t *argv) {
+	if (g_sci->_guestAdditions->kPlayDuckPlayHook()) {
+		return NULL_REG;
+	}
 	kPlayDuckOpen(s, argc, argv);
 	g_sci->_video32->getDuckPlayer().play(-1);
 	g_sci->_video32->getDuckPlayer().close();
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index e99f734..971802a 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -224,6 +224,8 @@ void Kernel::mapSelectors() {
 	FIND_SELECTOR(physicalBar);
 	FIND_SELECTOR(init);
 	FIND_SELECTOR(scratch);
+	FIND_SELECTOR(num);
+	FIND_SELECTOR(reallyRestore);
 #endif
 }
 
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index d26a0d7..e2afa74 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -181,6 +181,8 @@ struct SelectorCache {
 	Selector physicalBar; // for Phant2 volume sync
 	Selector init; // for Phant2 save/load patching
 	Selector scratch; // for Phant2 save/load patching
+	Selector num; // for Phant2 restore from launcher
+	Selector reallyRestore; // for Phant2 restore from launcher
 #endif
 };
 


Commit: caa8293fcec4441bd0470ba545877aaf8145d81f
    https://github.com/scummvm/scummvm/commit/caa8293fcec4441bd0470ba545877aaf8145d81f
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-07-30T19:22:35-05:00

Commit Message:
SCI32: Make sure audio is not paused from the future

This can occur when a save game from the past is loaded and the
audio system was paused prior to loading the save game. This was
fixed eventually in SSCI somewhere around GK2, since it pauses
all audio before restoring a game and then resumes it after the
save game is loaded (after all of the audio channels have been
added from the save game). Since this would seem to be a problem
for earlier games as well, this change is applied universally
instead of being conditionally applied only to the games with
interpreters containing this change.

This patch contains some additional sanity checks that emit
warnings if individual channels end up being started from the
future. There was never such checking in SSCI, and it does not
seem likely to ever happen, but it is unclear right now if this is
an actual problem or not.

Changed paths:
    engines/sci/sound/audio32.cpp


diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index be3f99d..7622305 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -842,6 +842,9 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool
 	channel.startedAtTick = now;
 
 	if (_numActiveChannels == 1) {
+		if (_pausedAtTick) {
+			_pausedAtTick = now;
+		}
 		_startedAtTick = now;
 	}
 
@@ -868,18 +871,29 @@ bool Audio32::resume(const int16 channelIndex) {
 			AudioChannel &channel = getChannel(i);
 			if (!channel.pausedAtTick) {
 				channel.startedAtTick += now - _pausedAtTick;
+				if (channel.startedAtTick > now) {
+					warning("%s is being resumed in the future", channel.id.toString().c_str());
+				}
 			}
 		}
 
 		_startedAtTick += now - _pausedAtTick;
+		if (_startedAtTick > now) {
+			warning("Audio32 is being resumed in the future");
+		}
 		_pausedAtTick = 0;
 		return true;
 	} else if (channelIndex == kRobotChannel) {
 		for (int i = 0; i < _numActiveChannels; ++i) {
 			AudioChannel &channel = getChannel(i);
 			if (channel.robot) {
-				channel.startedAtTick += now - channel.pausedAtTick;
-				channel.pausedAtTick = 0;
+				if (channel.pausedAtTick) {
+					channel.startedAtTick += now - channel.pausedAtTick;
+					if (channel.startedAtTick > now) {
+						warning("Robot audio is being resumed in the future");
+					}
+					channel.pausedAtTick = 0;
+				}
 				return true;
 			}
 		}
@@ -887,6 +901,9 @@ bool Audio32::resume(const int16 channelIndex) {
 		AudioChannel &channel = getChannel(channelIndex);
 		if (channel.pausedAtTick) {
 			channel.startedAtTick += now - channel.pausedAtTick;
+			if (channel.startedAtTick > now) {
+				warning("%s is being resumed in the future", channel.id.toString().c_str());
+			}
 			channel.pausedAtTick = 0;
 			return true;
 		}





More information about the Scummvm-git-logs mailing list