[Scummvm-git-logs] scummvm master -> 56ea963cea3cdf04c44d3d6f545df664f4a27e8d

sluicebox 22204938+sluicebox at users.noreply.github.com
Mon Jun 10 00:36:02 CEST 2019


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

Summary:
9326f2f31d SCI: Replace SQ4 script patch with workaround
56ea963cea SCI: Create message workaround system


Commit: 9326f2f31d4cd33131c8c384694369f4b27ed600
    https://github.com/scummvm/scummvm/commit/9326f2f31d4cd33131c8c384694369f4b27ed600
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2019-06-09T15:35:57-07:00

Commit Message:
SCI: Replace SQ4 script patch with workaround

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


diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 4412ffb..3e8cb7d 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -11894,54 +11894,6 @@ static const uint16 sq4CdPatchMazeTalkMessage[] = {
 	PATCH_END
 };
 
-// Smelling the sidewalk in room 35 results in a missing message due to not
-//  passing the cond parameter to Sq4GlobalNarrator:say. We pass the parameter
-//  and make room by removing an unused parameter passed to theRoom:doVerb.
-//
-// Applies to: English PC CD
-// Responsible method: sidewalk1:doVerb(6)
-// Fixes bug #10917
-static const uint16 sq4CdSignatureSidewalkSmellMessage[] = {
-	0x31, 0x10,                         // bnt 10
-	SIG_ADDTOOFFSET(+9),
-	SIG_MAGICDWORD,
-	0x76,                               // push0
-	0x81, 0x59,                         // lag 59
-	0x4a, 0x0a,                         // send 0a [ Sq4GlobalNarrator modNum: 25 say: ]
-	0x33, 0x24,                         // jmp 24  [ end of method ]
-	0x3c,                               // dup
-	0x35, 0x09,                         // ldi 09 [ rope ]
-	0x1a,                               // eq?
-	0x31, 0x15,                         // bnt 15
-	0x38, SIG_ADDTOOFFSET(+2),          // pushi doVerb
-	0x7a,                               // push2
-	0x8f, 0x01,                         // lsp 01 [ verb ]
-	0x8f, 0x02,                         // lsp 02 [ unused by theRoom:doVerb ]
-	SIG_ADDTOOFFSET(+9),
-	0x4a, 0x08,                         // send 08 [ theRoom doVerb: verb param2 ]
-	SIG_END
-};
-
-static const uint16 sq4CdPatchSidewalkSmellMessage[] = {
-	0x31, 0x12,                         // bnt 12
-	PATCH_ADDTOOFFSET(+9),
-	0x78,                               // push1
-	0x39, 0x0b,                         // push 0b
-	0x81, 0x59,                         // lag 59
-	0x4a, 0x0c,                         // send 0c [ Sq4GlobalNarrator modNum: 25 say: 11 ]
-	0x33, 0x22,                         // jmp 22  [ end of method ]
-	0x3c,                               // dup
-	0x35, 0x09,                         // ldi 09 [ rope ]
-	0x1a,                               // eq?
-	0x31, 0x13,                         // bnt 13
-	0x38, PATCH_GETORIGINALUINT16(+25), // pushi doVerb
-	0x78,                               // push1
-	0x8f, 0x01,                         // lsp 01 [ verb ]
-	PATCH_ADDTOOFFSET(+9),
-	0x4a, 0x06,                         // send 06 [ theRoom doVerb: verb ]
-	PATCH_END
-};
-
 // Talking to the red shopper in the mall has a 5% chance of a funny message but
 //  this script is broken in the CD version. After the first time the wrong
 //  message tuple is attempted by the narrator as its modNum value is cleared.
@@ -12648,7 +12600,6 @@ static const SciScriptPatcherEntry sq4Signatures[] = {
 	{  true,   376, "Floppy: click atm card on sequel police fix",    1, sq4FloppySignatureClickAtmCardOnSequelPolice,  sq4FloppyPatchClickAtmCardOnSequelPolice },
 	{  true,   376, "Floppy: throw stuff at sequel police fix",       1, sq4FloppySignatureThrowStuffAtSequelPolice,    sq4FloppyPatchThrowStuffAtSequelPolice },
 	{  true,   700, "Floppy: throw stuff at sequel police fix",       1, sq4FloppySignatureThrowStuffAtSequelPolice,    sq4FloppyPatchThrowStuffAtSequelPolice },
-	{  true,    35, "CD: sidewalk smell message fix",                 1, sq4CdSignatureSidewalkSmellMessage,            sq4CdPatchSidewalkSmellMessage },
 	{  true,    45, "CD: walk in from below for room 45 fix",         1, sq4CdSignatureWalkInFromBelowRoom45,           sq4CdPatchWalkInFromBelowRoom45 },
 	{  true,   105, "Floppy: sewer lockup fix",                       1, sq4FloppySignatureSewerLockup,                 sq4FloppyPatchSewerLockup },
 	{  true,   105, "CD: sewer lockup fix",                           1, sq4CDSignatureSewerLockup,                     sq4CDPatchSewerLockup },
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index ee4e11f..1fd99e4 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -326,6 +326,7 @@ const SciWorkaroundEntry uninitializedReadForParamWorkarounds[] = {
 	{ GID_PHANTASMAGORIA2,-1, 64926,  0,              "Thumb", "action",                          NULL,     1,     1,{ WORKAROUND_FAKE,   0 } }, // When dragging one of the volume sliders and releasing the mouse button over the +/- buttons
 	{ GID_PHANTASMAGORIA2,-1, 63019,  0,     "WynDocTextView", "cue",                             NULL,     2,     2,{ WORKAROUND_FAKE,   0 } }, // When dragging the slider next to an e-mail message
 	{ GID_SHIVERS,        -1, 64918,  0,                "Str", "strip",                           NULL,     1,     1,{ WORKAROUND_FAKE,   0 } }, // When starting a new game and entering a name
+	{ GID_SQ4,            35,   928,  0,           "Narrator", "say",                             NULL,     1,     1,{ WORKAROUND_FAKE,  11 } }, // Clicking smell on sidewalk, fixes message due to missing say parameter in sidewalk1:doVerb(6) - bug #10917
 	SCI_WORKAROUNDENTRY_TERMINATOR
 };
 


Commit: 56ea963cea3cdf04c44d3d6f545df664f4a27e8d
    https://github.com/scummvm/scummvm/commit/56ea963cea3cdf04c44d3d6f545df664f4a27e8d
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2019-06-09T15:35:57-07:00

Commit Message:
SCI: Create message workaround system

Adds a new workaround system for known broken messages and their
corresponding audio and sync resources. This replaces all special
cases in c++ and several script patches with data structures and
generic handling.

Common message bugs:
- Wrong tuple requested by game script
- Wrong tuple in message resource
- Wrong message text that exists in another record
- Missing message text
- Audio or sync resource with different tuple than message

Changed paths:
    engines/sci/engine/message.cpp
    engines/sci/engine/message.h
    engines/sci/engine/script_patches.cpp
    engines/sci/engine/workarounds.cpp
    engines/sci/engine/workarounds.h
    engines/sci/resource.cpp
    engines/sci/resource_audio.cpp


diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp
index 88aa735..0688b1a 100644
--- a/engines/sci/engine/message.cpp
+++ b/engines/sci/engine/message.cpp
@@ -25,6 +25,7 @@
 #include "sci/engine/kernel.h"
 #include "sci/engine/seg_manager.h"
 #include "sci/engine/state.h"
+#include "sci/engine/workarounds.h"
 #include "sci/util.h"
 
 namespace Sci {
@@ -183,10 +184,17 @@ public:
 #endif
 
 bool MessageState::getRecord(CursorStack &stack, bool recurse, MessageRecord &record) {
-	Resource *res = g_sci->getResMan()->findResource(ResourceId(kResourceTypeMessage, stack.getModule()), false);
+	// find a workaround for the requested message and use the prescribed module
+	int module = stack.getModule();
+	MessageTuple &tuple = stack.top();
+	SciMessageWorkaroundSolution workaround = findMessageWorkaround(module, tuple.noun, tuple.verb, tuple.cond, tuple.seq);
+	if (workaround.type != MSG_WORKAROUND_NONE) {
+		module = workaround.module;
+	}
+	Resource *res = g_sci->getResMan()->findResource(ResourceId(kResourceTypeMessage, module), false);
 
 	if (!res) {
-		warning("Failed to open message resource %d", stack.getModule());
+		warning("Failed to open message resource %d", module);
 		return false;
 	}
 
@@ -224,198 +232,51 @@ bool MessageState::getRecord(CursorStack &stack, bool recurse, MessageRecord &re
 		return false;
 	}
 
-	while (1) {
-		MessageTuple &t = stack.top();
-
-		// Fix known incorrect message tuples
-		// TODO: Add a more generic mechanism, like the one we have for
-		// script workarounds, for cases with incorrect sync resources,
-		// like the ones below.
-		if (g_sci->getGameId() == GID_QFG1VGA && stack.getModule() == 322 &&
-			t.noun == 14 && t.verb == 1 && t.cond == 19 && t.seq == 1) {
-			// Talking to Kaspar the shopkeeper - bug #3604944
-			t.verb = 2;
-		}
-
-		if (g_sci->getGameId() == GID_PQ1 && stack.getModule() == 38 &&
-			t.noun == 10 && t.verb == 4 && t.cond == 8 && t.seq == 1) {
-			// Using the hand icon on Keith in the Blue Room - bug #3605654
-			t.cond = 9;
-		}
-
-		if (g_sci->getGameId() == GID_PQ1 && stack.getModule() == 38 &&
-			t.noun == 10 && t.verb == 1 && t.cond == 0 && t.seq == 1) {
-			// Using the eye icon on Keith in the Blue Room - bug #3605654
-			t.cond = 13;
-		}
-
-		if (g_sci->getGameId() == GID_QFG4 && stack.getModule() == 16 &&
-			t.noun == 49 && t.verb == 1 && t.cond == 0 && t.seq == 2) {
-			// Examining the statue inventory item from the monastery - bug #10770
-			// The description says "squid-like monster", yet the icon is
-			// clearly an insect. It turned Chief into "an enormous beetle". We
-			// change the phrase to "monstrous insect".
-			//
-			// Note: The German string contains accented characters.
-			//  0x84 "a with diaeresis"
-			//  0x94 "o with diaeresis"
-			//
-			// Values were pulled from SCI Companion's raw message data. They
-			// are escaped that way here, as encoded bytes.
-			record.tuple = t;
-			record.refTuple = MessageTuple();
-			record.talker = 99;
-			if (g_sci->getSciLanguage() == K_LANG_GERMAN) {
-				record.string = "Die groteske Skulptur eines schrecklichen, monstr\x94sen insekts ist sorgf\x84ltig in die Einkaufstasche eingewickelt.";
-				record.length = 112;
-			} else {
-				record.string = "Carefully wrapped in a shopping bag is the grotesque sculpture of a horrible, monstrous insect.";
-				record.length = 95;
-			}
-			delete reader;
-			return true;
-		}
-
-		if (g_sci->getGameId() == GID_QFG4 && stack.getModule() == 579 &&
-			t.noun == 0 && t.verb == 0 && t.cond == 0 && t.seq == 1) {
-			// Talking with the Leshy and telling him about "bush in goo" - bug #10137
-			t.verb = 1;
-		}
-
-		if (g_sci->getGameId() == GID_QFG4 && g_sci->isCD() && stack.getModule() == 520 &&
-			t.noun == 2 && t.verb == 59 && t.cond == 0) {
-			// The CD edition mangled the Rusalka flowers dialogue. - bug #10849
-			// In the floppy edition, there are 3 lines, the first from
-			// the narrator, then two from Rusalka. The CD edition omits
-			// narration and only has the 3rd text, with the 2nd audio! The
-			// 3rd audio is orphaned but available.
-			//
-			// We only restore Rusalka's lines, providing the correct text
-			// for seq:1 to match the audio. We respond to seq:2 requests
-			// with Rusalka's last text. The orphaned audio (seq:3) has its
-			// tuple adjusted to seq:2 in resource_audio.cpp.
-			if (t.seq == 1) {
-				record.tuple = t;
-				record.refTuple = MessageTuple();
-				record.talker = 28;
-				record.string = "Thank you for the beautiful flowers.  No one has been so nice to me since I can remember.";
-				record.length = 89;
-				delete reader;
-				return true;
-			} else if (t.seq == 2) {
-				// The CD edition ships with this text at seq:1.
-				//  Look it up instead of hardcoding.
-				t.seq = 1;
-				if (!reader->findRecord(t, record)) {
-					delete reader;
-					return false;
-				}
-				t.seq = 2;             // Prevent an endless 2=1 -> 2=1 -> 2=1... loop.
-				record.tuple.seq = 2;  // Make the record seq:2 to get the seq:2 audio.
+	// apply the message workaround
+	if (workaround.type == MSG_WORKAROUND_REMAP) {
+		// remap the request to a different message record.
+		//  this alters the stack, nextMessage() will return the next
+		//  record in the sequence following the returned record.
+		stack.setModule(module);
+		tuple.noun = workaround.noun;
+		tuple.verb = workaround.verb;
+		tuple.cond = workaround.cond;
+		tuple.seq = workaround.seq;
+	} else if (workaround.type == MSG_WORKAROUND_FAKE) {
+		// return a fake message record hard-coded in the workaround.
+		//  this leaves the stack unchanged.
+		record.tuple = stack.top();
+		record.refTuple = MessageTuple();
+		record.string = workaround.text;
+		record.length = strlen(workaround.text);
+		record.talker = workaround.talker;
+		delete reader;
+		return true;
+	} else if (workaround.type == MSG_WORKAROUND_EXTRACT) {
+		// extract and return text from a different message record.
+		//  use the talker provided by the workaround since the correct value
+		//  could be in either, or neither, of the records.
+		//  this leaves the stack unchanged.
+		MessageTuple textTuple(workaround.noun, workaround.verb, workaround.cond, workaround.seq);
+		MessageRecord textRecord;
+		if (reader->findRecord(textTuple, textRecord)) {
+			uint32 textLength = (workaround.substringLength == 0) ? textRecord.length : workaround.substringLength;
+			if (workaround.substringIndex + textLength <= textRecord.length) {
+				record.tuple = stack.top();
 				record.refTuple = MessageTuple();
+				record.string = textRecord.string + workaround.substringIndex;
+				record.length = textLength;
+				record.talker = workaround.talker;
 				delete reader;
 				return true;
 			}
 		}
+	}
 
-		if (g_sci->getGameId() == GID_LAURABOW2 && !g_sci->isCD() && stack.getModule() == 1885 &&
-			t.noun == 1 && t.verb == 6 && t.cond == 16 && t.seq == 4 &&
-			(g_sci->getEngineState()->currentRoomNumber() == 350 ||
-			 g_sci->getEngineState()->currentRoomNumber() == 360 ||
-			 g_sci->getEngineState()->currentRoomNumber() == 370)) {
-			// Asking Yvette about Tut in act 2 party - bug #10723
-			// Skip the last two lines of dialogue about a murder that hasn't occurred yet.
-			// Sierra fixed this in cd version by creating a copy of this message without those lines.
-			// Room-specific as the message is used again later where it should display in full.
-			t.seq += 2;
-		}
-
-		// Fill in known missing message tuples
-		if (g_sci->getGameId() == GID_SQ4 && stack.getModule() == 16 &&
-			t.noun == 7 && t.verb == 0 && t.cond == 3 && t.seq == 1) {
-			// This fixes the error message shown when speech and subtitles are
-			// enabled simultaneously in SQ4 - the (very) long dialog when Roger
-			// is talking with the aliens is missing - bug #3538416.
-			record.tuple = t;
-			record.refTuple = MessageTuple();
-			record.talker = 7;	// Roger
-			// The missing text is just too big to fit in one speech bubble, and
-			// if it's added here manually and drawn on screen, it's painted over
-			// the entrance in the back where the Sequel Police enters, so it
-			// looks very ugly. Perhaps this is why this particular text is missing,
-			// as the text shown in this screen is very short (one-liners).
-			// Just output an empty string here instead of showing an error.
-			record.string = "";
-			record.length = 0;
-			delete reader;
-			return true;
-		}
-
-		// FPFP CD has several message sequences where audio and text were left
-		//  out of sync. This is probably because this version didn't formally
-		//  support text mode. Most of these texts just say "Dummy Msg". All the
-		//  real text is there but it's either concatenated or in a different
-		//  tuple. We extract and return the correct text. Fixes #10964
-		if (g_sci->getGameId() == GID_FREDDYPHARKAS && g_sci->isCD() &&
-			g_sci->getLanguage() == Common::EN_ANY && !g_sci->isDemo()) {
-			byte textSeq = 0;
-			uint32 substringIndex = 0;
-			uint32 substringLength = 0;
-
-			// lever brothers' introduction
-			if (stack.getModule() == 220 && t.noun == 24 && t.verb == 0 && t.cond == 0) {
-				switch (t.seq) {
-				case 1: textSeq = 1; substringIndex = 0;   substringLength = 25; break;
-				case 2: textSeq = 1; substringIndex = 26;  substringLength = 20; break;
-				case 3: textSeq = 1; substringIndex = 47;  substringLength = 58; break;
-				case 4: textSeq = 1; substringIndex = 106; substringLength = 34; break;
-				case 5: textSeq = 1; substringIndex = 141; substringLength = 27; break;
-				case 6: textSeq = 1; substringIndex = 169; substringLength = 29; break;
-				case 7: textSeq = 1; substringIndex = 199; substringLength = 52; break;
-				case 8: textSeq = 1; substringIndex = 252; substringLength = 37; break;
-				}
-			}
-
-			// kenny's introduction
-			if (stack.getModule() == 220 && t.noun == 30 && t.verb == 0 && t.cond == 0) {
-				switch (t.seq) {
-				case 3: textSeq = 3; substringIndex = 0;  substringLength = 14;  break;
-				case 4: textSeq = 3; substringIndex = 15; substringLength = 245; break;
-				case 5: textSeq = 4; break;
-				}
-			}
-
-			// helen swatting flies
-			if (stack.getModule() == 660 && t.noun == 35 && t.verb == 0 && t.cond == 0) {
-				switch (t.seq) {
-				case 1: textSeq = 1; substringIndex = 0;   substringLength = 42; break;
-				case 2: textSeq = 1; substringIndex = 43;  substringLength = 93; break;
-				case 3: textSeq = 1; substringIndex = 137; substringLength = 72; break;
-				case 4: textSeq = 2; break;
-				case 5: textSeq = 1; substringIndex = 210; substringLength = 57; break;
-				case 6: textSeq = 3; break;
-				}
-			}
-
-			// find the original message record and the record that contains the
-			//  correct text, then use the correct substring. the original must
-			//  be used to preserve its correct talker and tuple values.
-			if (textSeq != 0 && reader->findRecord(t, record)) {
-				MessageTuple textTuple(t.noun, t.verb, t.cond, textSeq);
-				MessageRecord textRecord;
-				if (reader->findRecord(textTuple, textRecord)) {
-					uint32 textLength = (substringLength == 0) ? textRecord.length : substringLength;
-					if (substringIndex + textLength <= textRecord.length) {
-						record.string = textRecord.string + substringIndex;
-						record.length = textLength;
-						delete reader;
-						return true;
-					}
-				}
-			}
-		}
+	while (1) {
+		tuple = stack.top();
 
-		if (!reader->findRecord(t, record)) {
+		if (!reader->findRecord(tuple, record)) {
 			// Tuple not found
 			if (recurse && (stack.size() > 1)) {
 				stack.pop();
@@ -430,7 +291,7 @@ bool MessageState::getRecord(CursorStack &stack, bool recurse, MessageRecord &re
 			MessageTuple &ref = record.refTuple;
 
 			if (ref.noun || ref.verb || ref.cond) {
-				t.seq++;
+				tuple.seq++;
 				stack.push(ref);
 				continue;
 			}
diff --git a/engines/sci/engine/message.h b/engines/sci/engine/message.h
index 8a1c548..2777cd5 100644
--- a/engines/sci/engine/message.h
+++ b/engines/sci/engine/message.h
@@ -56,6 +56,7 @@ public:
 	}
 
 	int getModule() const { return _module; }
+	void setModule(int module) { _module = module; }
 
 private:
 	int _module;
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 3e8cb7d..6574eb4 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -2091,28 +2091,6 @@ static const uint16 gk1Day5MoselyVevePointsPatch[] = {
 	PATCH_END
 };
 
-// GK1 english pc floppy has a missing message when showing certain items to
-//  Magentia in room 290 such as the flashlight. This triggers an error message.
-//  We fix this by passing GkMessager:say the correct cond as later versions do.
-//
-// Applies to: English PC Floppy 1.0
-// Responsible method: magentia:doVerb
-// Fixes bug: #10782
-static const uint16 gk1ShowMagentiaItemSignature[] = {
-	SIG_MAGICDWORD,
-	0x76,                           // push0
-	0x39, 0x23,                     // pushi 23 [ invalid message cond ]
-	0x76,                           // push0
-	0x81, 0x5b,                     // lag global[5b] [ GkMessager ]
-	SIG_END
-};
-
-static const uint16 gk1ShowMagentiaItemPatch[] = {
-	PATCH_ADDTOOFFSET(+1),
-	0x39, 0x00,                     // pushi 0 [ "Does this mean anything to you?" ]
-	PATCH_END
-};
-
 // The day 5 snake attack has speed, audio, and graphics problems.
 //  These occur in all versions and also in Sierra's interpreter.
 //
@@ -2459,85 +2437,6 @@ static const uint16 gk1GranRoomInitPatch[] = {
 	PATCH_END
 };
 
-// Using the money on the fortune teller Lorelei is scripted to respond with
-//  a different message depending on whether she's sitting or dancing. This
-//  feature manages to have three bugs which all occur in Sierra's interpreter.
-//
-// Bug 1: The script transposes the sitting and dancing responses.
-//        We reverse the test so that the right messages are attempted.
-//
-// Bug 2: The script passes the wrong message tuple when Lorelei is sitting
-//        and so a missing message error occurs. We pass the right tuple.
-//
-// Bug 3: The audio36 resource for message 420 2 32 0 1 has the wrong tuple and
-//        so no audio plays when using the money on Lorelei while dancing.
-//        This is a CD resource bug which we fix in the audio loader.
-//
-// Applies to: All PC Floppy and CD versions. TODO: Test Mac, should apply
-// Responsible method: lorelei:doVerb(32)
-// Fixes bug: #10819
-static const uint16 gk1LoreleiMoneySignature[] = {
-	0x30, SIG_UINT16(0x000d),           // bnt 000d [ lorelei is sitting ]
-	SIG_ADDTOOFFSET(+19),
-	SIG_MAGICDWORD,
-	0x7a,                               // push2        [ noun ]
-	0x8f, 0x01,                         // lsp param[1] [ verb (32d) ]
-	0x39, 0x03,                         // pushi 03     [ cond ]
-	SIG_END
-};
-
-static const uint16 gk1LoreleiMoneyPatch[] = {
-	0x2e, PATCH_UINT16(0x000d),         // bt 000d [ lorelei is dancing ]
-	PATCH_ADDTOOFFSET(+22),
-	0x39, 0x02,                         // pushi 02 [ correct cond ]
-	PATCH_END
-};
-
-// Using "Operate" on the fortune teller Lorelei's right chair causes a
-//  missing message error when she's standing in english pc floppy.
-//  We fix the message tuple as Sierra did in later versions.
-//
-// Applies to: English PC Floppy 1.0
-// Responsible method: chair2:doVerb(8)
-// Fixes bug: #10820
-static const uint16 gk1OperateLoreleiChairSignature[] = {
-	// we have to reach far ahead of the doVerb method for a unique byte
-	//  sequence to base a signature off of. chair2:doVerb has no unique bytes
-	//  and is surrounded by doVerb methods which are duplicates of others.
-	SIG_MAGICDWORD,
-	0x72, SIG_UINT16(0x023a),           // lofsa sPickupVeil
-	0x36,                               // push
-	SIG_ADDTOOFFSET(+913),
-	0x39, 0x03,                         // pushi 03 [ cond ]
-	0x81, 0x5b,                         // lag global[5b] [ gkMessager ]
-	SIG_END
-};
-
-static const uint16 gk1OperateLoreleiChairPatch[] = {
-	PATCH_ADDTOOFFSET(+917),
-	0x39, 0x07,                         // pushi 07 [ correct cond ]
-	PATCH_END
-};
-
-// Using the photocopy of the veve on the artist causes a missing message error
-//  after giving the sketch from the lake and then the original veve file.
-//  This is due to using the wrong verb in the message tuple, which we fix.
-//
-// Applies to: All PC Floppy and CD versions. TODO: Test Mac, should apply
-// Responsible method: artist:doVerb(24)
-// Fixes bug: #10818
-static const uint16 gk1ArtistVeveCopySignature[] = {
-	SIG_MAGICDWORD,
-	0x39, 0x30,                         // pushi 30 [ verb: original veve file ]
-	0x39, 0x1f,                         // pushi 1f [ cond ]
-	SIG_END
-};
-
-static const uint16 gk1ArtistVeveCopyPatch[] = {
-	0x39, 0x18,                         // pushi 18 [ verb: veve photocopy ]
-	PATCH_END
-};
-
 // After phoning Wolfgang on day 7, Gabriel is placed beyond room 220's obstacle
 //  boundary and can walk through walls and behind the room. This also occurs in
 //  the original. The script inconsistently uses accessible and inaccessible
@@ -2577,12 +2476,8 @@ static const SciScriptPatcherEntry gk1Signatures[] = {
 	{  true,   260, "fix day 5 snake attack (1/2)",                1, gk1Day5SnakeAttackSignature1,     gk1Day5SnakeAttackPatch1 },
 	{  true,   260, "fix day 5 snake attack (2/2)",                1, gk1Day5SnakeAttackSignature2,     gk1Day5SnakeAttackPatch2 },
 	{  true,   280, "fix pathfinding in Madame Cazanoux's house",  1, gk1CazanouxPathfindingSignature,  gk1CazanouxPathfindingPatch },
-	{  true,   290, "fix magentia missing message",                1, gk1ShowMagentiaItemSignature,     gk1ShowMagentiaItemPatch },
 	{  true,   380, "fix Gran's room obstacles and ego flicker",   1, gk1GranRoomInitSignature,         gk1GranRoomInitPatch },
 	{  true,   410, "fix day 2 binoculars lockup",                 1, gk1Day2BinocularsLockupSignature, gk1Day2BinocularsLockupPatch },
-	{  true,   410, "fix artist veve photocopy missing message",   1, gk1ArtistVeveCopySignature,       gk1ArtistVeveCopyPatch },
-	{  true,   420, "fix lorelei chair missing message",           1, gk1OperateLoreleiChairSignature,  gk1OperateLoreleiChairPatch },
-	{  true,   420, "fix lorelei money messages",                  1, gk1LoreleiMoneySignature,         gk1LoreleiMoneyPatch },
 	{  true,   710, "fix day 9 vine swing speech playing",         1, gk1Day9VineSwingSignature,        gk1Day9VineSwingPatch },
 	{  true,   710, "fix day 9 mummy animation (floppy)",          1, gk1MummyAnimateFloppySignature,   gk1MummyAnimateFloppyPatch },
 	{  true,   710, "fix day 9 mummy animation (cd)",              1, gk1MummyAnimateCDSignature,       gk1MummyAnimateCDPatch },
@@ -5824,33 +5719,6 @@ static const uint16 laurabow2PatchFixAct4Initialization[] = {
 	PATCH_END
 };
 
-// LB2 Floppy 1.0 attempts to show a non-existent message when using the
-//  carbon paper on the desk lamp in room 550.
-//
-// deskLamp:<noname300>(39), which is really doVerb, attempts to show a message
-//  for its own noun (5) instead of the expected noun (45) when the lamp is off.
-//  This results in "<Messager> 550: 5, 39, 6, 1 not found".
-//
-// We fix this the way Sierra did in version 1.1 by passing the correct noun.
-//
-// Applies to: English floppy 1.000
-// Responsible method: deskLamp:<noname300>(39), which is really doVerb
-// Fixes bug: #10706
-static const uint16 laurabow2SignatureMissingDeskLampMessage[] = {
-	SIG_MAGICDWORD,
-	0x33, 0x1a,                         // jmp 1a
-	0x38, SIG_UINT16(0x0127),           // pushi 127h [ say, hardcoded as we only patch one floppy version ]
-	0x39, 0x03,                         // pushi 3
-	0x67, 0x1a,                         // pTos 1a [ deskLamp noun (5) ]
-	SIG_END
-};
-
-static const uint16 laurabow2PatchMissingDeskLampMessage[] = {
-	PATCH_ADDTOOFFSET(+7),
-	0x39, 0x2d,                         // pushi 45d [ correct message noun ]
-	PATCH_END
-};
-
 // The armor exhibit rooms (440 and 448) have event handlers that fail to handle
 //  all events, preventing messages from being displayed.
 //
@@ -6196,7 +6064,6 @@ static const SciScriptPatcherEntry laurabow2Signatures[] = {
 	{  true,   550, "CD/Floppy: fix back rub east entrance lockup",   1, laurabow2SignatureFixBackRubEastEntranceLockup, laurabow2PatchFixBackRubEastEntranceLockup },
 	{  true,   550, "CD/Floppy: fix disappearing desk items",         1, laurabow2SignatureFixDisappearingDeskItems,     laurabow2PatchFixDisappearingDeskItems },
 	{  true,    26, "Floppy: fix act 4 initialization",               1, laurabow2SignatureFixAct4Initialization,        laurabow2PatchFixAct4Initialization },
-	{  true,   550, "Floppy: missing desk lamp message",              1, laurabow2SignatureMissingDeskLampMessage,       laurabow2PatchMissingDeskLampMessage },
 	{  true,   440, "CD/Floppy: handle armor room events",            1, laurabow2SignatureHandleArmorRoomEvents,        laurabow2PatchHandleArmorRoomEvents },
 	{  true,   448, "CD/Floppy: handle armor hall room events",       1, laurabow2SignatureHandleArmorRoomEvents,        laurabow2PatchHandleArmorRoomEvents },
 	{  true,   600, "Floppy: fix bugs with meat",                     1, laurabow2FloppySignatureFixBugsWithMeat,        laurabow2FloppyPatchFixBugsWithMeat },
@@ -11873,27 +11740,6 @@ static const uint16 sq4PatchEgaIntroDelay[] = {
 	PATCH_END
 };
 
-// Talking in room 520 results in a missing message due to passing the wrong
-//  modNum to the narrator.
-//
-// Applies to: English PC CD
-// Responsible method: rm520:doVerb(2)
-// Fixes bug #10915
-static const uint16 sq4CdSignatureMazeTalkMessage[] = {
-	0x38, SIG_SELECTOR16(modNum),       // pushi modNum
-	SIG_MAGICDWORD,
-	0x78,                               // push1
-	0x38, SIG_UINT16(0x01fe),           // pushi 01fe [ incorrect modNum: 510 ]
-	0x38, SIG_SELECTOR16(say),          // pushi say
-	SIG_END,
-};
-
-static const uint16 sq4CdPatchMazeTalkMessage[] = {
-	PATCH_ADDTOOFFSET(+4),
-	0x38, PATCH_UINT16(0x01f4),         // pushi 01f4 [ correct modNum: 500 ]
-	PATCH_END
-};
-
 // Talking to the red shopper in the mall has a 5% chance of a funny message but
 //  this script is broken in the CD version. After the first time the wrong
 //  message tuple is attempted by the narrator as its modNum value is cleared.
@@ -12613,7 +12459,6 @@ static const SciScriptPatcherEntry sq4Signatures[] = {
 	{  true,   406, "CD/Floppy: zero gravity blast fix",              1, sq4SignatureZeroGravityBlast,                  sq4PatchZeroGravityBlast },
 	{  true,   410, "CD/Floppy: zero gravity blast fix",              1, sq4SignatureZeroGravityBlast,                  sq4PatchZeroGravityBlast },
 	{  true,   411, "CD/Floppy: zero gravity blast fix",              1, sq4SignatureZeroGravityBlast,                  sq4PatchZeroGravityBlast },
-	{  true,   520, "CD: maze talk message fix",                      1, sq4CdSignatureMazeTalkMessage,                 sq4CdPatchMazeTalkMessage },
 	{  true,   545, "CD: vohaul pocketpal text+speech fix",           1, sq4CdSignatureVohaulPocketPalTextSpeech,       sq4CdPatchVohaulPocketPalTextSpeech },
 	{  true,   610, "CD: biker bar door fix",                         1, sq4CdSignatureBikerBarDoor,                    sq4CdPatchBikerBarDoor },
 	{  true,   610, "CD: biker hands-on fix",                         3, sq4CdSignatureBikerHandsOn,                    sq4CdPatchBikerHandsOn },
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index 1fd99e4..a9c9302 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -28,6 +28,7 @@
 #include "sci/engine/workarounds.h"
 
 #define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, -1, 0, NULL, NULL, NULL, 0, 0, { WORKAROUND_NONE, 0 } }
+#define SCI_MESSAGEWORKAROUNDENTRY_TERMINATOR { (SciGameId)0, (SciMedia)0, (kLanguage)0, -1, -1, 0, 0, 0, 0, { MSG_WORKAROUND_NONE, -1, 0, 0, 0, 0, 0, 0, 0, NULL } }
 
 namespace Sci {
 
@@ -1135,4 +1136,203 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun
 	return noneFound;
 }
 
+// Workarounds for known broken messages
+//
+// The most common message bug is a script passing the wrong tuple or a message
+//  having the wrong tuple. This results in a "missing message" message. These
+//  requests just need to be remapped to the right message. In some cases the
+//  message text is incorrect, or missing, in which case it can be extracted
+//  from another record or faked with a hard-coded response.
+//
+// Workarounds can be optionally scoped to media (floppy vs cd), language, and
+//  room number. If a message is remapped, this will also remap any audio36 and
+//  sync36 resources with the same tuple, unless those resources have their own
+//  remapping workarounds due to their tuples being out of sync with their
+//  already broken messages, which we've seen twice.
+//
+// The one kind of broken message that we don't handle yet is when the audio is
+//  completely missing from the game. QFG4 has most of those. In speech-only
+//  mode this means no audio or text and in dual mode the text disappears
+//  quickly as most games use audio length for the message delay. That logic is
+//  in each game's Messager/Narrator/Talker scripts.
+static const SciMessageWorkaroundEntry messageWorkarounds[] = {
+	// game              media             language       room   mod    n    v    c   s    workaround-type          mod    n    v    c   s tlk  idx  len  text
+	// FPFP CD has several message sequences where audio and text were left out of sync - bug #10964
+	//  Some of the texts just say "Dummy Msg" and the real values are concatenated in the first record.
+	// Lever Brothers' intro
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  220,  24,   0,   0,  1, { MSG_WORKAROUND_EXTRACT,  220,  24,   0,   0,  1, 99,   0,  25, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  220,  24,   0,   0,  2, { MSG_WORKAROUND_EXTRACT,  220,  24,   0,   0,  1, 99,  26,  20, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  220,  24,   0,   0,  3, { MSG_WORKAROUND_EXTRACT,  220,  24,   0,   0,  1, 99,  47,  58, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  220,  24,   0,   0,  4, { MSG_WORKAROUND_EXTRACT,  220,  24,   0,   0,  1, 99, 106,  34, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  220,  24,   0,   0,  5, { MSG_WORKAROUND_EXTRACT,  220,  24,   0,   0,  1, 99, 141,  27, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  220,  24,   0,   0,  6, { MSG_WORKAROUND_EXTRACT,  220,  24,   0,   0,  1, 99, 169,  29, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  220,  24,   0,   0,  7, { MSG_WORKAROUND_EXTRACT,  220,  24,   0,   0,  1, 99, 199,  52, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  220,  24,   0,   0,  8, { MSG_WORKAROUND_EXTRACT,  220,  24,   0,   0,  1, 99, 252,  37, NULL } },
+	// Kenny's intro
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  220,  30,   0,   0,  3, { MSG_WORKAROUND_EXTRACT,  220,  30,   0,   0,  3, 99,   0,  14, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  220,  30,   0,   0,  4, { MSG_WORKAROUND_EXTRACT,  220,  30,   0,   0,  3, 99,  15, 245, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  220,  30,   0,   0,  5, { MSG_WORKAROUND_EXTRACT,  220,  30,   0,   0,  4, 99,   0,   0, NULL } },
+	// Helen swatting flies
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  660,  35,   0,   0,  1, { MSG_WORKAROUND_EXTRACT,  660,  35,   0,   0,  1, 53,   0,  42, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  660,  35,   0,   0,  2, { MSG_WORKAROUND_EXTRACT,  660,  35,   0,   0,  1, 53,  43,  93, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  660,  35,   0,   0,  3, { MSG_WORKAROUND_EXTRACT,  660,  35,   0,   0,  1, 53, 137,  72, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  660,  35,   0,   0,  4, { MSG_WORKAROUND_EXTRACT,  660,  35,   0,   0,  2, 53,   0,   0, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  660,  35,   0,   0,  5, { MSG_WORKAROUND_EXTRACT,  660,  35,   0,   0,  1, 53, 210,  57, NULL } },
+	{ GID_FREDDYPHARKAS, SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  660,  35,   0,   0,  6, { MSG_WORKAROUND_EXTRACT,  660,  35,   0,   0,  3, 12,   0,   0, NULL } },
+	// Missing message when clicking flashlight and other items on Magentia in room 290, floppy 1.0 - bug #10782
+	{ GID_GK1,           SCI_MEDIA_FLOPPY, K_LANG_NONE,     -1,  290,   8,   0,  35,  1, { MSG_WORKAROUND_REMAP,    290,   8,   0,   0,  1,  0,   0,   0, NULL } },
+	// Missing message when clicking photocopy of the veve on the artist after giving sketch and original veve in room 410 - bug #10818
+	{ GID_GK1,           SCI_MEDIA_ALL,    K_LANG_NONE,     -1,  410,   3,  48,  31,  1, { MSG_WORKAROUND_REMAP,    410,   3,  24,  31,  1,  0,   0,   0, NULL } },
+	// Missing message when clicking operate on Loreli's right chair in room 420, floppy 1.0 - bug #10820
+	{ GID_GK1,           SCI_MEDIA_FLOPPY, K_LANG_NONE,     -1,  420,   4,   8,   3,  1, { MSG_WORKAROUND_REMAP,    420,   4,   8,   7,  1,  0,   0,   0, NULL } },
+	// Clicking money on Loreli when sitting or dancing in room 420 - bug #10819
+	//  The script transposes sitting vs dancing responses, passes an invalid cond for one of them, and the
+	//  audio36 for the the other has the wrong tuple, which we fix in the audio36 workarounds.
+	{ GID_GK1,           SCI_MEDIA_ALL,    K_LANG_NONE,     -1,  420,   2,  32,   3,  1, { MSG_WORKAROUND_REMAP,    420,   2,  32,   0,  1,  0,   0,   0, NULL } },
+	{ GID_GK1,           SCI_MEDIA_ALL,    K_LANG_NONE,     -1,  420,   2,  32,   0,  1, { MSG_WORKAROUND_REMAP,    420,   2,  32,   2,  1,  0,   0,   0, NULL } },
+	// Asking Yvette about Tut in act 2 party in floppy version - bug #10723
+	//  The last two sequences in this five part message reveal a murder that hasn't occurred yet.
+	//  We skip these as to not spoil the plot, but only in the act 2 rooms, as the message is used
+	//  in later acts where all five parts are appropriate. Sierra fixed this in the CD version by
+	//  creating a new three-sequence message for act 2 to accomplish the same thing.
+	{ GID_LAURABOW2,     SCI_MEDIA_FLOPPY, K_LANG_NONE,    350, 1885,   1,   6,  16,  4, { MSG_WORKAROUND_REMAP,   1885,   1,   6,  16,  6,  0,   0,   0, NULL } },
+	{ GID_LAURABOW2,     SCI_MEDIA_FLOPPY, K_LANG_NONE,    360, 1885,   1,   6,  16,  4, { MSG_WORKAROUND_REMAP,   1885,   1,   6,  16,  6,  0,   0,   0, NULL } },
+	{ GID_LAURABOW2,     SCI_MEDIA_FLOPPY, K_LANG_NONE,    370, 1885,   1,   6,  16,  4, { MSG_WORKAROUND_REMAP,   1885,   1,   6,  16,  6,  0,   0,   0, NULL } },
+	// Missing message when clicking carbon paper on desk lamp in room 550, floppy 1.0 - bug #10706
+	{ GID_LAURABOW2,     SCI_MEDIA_FLOPPY, K_LANG_NONE,     -1,  550,   5,  39,   6,  1, { MSG_WORKAROUND_REMAP,    550,  45,  39,   6,  1,  0,   0,   0, NULL } },
+	// Using the hand icon on Keith in the Blue Room (missing message) - bug #6253
+	{ GID_PQ1,           SCI_MEDIA_ALL,    K_LANG_NONE,     -1,   38,  10,   4,   8,  1, { MSG_WORKAROUND_REMAP,     38,  10,   4,   9,  1,  0,   0,   0, NULL } },
+	// Using the eye icon on Keith in the Blue Room (no message and wrong talker) - bug #6253
+	{ GID_PQ1,           SCI_MEDIA_ALL,    K_LANG_NONE,     -1,   38,  10,   1,   0,  1, { MSG_WORKAROUND_EXTRACT,   38,  10,   1,  13,  1, 99,   0,   0, NULL } },
+	// Talking to Kaspar the shopkeeper - bug #6250
+	{ GID_QFG1VGA,       SCI_MEDIA_ALL,    K_LANG_NONE,     -1,  322,  14,   1,  19,  1, { MSG_WORKAROUND_REMAP,    322,  14,   2,  19,  1,  0,   0,   0, NULL } },
+	// Talking with the Leshy and telling him about "bush in goo" - bug #10137
+	{ GID_QFG4,          SCI_MEDIA_ALL,    K_LANG_NONE,     -1,  579,   0,   0,   0,  1, { MSG_WORKAROUND_REMAP,    579,   0,   1,   0,  1,  0,   0,   0, NULL } },
+	// Examining the statue inventory item from the monastery - bug #10770
+	// The description says "squid-like monster", yet the icon is
+	// clearly an insect. It turned Chief into "an enormous beetle". We
+	// change the phrase to "monstrous insect". This message is text-only.
+	// Note: The German string contains accented characters.
+	//  0x84 "a with diaeresis"
+	//  0x94 "o with diaeresis"
+	{ GID_QFG4,          SCI_MEDIA_ALL,    K_LANG_ENGLISH,  -1,   16,  49,   1,   0,  2, { MSG_WORKAROUND_FAKE,      16,  49,   1,   0,  2, 99,   0,   0, "Carefully wrapped in a shopping bag is the grotesque sculpture of a horrible, monstrous insect." } },
+	{ GID_QFG4,          SCI_MEDIA_ALL,    K_LANG_GERMAN,   -1,   16,  49,   1,   0,  2, { MSG_WORKAROUND_FAKE,      16,  49,   1,   0,  2, 99,   0,   0, "Die groteske Skulptur eines schrecklichen, monstr\x94sen insekts ist sorgf\x84ltig in die Einkaufstasche eingewickelt." } },
+	// The CD edition mangled the Rusalka flowers dialogue. - bug #10849
+	// In the floppy edition, there are 3 lines, the first from
+	// the narrator, then two from Rusalka. The CD edition omits
+	// narration and only has the 3rd text, with the 2nd audio! The
+	// 3rd audio is orphaned but available.
+	// We only restore Rusalka's lines, providing the correct text
+	// for seq:1 to match the audio. We respond to seq:2 requests
+	// with Rusalka's last text. The orphaned audio (seq:3) has its
+	// tuple remapped to seq:2 in an audio workaround below.
+	{ GID_QFG4,          SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  520,   2,  59,   0,  1, { MSG_WORKAROUND_FAKE,     520,   2,  59,   0,  1, 28,   0,   0, "Thank you for the beautiful flowers.  No one has been so nice to me since I can remember." } },
+	{ GID_QFG4,          SCI_MEDIA_CD,     K_LANG_ENGLISH,  -1,  520,   2,  59,   0,  2, { MSG_WORKAROUND_EXTRACT,  520,   2,  59,   0,  1, 28,   0,   0, NULL } },
+	// This fixes the error message shown when speech and subtitles are
+	// enabled simultaneously in SQ4 - the (very) long dialog when Roger
+	// is talking with the aliens is missing - bug #6067.
+	// The missing text is just too big to fit in one speech bubble, and
+	// if it's added here manually and drawn on screen, it's painted over
+	// the entrance in the back where the Sequel Police enters, so it
+	// looks very ugly. Perhaps this is why this particular text is missing,
+	// as the text shown in this screen is very short (one-liners).
+	// Just output an empty string here instead of showing an error.
+	{ GID_SQ4,           SCI_MEDIA_CD,     K_LANG_NONE,     -1,   16,   7,   0,   3,  1, { MSG_WORKAROUND_FAKE,      16,   7,   0,   3,  1,  7,   0,   0, "" } },
+	// Missing message when clicking talk in room 520 - bug #10915
+	{ GID_SQ4,           SCI_MEDIA_CD,     K_LANG_NONE,     -1,  510,  99,   0,   3,  1, { MSG_WORKAROUND_REMAP,    500,  99,   0,   3,  1,  0,   0,   0, NULL } },
+	SCI_MESSAGEWORKAROUNDENTRY_TERMINATOR
+};
+
+// Audio36 workarounds are for when an audio36 resource's tuple is out of sync
+//  with its corresponding message, in which case it can be remapped. Any sync36
+//  resource with the same tuple will also be remapped. Remapping is the only
+//  workaround type available to this table. If a tuple is remapped both here
+//  and in message workarounds, this remapping is used.
+static const SciMessageWorkaroundEntry audio36Workarounds[] = {
+	// game              media             language       room   mod    n    v    c   s    workaround-type          mod    n    v    c   s tlk  idx  len  text
+	// Clicking money on Lorelei when dancing - bug #10819 (see message workarounds above)
+	{ GID_GK1,           SCI_MEDIA_CD,     K_LANG_NONE,     -1,  420,   2,  32,   0,  1, { MSG_WORKAROUND_REMAP,    420,   2,  32,   3,  1,  0,   0,   0, NULL } },
+	// Clicking Look on floor grate in room 510 - bug #10848
+	{ GID_QFG4,          SCI_MEDIA_CD,     K_LANG_NONE,     -1,  510,  23,   1,   0,  1, { MSG_WORKAROUND_REMAP,    510, 199,   1,   0,  1,  0,   0,   0, NULL } },
+	// Clicking flowers on Rusalka - bug #10849 (see message workarounds above)
+	{ GID_QFG4,          SCI_MEDIA_CD,     K_LANG_NONE,     -1,  520,   2,  59,   0,  2, { MSG_WORKAROUND_REMAP,    520,   2,  59,   0,  3,  0,   0,   0, NULL } },
+	SCI_MESSAGEWORKAROUNDENTRY_TERMINATOR
+};
+
+// Sync36 workarounds are for when a sync36 resource's tuple is out of sync with
+//  its corresponding message, in which case it can be remapped. This hasn't
+//  been encountered yet, but remapping solves a similar LB2 sync bug.
+static const SciMessageWorkaroundEntry sync36Workarounds[] = {
+	// game              media             language       room   mod    n    v    c   s    workaround-type          mod    n    v    c   s tlk  idx  len  text
+	// Asking yvette about tut in act 2 is missing a sync resource but a duplicate message has a sync resource - bug #9956
+	{ GID_LAURABOW2,     SCI_MEDIA_CD,     K_LANG_NONE,     -1, 1885,   1,   6,  30,  2, { MSG_WORKAROUND_REMAP,   1885,   1,   6,  10,  2,  0,   0,   0, NULL } },
+	SCI_MESSAGEWORKAROUNDENTRY_TERMINATOR
+};
+
+static SciMessageWorkaroundSolution findMessageWorkaround(int module, byte noun, byte verb, byte cond, byte seq, const SciMessageWorkaroundEntry *workaroundList) {
+	const SciMessageWorkaroundEntry *workaround = workaroundList;
+	while (workaround->solution.type != MSG_WORKAROUND_NONE) {
+		if (workaround->gameId == g_sci->getGameId() &&
+			(workaround->media == SCI_MEDIA_ALL ||
+			(workaround->media == SCI_MEDIA_FLOPPY && !g_sci->isCD()) ||
+			(workaround->media == SCI_MEDIA_CD && g_sci->isCD())) &&
+			(workaround->language == K_LANG_NONE ||
+			workaround->language == g_sci->getSciLanguage()) &&
+			(workaround->roomNumber == -1 ||
+			workaround->roomNumber == g_sci->getEngineState()->currentRoomNumber()) &&
+			workaround->module == module &&
+			workaround->noun == noun &&
+			workaround->verb == verb &&
+			workaround->cond == cond &&
+			workaround->seq == seq) {
+			break;
+		}
+		workaround++;
+	}
+	return workaround->solution;
+}
+
+SciMessageWorkaroundSolution findMessageWorkaround(int module, byte noun, byte verb, byte cond, byte seq) {
+	return findMessageWorkaround(module, noun, verb, cond, seq, messageWorkarounds);
+}
+
+ResourceId remapAudio36ResourceId(const ResourceId &resourceId) {
+	int module = resourceId.getNumber();
+	byte noun = resourceId.getTuple() >> 24;
+	byte verb = (resourceId.getTuple() >> 16) & 0xff;
+	byte cond = (resourceId.getTuple() >> 8) & 0xff;
+	byte seq = resourceId.getTuple() & 0xff;
+
+	SciMessageWorkaroundSolution workaround = findMessageWorkaround(module, noun, verb, cond, seq, audio36Workarounds);
+	if (workaround.type != MSG_WORKAROUND_REMAP) {
+		workaround = findMessageWorkaround(module, noun, verb, cond, seq, messageWorkarounds);
+	}
+
+	if (workaround.type == MSG_WORKAROUND_REMAP) {
+		return ResourceId(resourceId.getType(), workaround.module, workaround.noun, workaround.verb, workaround.cond, workaround.seq);
+	}
+	return resourceId;
+}
+
+ResourceId remapSync36ResourceId(const ResourceId &resourceId) {
+	int module = resourceId.getNumber();
+	byte noun = resourceId.getTuple() >> 24;
+	byte verb = (resourceId.getTuple() >> 16) & 0xff;
+	byte cond = (resourceId.getTuple() >> 8) & 0xff;
+	byte seq = resourceId.getTuple() & 0xff;
+
+	SciMessageWorkaroundSolution workaround = findMessageWorkaround(module, noun, verb, cond, seq, sync36Workarounds);
+	if (workaround.type != MSG_WORKAROUND_REMAP) {
+		workaround = findMessageWorkaround(module, noun, verb, cond, seq, audio36Workarounds);
+	}
+	if (workaround.type != MSG_WORKAROUND_REMAP) {
+		workaround = findMessageWorkaround(module, noun, verb, cond, seq, messageWorkarounds);
+	}
+
+	if (workaround.type == MSG_WORKAROUND_REMAP) {
+		return ResourceId(resourceId.getType(), workaround.module, workaround.noun, workaround.verb, workaround.cond, workaround.seq);
+	}
+	return resourceId;
+}
+
 } // End of namespace Sci
diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h
index 284936b..17ea81f 100644
--- a/engines/sci/engine/workarounds.h
+++ b/engines/sci/engine/workarounds.h
@@ -113,6 +113,49 @@ extern const SciWorkaroundEntry kScrollWindowAdd_workarounds[];
 
 extern SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciCallOrigin *trackOrigin);
 
+enum SciMessageWorkaroundType {
+	MSG_WORKAROUND_NONE,        // only used by terminator or when no workaround was found
+	MSG_WORKAROUND_REMAP,       // use a different tuple instead
+	MSG_WORKAROUND_FAKE,        // use a hard-coded response
+	MSG_WORKAROUND_EXTRACT      // use text from a different record, optionally a substring
+};
+
+enum SciMedia {
+	SCI_MEDIA_ALL,
+	SCI_MEDIA_FLOPPY,
+	SCI_MEDIA_CD
+};
+
+struct SciMessageWorkaroundSolution {
+	SciMessageWorkaroundType type;
+	int module;
+	byte noun;
+	byte verb;
+	byte cond;
+	byte seq;
+	byte talker;
+	uint32 substringIndex;
+	uint32 substringLength;
+	const char *text;
+};
+
+struct SciMessageWorkaroundEntry {
+	SciGameId gameId;
+	SciMedia media;
+	kLanguage language;
+	int roomNumber;
+	int module;
+	byte noun;
+	byte verb;
+	byte cond;
+	byte seq;
+	SciMessageWorkaroundSolution solution;
+};
+
+extern SciMessageWorkaroundSolution findMessageWorkaround(int module, byte noun, byte verb, byte cond, byte seq);
+extern ResourceId remapAudio36ResourceId(const ResourceId &resourceId);
+extern ResourceId remapSync36ResourceId(const ResourceId &resourceId);
+
 } // End of namespace Sci
 
 #endif // SCI_ENGINE_WORKAROUNDS_H
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 8e4a6d1..8e48d0a 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -31,6 +31,7 @@
 #include "common/memstream.h"
 #endif
 
+#include "sci/engine/workarounds.h"
 #include "sci/parser/vocabulary.h"
 #include "sci/resource.h"
 #include "sci/resource_intern.h"
@@ -1134,6 +1135,12 @@ Common::List<ResourceId> ResourceManager::listResources(ResourceType type, int m
 }
 
 Resource *ResourceManager::findResource(ResourceId id, bool lock) {
+	// remap known incorrect audio36 and sync36 resource ids
+	if (id.getType() == kResourceTypeAudio36) {
+		id = remapAudio36ResourceId(id);
+	} else if (id.getType() == kResourceTypeSync36) {
+		id = remapSync36ResourceId(id);
+	}
 	Resource *retval = testResource(id);
 
 	if (!retval)
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index 879b25f..4704963 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -455,28 +455,6 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
 				break;
 			}
 
-			// GK1CD has a message whose audio36 resource has the wrong tuple and never plays.
-			//  The message tuple is 420 2 32 0 1 but the audio36 tuple is 420 2 32 3 1. bug #10819
-			if (g_sci->getGameId() == GID_GK1 && g_sci->isCD() &&
-				map->_mapNumber == 420 && n == 0x02200301) {
-				n = 0x02200001;
-			}
-
-			// QFG4CD has a message whose audio36 resource has the wrong tuple and never plays.
-			//  The message tuple is 510 23 1 0 1 but the audio36 tuple is 510 199 1 0 1. bug #10848
-			if (g_sci->getGameId() == GID_QFG4 && g_sci->isCD() &&
-				map->_mapNumber == 510 && n == 0xc7010001) {
-				n = 0x17010001;
-			}
-
-			// QFG4CD has an orphaned audio36 resource that additionally has the wrong tuple.
-			//  The audio36 tuple is 520 2 59 0 3. The message would be 520 2 59 0 2. bug #10849
-			//  We restore the missing message in message.cpp.
-			if (g_sci->getGameId() == GID_QFG4 && g_sci->isCD() &&
-				map->_mapNumber == 520 && n == 0x023b0003) {
-				n = 0x023b0002;
-			}
-
 			if (isEarly) {
 				offset = ptr.getUint32LE();
 				ptr += 4;
@@ -492,14 +470,6 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
 				// FIXME: The sync36 resource seems to be two bytes too big in KQ6CD
 				// (bytes taken from the RAVE resource right after it)
 				if (syncSize > 0) {
-					// TODO: Add a mechanism to handle cases with missing resources like the ones below
-					//
-					// LB2CD is missing the sync resource for message 1885 1 6 30 2 but it's a duplicate
-					//  of 1885 1 6 16 2 which does have a sync resource so use that for both. bug #9956
-					if (g_sci->getGameId() == GID_LAURABOW2 && map->_mapNumber == 1885 && n == 0x01061002) {
-						addResource(ResourceId(kResourceTypeSync36, map->_mapNumber, 0x01061e02), src, offset, syncSize, map->getLocationName());
-					}
-
 					addResource(ResourceId(kResourceTypeSync36, map->_mapNumber, n & 0xffffff3f), src, offset, syncSize, map->getLocationName());
 				}
 			}





More information about the Scummvm-git-logs mailing list