aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine/script_patches.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine/script_patches.cpp')
-rw-r--r--engines/sci/engine/script_patches.cpp2148
1 files changed, 1899 insertions, 249 deletions
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 03cd1d06e9..d45c689985 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -94,11 +94,15 @@ static const char *const selectorNameTable[] = {
"deskSarg", // Gabriel Knight
"localize", // Freddy Pharkas
"put", // Police Quest 1 VGA
+ "say", // Quest For Glory 1 VGA
+ "contains", // Quest For Glory 2
"solvePuzzle", // Quest For Glory 3
"timesShownID", // Space Quest 1 VGA
"startText", // King's Quest 6 CD / Laura Bow 2 CD for audio+text support
"startAudio", // King's Quest 6 CD / Laura Bow 2 CD for audio+text support
"modNum", // King's Quest 6 CD / Laura Bow 2 CD for audio+text support
+ "cycler", // Space Quest 4 / system selector
+ "setLoop", // Laura Bow 1 Colonel's Bequest
NULL
};
@@ -119,11 +123,15 @@ enum ScriptPatcherSelectors {
SELECTOR_deskSarg,
SELECTOR_localize,
SELECTOR_put,
+ SELECTOR_say,
+ SELECTOR_contains,
SELECTOR_solvePuzzle,
SELECTOR_timesShownID,
SELECTOR_startText,
SELECTOR_startAudio,
- SELECTOR_modNum
+ SELECTOR_modNum,
+ SELECTOR_cycler,
+ SELECTOR_setLoop
};
// ===========================================================================
@@ -371,12 +379,40 @@ static const SciScriptPatcherEntry ecoquest2Signatures[] = {
};
// ===========================================================================
+// Fan-made games
+// Attention: Try to make script patches as specific as possible
+
+// CascadeQuest::autosave in script 994 is called various times to auto-save the game.
+// The script use a fixed slot "999" for this purpose. This doesn't work in ScummVM, because we do not let
+// scripts save directly into specific slots, but instead use virtual slots / detect scripts wanting to
+// create a new slot.
+//
+// For this game we patch the code to use slot 99 instead. kSaveGame also checks for Cascade Quest,
+// will then check, if slot 99 is asked for and will then use the actual slot 0, which is the official
+// ScummVM auto-save slot.
+//
+// Responsible method: CascadeQuest::autosave
+// Fixes bug: #7007
+static const uint16 fanmadeSignatureCascadeQuestFixAutoSaving[] = {
+ SIG_MAGICDWORD,
+ 0x38, SIG_UINT16(0x03e7), // pushi 3E7 (999d) -> save game slot 999
+ 0x74, SIG_UINT16(0x06f8), // lofss "AutoSave"
+ 0x89, 0x1e, // lsg global[1E]
+ 0x43, 0x2d, 0x08, // callk SaveGame
+ SIG_END
+};
+
+static const uint16 fanmadePatchCascadeQuestFixAutoSaving[] = {
+ 0x38, PATCH_UINT16((SAVEGAMEID_OFFICIALRANGE_START - 1)), // fix slot
+ PATCH_END
+};
+
// EventHandler::handleEvent in Demo Quest has a bug, and it jumps to the
// wrong address when an incorrect word is typed, therefore leading to an
// infinite loop. This script bug was not apparent in SSCI, probably because
// event handling was slightly different there, so it was never discovered.
// Fixes bug: #5120
-static const uint16 fanmadeSignatureInfiniteLoop[] = {
+static const uint16 fanmadeSignatureDemoQuestInfiniteLoop[] = {
0x38, SIG_UINT16(0x004c), // pushi 004c
0x39, 0x00, // pushi 00
0x87, 0x01, // lap 01
@@ -387,19 +423,73 @@ static const uint16 fanmadeSignatureInfiniteLoop[] = {
SIG_END
};
-static const uint16 fanmadePatchInfiniteLoop[] = {
+static const uint16 fanmadePatchDemoQuestInfiniteLoop[] = {
PATCH_ADDTOOFFSET(+10),
- 0x30, SIG_UINT16(0x0032), // bnt 0032 [06a8] --> pushi 004c
+ 0x30, PATCH_UINT16(0x0032), // bnt 0032 [06a8] --> pushi 004c
PATCH_END
};
-// script, description, signature patch
+// script, description, signature patch
static const SciScriptPatcherEntry fanmadeSignatures[] = {
- { true, 999, "infinite loop on typo", 1, fanmadeSignatureInfiniteLoop, fanmadePatchInfiniteLoop },
+ { true, 994, "Cascade Quest: fix auto-saving", 1, fanmadeSignatureCascadeQuestFixAutoSaving, fanmadePatchCascadeQuestFixAutoSaving },
+ { true, 999, "Demo Quest: infinite loop on typo", 1, fanmadeSignatureDemoQuestInfiniteLoop, fanmadePatchDemoQuestInfiniteLoop },
SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
+
+// WORKAROUND
+// Freddy Pharkas intro screen
+// Sierra used inner loops for the scaling of the 2 title views.
+// Those inner loops don't call kGameIsRestarting, which is why
+// we do not update the screen and we also do not throttle.
+//
+// This patch fixes this and makes it work.
+// Applies to at least: English PC-CD
+// Responsible method: sTownScript::changeState(1), sTownScript::changeState(3) (script 110)
+static const uint16 freddypharkasSignatureIntroScaling[] = {
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi (setLoop) (009b for PC CD)
+ 0x78, // push1
+ PATCH_ADDTOOFFSET(1), // push0 for first code, push1 for second code
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi (setStep) (0143 for PC CD)
+ 0x7a, // push2
+ 0x39, 0x05, // pushi 05
+ 0x3c, // dup
+ 0x72, SIG_ADDTOOFFSET(+2), // lofsa (view)
+ SIG_MAGICDWORD,
+ 0x4a, 0x1e, // send 1e
+ 0x35, 0x0a, // ldi 0a
+ 0xa3, 0x02, // sal local[2]
+ // start of inner loop
+ 0x8b, 0x02, // lsl local[2]
+ SIG_ADDTOOFFSET(+43), // skip almost all of inner loop
+ 0xa3, 0x02, // sal local[2]
+ 0x33, 0xcf, // jmp [inner loop start]
+ SIG_END
+};
+
+static const uint16 freddypharkasPatchIntroScaling[] = {
+ // remove setLoop(), objects in heap are already prepared, saves 5 bytes
+ 0x38,
+ PATCH_GETORIGINALBYTE(+6),
+ PATCH_GETORIGINALBYTE(+7), // pushi (setStep)
+ 0x7a, // push2
+ 0x39, 0x05, // pushi 05
+ 0x3c, // dup
+ 0x72,
+ PATCH_GETORIGINALBYTE(+13),
+ PATCH_GETORIGINALBYTE(+14), // lofsa (view)
+ 0x4a, 0x18, // send 18 - adjusted
+ 0x35, 0x0a, // ldi 0a
+ 0xa3, 0x02, // sal local[2]
+ // start of new inner loop
+ 0x39, 0x00, // pushi 00
+ 0x43, 0x2c, 0x00, // callk GameIsRestarting <-- add this so that our speed throttler is triggered
+ SIG_ADDTOOFFSET(+47), // skip almost all of inner loop
+ 0x33, 0xca, // jmp [inner loop start]
+ PATCH_END
+};
+
// script 0 of freddy pharkas/CD PointsSound::check waits for a signal and if
// no signal received will call kDoSound(0xD) which is a dummy in sierra sci
// and ScummVM and will use acc (which is not set by the dummy) to trigger
@@ -518,6 +608,7 @@ static const uint16 freddypharkasPatchMacInventory[] = {
static const SciScriptPatcherEntry freddypharkasSignatures[] = {
{ true, 0, "CD: score early disposal", 1, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal },
{ true, 15, "Mac: broken inventory", 1, freddypharkasSignatureMacInventory, freddypharkasPatchMacInventory },
+ { true, 110, "intro scaling workaround", 2, freddypharkasSignatureIntroScaling, freddypharkasPatchIntroScaling },
{ true, 235, "CD: canister pickup hang", 3, freddypharkasSignatureCanisterHang, freddypharkasPatchCanisterHang },
{ true, 320, "ladder event issue", 2, freddypharkasSignatureLadderEvent, freddypharkasPatchLadderEvent },
SCI_SIGNATUREENTRY_TERMINATOR
@@ -562,12 +653,12 @@ static const uint16 gk1SignatureDay6PoliceSleep[] = {
0x34, SIG_UINT16(0x00dc), // ldi 220
0x65, SIG_ADDTOOFFSET(+1), // aTop cycles (1a for PC, 1c for Mac)
0x32, // jmp [end]
- 0
+ SIG_END
};
static const uint16 gk1PatchDay6PoliceSleep[] = {
PATCH_ADDTOOFFSET(+5),
- 0x34, SIG_UINT16(0x002a), // ldi 42
+ 0x34, PATCH_UINT16(0x002a), // ldi 42
0x65, PATCH_GETORIGINALBYTEADJUST(+9, +2), // aTop seconds (1c for PC, 1e for Mac)
PATCH_END
};
@@ -655,12 +746,115 @@ static const uint16 gk1PatchInterrogationBug[] = {
PATCH_END
};
-// script, description, signature patch
+// On day 10 nearly at the end of the game, Gabriel Knight dresses up and right after that
+// someone will be at the door. Gabriel turns around to see what's going on.
+//
+// In ScummVM Gabriel turning around plays endlessly. This is caused by the loop of Gabriel
+// being kept at 1, but view + cel were changed accordingly. The view used - which is view 859 -
+// does not have a loop 1. kNumCels is called on that, BUT kNumCels in SSCI is broken in that
+// regard. It checks for loop > count and not loop >= count and will return basically random data
+// in case loop == count.
+//
+// In SSCI this simply worked by accident. kNumCels returned 0x53 in this case, but later script code
+// fixed that up somehow, so it worked out in the end.
+//
+// The setup for this is done in SDJEnters::changeState(0). The cycler will never reach the goal
+// because the goal will be cel -1, so it loops endlessly.
+//
+// We fix this by adding a setLoop(0).
+//
+// Applies to at least: English PC-CD, German PC-CD
+// Responsible method: sDJEnters::changeState
+static const uint16 gk1SignatureDay10GabrielDressUp[] = {
+ 0x87, 0x01, // lap param[1]
+ 0x65, 0x14, // aTop state
+ 0x36, // push
+ 0x3c, // dup
+ 0x35, 0x00, // ldi 0
+ 0x1a, // eq?
+ 0x30, SIG_UINT16(0x006f), // bnt [next state 1]
+ SIG_ADDTOOFFSET(+84),
+ 0x39, 0x0e, // pushi 0Eh (view)
+ 0x78, // push1
+ SIG_MAGICDWORD,
+ 0x38, SIG_UINT16(0x035B), // pushi 035Bh (859d)
+ 0x38, SIG_UINT16(0x0141), // pushi 0141h (setCel)
+ 0x78, // push1
+ 0x76, // push0
+ 0x38, SIG_UINT16(0x00E9), // pushi 00E9h (setCycle)
+ 0x7a, // push2
+ 0x51, 0x18, // class End
+ 0x36, // push
+ 0x7c, // pushSelf
+ 0x81, 0x00, // lag global[0]
+ 0x4a, 0x14, 0x00, // send 14h
+ // GKEgo::view(859)
+ // GKEgo::setCel(0)
+ // GKEgo::setCycle(End, sDJEnters)
+ 0x32, SIG_UINT16(0x0233), // jmp [ret]
+ // next state
+ 0x3c, // dup
+ 0x35, 0x01, // ldi 01
+ 0x1a, // eq?
+ 0x31, 0x07, // bnt [next state 2]
+ 0x35, 0x02, // ldi 02
+ 0x65, 0x1a, // aTop cycles
+ 0x32, SIG_UINT16(0x0226), // jmp [ret]
+ // next state
+ 0x3c, // dup
+ 0x35, 0x02, // ldi 02
+ 0x1a, // eq?
+ 0x31, 0x2a, // bnt [next state 3]
+ 0x78, // push1
+ SIG_ADDTOOFFSET(+34),
+ // part of state 2 code, delays for 1 cycle
+ 0x35, 0x01, // ldi 1
+ 0x65, 0x1a, // aTop cycles
+ SIG_END
+};
+
+static const uint16 gk1PatchDay10GabrielDressUp[] = {
+ PATCH_ADDTOOFFSET(+9),
+ 0x30, SIG_UINT16(0x0073), // bnt [next state 1] - offset adjusted
+ SIG_ADDTOOFFSET(+84 + 11),
+ // added by us: setting loop to 0 (5 bytes needed)
+ 0x38, SIG_UINT16(0x00FB), // pushi 00FBh (setLoop)
+ 0x78, // push1
+ 0x76, // push0
+ // original code, but offset changed
+ 0x38, SIG_UINT16(0x00E9), // pushi 00E9h (setCycle)
+ 0x7a, // push2
+ 0x51, 0x18, // class End
+ 0x36, // push
+ 0x7c, // pushSelf
+ 0x81, 0x00, // lag global[0]
+ 0x4a, 0x1a, 0x00, // send 1Ah - adjusted
+ // GKEgo::view(859)
+ // GKEgo::setCel(0)
+ // GKEgo::setLoop(0) <-- new, by us
+ // GKEgo::setCycle(End, sDJEnters)
+ // end of original code
+ 0x3a, // toss
+ 0x48, // ret (saves 1 byte)
+ // state 1 code
+ 0x3c, // dup
+ 0x34, SIG_UINT16(0x0001), // ldi 0001 (waste 1 byte)
+ 0x1a, // eq?
+ 0x31, 2, // bnt [next state 2]
+ 0x33, 41, // jmp to state 2 delay code
+ SIG_ADDTOOFFSET(+41),
+ // wait 2 cycles instead of only 1
+ 0x35, 0x02, // ldi 2
+ PATCH_END
+};
+
+// script, description, signature patch
static const SciScriptPatcherEntry gk1Signatures[] = {
- { true, 51, "interrogation bug", 1, gk1SignatureInterrogationBug, gk1PatchInterrogationBug },
- { true, 212, "day 5 phone freeze", 1, gk1SignatureDay5PhoneFreeze, gk1PatchDay5PhoneFreeze },
- { true, 230, "day 6 police beignet timer issue", 1, gk1SignatureDay6PoliceBeignet, gk1PatchDay6PoliceBeignet },
- { true, 230, "day 6 police sleep timer issue", 1, gk1SignatureDay6PoliceSleep, gk1PatchDay6PoliceSleep },
+ { true, 51, "interrogation bug", 1, gk1SignatureInterrogationBug, gk1PatchInterrogationBug },
+ { true, 212, "day 5 phone freeze", 1, gk1SignatureDay5PhoneFreeze, gk1PatchDay5PhoneFreeze },
+ { true, 230, "day 6 police beignet timer issue", 1, gk1SignatureDay6PoliceBeignet, gk1PatchDay6PoliceBeignet },
+ { true, 230, "day 6 police sleep timer issue", 1, gk1SignatureDay6PoliceSleep, gk1PatchDay6PoliceSleep },
+ { true, 808, "day 10 gabriel dress up infinite turning", 1, gk1SignatureDay10GabrielDressUp, gk1PatchDay10GabrielDressUp },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -751,6 +945,32 @@ static const uint16 kq5PatchWitchCageInit[] = {
PATCH_END
};
+// The multilingual releases of KQ5 hang right at the end during the magic battle with Mordack.
+// It seems additional code was added to wait for signals, but the signals are never set and thus
+// the game hangs. We disable that code, so that the battle works again.
+// This also happened in the original interpreter.
+// We must not change similar code, that happens before.
+
+// Applies to at least: French PC floppy, German PC floppy, Spanish PC floppy
+// Responsible method: stingScript::changeState, dragonScript::changeState, snakeScript::changeState
+static const uint16 kq5SignatureMultilingualEndingGlitch[] = {
+ SIG_MAGICDWORD,
+ 0x89, 0x57, // lsg global[57h]
+ 0x35, 0x00, // ldi 0
+ 0x1a, // eq?
+ 0x18, // not
+ 0x30, SIG_UINT16(0x0011), // bnt [skip signal check]
+ SIG_ADDTOOFFSET(+8), // skip globalSound::prevSignal get code
+ 0x36, // push
+ 0x35, 0x0a, // ldi 0Ah
+ SIG_END
+};
+
+static const uint16 kq5PatchMultilingualEndingGlitch[] = {
+ PATCH_ADDTOOFFSET(+6),
+ 0x32, // change BNT into JMP
+ PATCH_END
+};
// In the final battle, the DOS version uses signals in the music to handle
// timing, while in the Windows version another method is used and the GM
@@ -781,9 +1001,10 @@ static const uint16 kq5PatchWinGMSignals[] = {
// script, description, signature patch
static const SciScriptPatcherEntry kq5Signatures[] = {
- { true, 0, "CD: harpy volume change", 1, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume },
- { true, 200, "CD: witch cage init", 1, kq5SignatureWitchCageInit, kq5PatchWitchCageInit },
- { false, 124, "Win: GM Music signal checks", 4, kq5SignatureWinGMSignals, kq5PatchWinGMSignals },
+ { true, 0, "CD: harpy volume change", 1, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume },
+ { true, 200, "CD: witch cage init", 1, kq5SignatureWitchCageInit, kq5PatchWitchCageInit },
+ { true, 124, "Multilingual: Ending glitching out", 3, kq5SignatureMultilingualEndingGlitch, kq5PatchMultilingualEndingGlitch },
+ { false, 124, "Win: GM Music signal checks", 4, kq5SignatureWinGMSignals, kq5PatchWinGMSignals },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -1197,8 +1418,10 @@ static const uint16 kq6CDPatchAudioTextSupportJumpAlways[] = {
};
// Fixes "Girl In The Tower" to get played in dual mode as well
+// Also changes credits to use CD audio for dual mode.
+//
// Applies to at least: PC-CD
-// Patched method: rm740::cue
+// Patched method: rm740::cue (script 740), sCredits::init (script 52)
static const uint16 kq6CDSignatureAudioTextSupportGirlInTheTower[] = {
SIG_MAGICDWORD,
0x89, 0x5a, // lsg global[5a]
@@ -1325,6 +1548,7 @@ static const SciScriptPatcherEntry kq6Signatures[] = {
{ false, 928, "CD: audio + text support KQ6 4", 1, kq6CDSignatureAudioTextSupport4, kq6CDPatchAudioTextSupport4 },
{ false, 1009, "CD: audio + text support KQ6 Guards", 2, kq6CDSignatureAudioTextSupportGuards, kq6CDPatchAudioTextSupportGuards },
{ false, 1027, "CD: audio + text support KQ6 Stepmother", 1, kq6CDSignatureAudioTextSupportStepmother, kq6CDPatchAudioTextSupportJumpAlways },
+ { false, 52, "CD: audio + text support KQ6 Girl In The Tower", 1, kq6CDSignatureAudioTextSupportGirlInTheTower, kq6CDPatchAudioTextSupportGirlInTheTower },
{ false, 740, "CD: audio + text support KQ6 Girl In The Tower", 1, kq6CDSignatureAudioTextSupportGirlInTheTower, kq6CDPatchAudioTextSupportGirlInTheTower },
{ false, 370, "CD: audio + text support KQ6 Azure & Ariel", 6, kq6CDSignatureAudioTextSupportAzureAriel, kq6CDPatchAudioTextSupportAzureAriel },
{ false, 903, "CD: audio + text support KQ6 menu", 1, kq6CDSignatureAudioTextMenuSupport, kq6CDPatchAudioTextMenuSupport },
@@ -1332,6 +1556,167 @@ static const SciScriptPatcherEntry kq6Signatures[] = {
};
// ===========================================================================
+
+// King's Quest 7 has really weird subtitles. It seems as if the subtitles were
+// not fully finished.
+//
+// Method kqMessager::findTalker in script 0 tries to figure out, which class to use for
+// displaying subtitles. It uses the "talker" data of the given message to do that.
+// Strangely this "talker" data seems to be quite broken.
+// For example chapter 2 starts with a cutscene.
+// Troll king: "Welcome, most beautiful of princesses!" - talker 6
+// Which is followed by the princess going
+// "Hmm?" - which is set to talker 99, normally the princess is talker 7.
+//
+// Talker 99 is seen as unknown and thus treated as "narrator", which makes
+// the scripts put the text at the top of the game screen and even use a
+// different font.
+//
+// In other cases, when the player character thinks to himself talker 99
+// is also used. In such situations it may make somewhat sense to do so,
+// but putting the text at the top of the screen is also irritating to the player.
+// It's really weird.
+//
+// The scripts also put the regular text in the middle of the screen, blocking
+// animations.
+//
+// And for certain rooms, the subtitle box may use another color
+// like for example pink/purple at the start of chapter 5.
+//
+// We fix all of that (hopefully - lots of testing is required).
+// We put the text at the bottom of the play screen.
+// We also make the scripts use the regular KQTalker instead of KQNarrator.
+// And we also make the subtitle box use color 255, which is fixed white.
+//
+// Applies to at least: PC CD 1.4 English, 1.51 English, 1.51 German, 2.00 English
+// Patched method: KQNarrator::init (script 31)
+static const uint16 kq7SignatureSubtitleFix1[] = {
+ SIG_MAGICDWORD,
+ 0x39, 0x25, // pushi 25h (fore)
+ 0x78, // push1
+ 0x39, 0x06, // pushi 06 - sets back to 6
+ 0x39, 0x26, // pushi 26 (back)
+ 0x78, // push1
+ 0x78, // push1 - sets back to 1
+ 0x39, 0x2a, // pushi 2Ah (font)
+ 0x78, // push1
+ 0x89, 0x16, // lsg global[16h] - sets font to global[16h]
+ 0x7a, // push2 (y)
+ 0x78, // push1
+ 0x76, // push0 - sets y to 0
+ 0x54, SIG_UINT16(0x0018), // self 18h
+ SIG_END
+};
+
+static const uint16 kq7PatchSubtitleFix1[] = {
+ 0x33, 0x12, // jmp [skip special init code]
+ PATCH_END
+};
+
+// Applies to at least: PC CD 1.51 English, 1.51 German, 2.00 English
+// Patched method: Narrator::init (script 64928)
+static const uint16 kq7SignatureSubtitleFix2[] = {
+ SIG_MAGICDWORD,
+ 0x89, 0x5a, // lsg global[5a]
+ 0x35, 0x02, // ldi 02
+ 0x12, // and
+ 0x31, 0x1e, // bnt [skip audio volume code]
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi masterVolume (0212h for 2.00, 0219h for 1.51)
+ 0x76, // push0
+ 0x81, 0x01, // lag global[1]
+ 0x4a, 0x04, 0x00, // send 04
+ 0x65, 0x32, // aTop curVolume
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi masterVolume (0212h for 2.00, 0219h for 1.51)
+ 0x78, // push1
+ 0x67, 0x32, // pTos curVolume
+ 0x35, 0x02, // ldi 02
+ 0x06, // mul
+ 0x36, // push
+ 0x35, 0x03, // ldi 03
+ 0x08, // div
+ 0x36, // push
+ 0x81, 0x01, // lag global[1]
+ 0x4a, 0x06, 0x00, // send 06
+ // end of volume code
+ 0x35, 0x01, // ldi 01
+ 0x65, 0x28, // aTop initialized
+ SIG_END
+};
+
+static const uint16 kq7PatchSubtitleFix2[] = {
+ PATCH_ADDTOOFFSET(+5), // skip to bnt
+ 0x31, 0x1b, // bnt [skip audio volume code]
+ PATCH_ADDTOOFFSET(+15), // right after "aTop curVolume / pushi masterVolume / push1"
+ 0x7a, // push2
+ 0x06, // mul (saves 3 bytes in total)
+ 0x36, // push
+ 0x35, 0x03, // ldi 03
+ 0x08, // div
+ 0x36, // push
+ 0x81, 0x01, // lag global[1]
+ 0x4a, 0x06, 0x00, // send 06
+ // end of volume code
+ 0x35, 118, // ldi 118d
+ 0x65, 0x16, // aTop y
+ 0x78, // push1 (saves 1 byte)
+ 0x69, 0x28, // sTop initialized
+ PATCH_END
+};
+
+// Applies to at least: PC CD 1.51 English, 1.51 German, 2.00 English
+// Patched method: Narrator::say (script 64928)
+static const uint16 kq7SignatureSubtitleFix3[] = {
+ SIG_MAGICDWORD,
+ 0x63, 0x28, // pToa initialized
+ 0x18, // not
+ 0x31, 0x07, // bnt [skip init code]
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi init (008Eh for 2.00, 0093h for 1.51)
+ 0x76, // push0
+ 0x54, SIG_UINT16(0x0004), // self 04
+ // end of init code
+ 0x8f, 0x00, // lsp param[0]
+ 0x35, 0x01, // ldi 01
+ 0x1e, // gt?
+ 0x31, 0x08, // bnt [set acc to 0]
+ 0x87, 0x02, // lap param[2]
+ 0x31, 0x04, // bnt [set acc to 0]
+ 0x87, 0x02, // lap param[2]
+ 0x33, 0x02, // jmp [over set acc to 0 code]
+ 0x35, 0x00, // ldi 00
+ 0x65, 0x18, // aTop caller
+ SIG_END
+};
+
+static const uint16 kq7PatchSubtitleFix3[] = {
+ PATCH_ADDTOOFFSET(+2), // skip over "pToa initialized code"
+ 0x2f, 0x0c, // bt [skip init code] - saved 1 byte
+ 0x38,
+ PATCH_GETORIGINALBYTE(+6),
+ PATCH_GETORIGINALBYTE(+7), // pushi (init)
+ 0x76, // push0
+ 0x54, PATCH_UINT16(0x0004), // self 04
+ // additionally set background color here (5 bytes)
+ 0x34, PATCH_UINT16(255), // pushi 255d
+ 0x65, 0x2e, // aTop back
+ // end of init code
+ 0x8f, 0x00, // lsp param[0]
+ 0x35, 0x01, // ldi 01 - this may get optimized to get another byte
+ 0x1e, // gt?
+ 0x31, 0x04, // bnt [set acc to 0]
+ 0x87, 0x02, // lap param[2]
+ 0x2f, 0x02, // bt [over set acc to 0 code]
+ PATCH_END
+};
+
+// script, description, signature patch
+static const SciScriptPatcherEntry kq7Signatures[] = {
+ { true, 31, "subtitle fix 1/3", 1, kq7SignatureSubtitleFix1, kq7PatchSubtitleFix1 },
+ { true, 64928, "subtitle fix 2/3", 1, kq7SignatureSubtitleFix2, kq7PatchSubtitleFix2 },
+ { true, 64928, "subtitle fix 3/3", 1, kq7SignatureSubtitleFix3, kq7PatchSubtitleFix3 },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
// Script 210 in the German version of Longbow handles the case where Robin
// hands out the scroll to Marion and then types his name using the hand code.
// The German version script contains a typo (probably a copy/paste error),
@@ -1371,9 +1756,109 @@ static const uint16 longbowPatchShowHandCode[] = {
PATCH_END
};
+// When walking through the forest, arithmetic errors may occur at "random".
+// The scripts try to add a value and a pointer to the object "berryBush".
+//
+// This is caused by a local variable overflow.
+//
+// The scripts create berry bush objects dynamically. The array storage for
+// those bushes may hold a total of 8 bushes. But sometimes 10 bushes
+// are created. This overwrites 2 additional locals in script 225 and
+// those locals are used normally for value lookups.
+//
+// Changing the total of bushes could cause all sorts of other issues,
+// that's why I rather patched the code, that uses the locals for a lookup.
+// Which means it doesn't matter anymore when those locals are overwritten.
+//
+// Applies to at least: English PC floppy, German PC floppy, English Amiga floppy
+// Responsible method: export 2 of script 225
+// Fixes bug: #6751
+static const uint16 longbowSignatureBerryBushFix[] = {
+ 0x89, 0x70, // lsg global[70h]
+ 0x35, 0x03, // ldi 03h
+ 0x1a, // eq?
+ 0x2e, SIG_UINT16(0x002d), // bt [process code]
+ 0x89, 0x70, // lsg global[70h]
+ 0x35, 0x04, // ldi 04h
+ 0x1a, // eq?
+ 0x2e, SIG_UINT16(0x0025), // bt [process code]
+ 0x89, 0x70, // lsg global[70h]
+ 0x35, 0x05, // ldi 05h
+ 0x1a, // eq?
+ 0x2e, SIG_UINT16(0x001d), // bt [process code]
+ 0x89, 0x70, // lsg global[70h]
+ 0x35, 0x06, // ldi 06h
+ 0x1a, // eq?
+ 0x2e, SIG_UINT16(0x0015), // bt [process code]
+ 0x89, 0x70, // lsg global[70h]
+ 0x35, 0x18, // ldi 18h
+ 0x1a, // eq?
+ 0x2e, SIG_UINT16(0x000d), // bt [process code]
+ 0x89, 0x70, // lsg global[70h]
+ 0x35, 0x19, // ldi 19h
+ 0x1a, // eq?
+ 0x2e, SIG_UINT16(0x0005), // bt [process code]
+ 0x89, 0x70, // lsg global[70h]
+ 0x35, 0x1a, // ldi 1Ah
+ 0x1a, // eq?
+ // jump location for the "bt" instructions
+ 0x30, SIG_UINT16(0x0011), // bnt [skip over follow up code, to offset 0c35]
+ // 55 bytes until here
+ 0x85, 00, // lat temp[0]
+ SIG_MAGICDWORD,
+ 0x9a, SIG_UINT16(0x0110), // lsli local[110h] -> 110h points normally to 110h / 2Bh
+ // 5 bytes
+ 0x7a, // push2
+ SIG_END
+};
+
+static const uint16 longbowPatchBerryBushFix[] = {
+ PATCH_ADDTOOFFSET(+4), // keep: lsg global[70h], ldi 03h
+ 0x22, // lt? (global < 03h)
+ 0x2f, 0x42, // bt [skip over all the code directly]
+ 0x89, 0x70, // lsg global[70h]
+ 0x35, 0x06, // ldi 06h
+ 0x24, // le? (global <= 06h)
+ 0x2f, 0x0e, // bt [to kRandom code]
+ 0x89, 0x70, // lsg global[70h]
+ 0x35, 0x18, // ldi 18h
+ 0x22, // lt? (global < 18h)
+ 0x2f, 0x34, // bt [skip over all the code directly]
+ 0x89, 0x70, // lsg global[70h]
+ 0x35, 0x1a, // ldi 1Ah
+ 0x24, // le? (global <= 1Ah)
+ 0x31, 0x2d, // bnt [skip over all the code directly]
+ // 28 bytes, 27 bytes saved
+ // kRandom code
+ 0x85, 0x00, // lat temp[0]
+ 0x2f, 0x05, // bt [skip over case 0]
+ // temp[0] == 0
+ 0x38, SIG_UINT16(0x0110), // pushi 0110h - that's what's normally at local[110h]
+ 0x33, 0x18, // jmp [kRandom call]
+ // check temp[0] further
+ 0x78, // push1
+ 0x1a, // eq?
+ 0x31, 0x05, // bt [skip over case 1]
+ // temp[0] == 1
+ 0x38, SIG_UINT16(0x002b), // pushi 002Bh - that's what's normally at local[111h]
+ 0x33, 0x0F, // jmp [kRandom call]
+ // temp[0] >= 2
+ 0x8d, 00, // lst temp[0]
+ 0x35, 0x02, // ldi 02
+ 0x04, // sub
+ 0x9a, SIG_UINT16(0x0112), // lsli local[112h] -> look up value in 2nd table
+ // this may not be needed at all and was just added for safety reasons
+ // waste 9 spare bytes
+ 0x35, 0x00, // ldi 00
+ 0x35, 0x00, // ldi 00
+ 0x34, PATCH_UINT16(0x0000), // ldi 0000
+ PATCH_END
+};
+
// script, description, signature patch
static const SciScriptPatcherEntry longbowSignatures[] = {
{ true, 210, "hand code crash", 5, longbowSignatureShowHandCode, longbowPatchShowHandCode },
+ { true, 225, "arithmetic berry bush fix", 1, longbowSignatureBerryBushFix, longbowPatchBerryBushFix },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -1421,6 +1906,37 @@ static const SciScriptPatcherEntry larry2Signatures[] = {
// ===========================================================================
// Leisure Suit Larry 5
+// In Miami the player can call the green card telephone number and get
+// green card including limo at the same time in the English 1.000 PC release.
+// This results later in a broken game in case the player doesn't read
+// the second telephone number for the actual limousine service, because
+// in that case it's impossible for the player to get back to the airport.
+//
+// We disable the code, that is responsible to make the limo arrive.
+//
+// This bug was fixed in the European (dual language) versions of the game.
+//
+// Applies to at least: English PC floppy (1.000)
+// Responsible method: sPhone::changeState(40)
+static const uint16 larry5SignatureGreenCardLimoBug[] = {
+ 0x7a, // push2
+ SIG_MAGICDWORD,
+ 0x39, 0x07, // pushi 07
+ 0x39, 0x0c, // pushi 0Ch
+ 0x45, 0x0a, 0x04, // call export 10 of script 0
+ 0x78, // push1
+ 0x39, 0x26, // pushi 26h (limo arrived flag)
+ 0x45, 0x07, 0x02, // call export 7 of script 0 (sets flag)
+ SIG_END
+};
+
+static const uint16 larry5PatchGreenCardLimoBug[] = {
+ PATCH_ADDTOOFFSET(+8),
+ 0x34, PATCH_UINT16(0), // ldi 0000 (dummy)
+ 0x34, PATCH_UINT16(0), // ldi 0000 (dummy)
+ PATCH_END
+};
+
// In one of the conversations near the end (to be exact - room 380 and the text
// about using champagne on Reverse Biaz - only used when you actually did that
// in the game), the German text is too large, causing the textbox to get too large.
@@ -1444,6 +1960,7 @@ static const uint16 larry5PatchGermanEndingPattiTalker[] = {
// script, description, signature patch
static const SciScriptPatcherEntry larry5Signatures[] = {
+ { true, 280, "English-only: fix green card limo bug", 1, larry5SignatureGreenCardLimoBug, larry5PatchGreenCardLimoBug },
{ true, 380, "German-only: Enlarge Patti Textbox", 1, larry5SignatureGermanEndingPattiTalker, larry5PatchGermanEndingPattiTalker },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -1500,6 +2017,194 @@ static const SciScriptPatcherEntry larry6Signatures[] = {
};
// ===========================================================================
+// Laura Bow 1 - Colonel's Bequest
+//
+// This is basically just a broken easter egg in Colonel's Bequest.
+// A plane can show up in room 4, but that only happens really rarely.
+// Anyway the Sierra developer seems to have just entered the wrong loop,
+// which is why the statue view is used instead (loop 0).
+// We fix it to use the correct loop.
+//
+// This is only broken in the PC version. It was fixed for Amiga + Atari ST.
+//
+// Credits to OmerMor, for finding it.
+
+// Applies to at least: English PC Floppy
+// Responsible method: room4::init
+static const uint16 laurabow1SignatureEasterEggViewFix[] = {
+ 0x78, // push1
+ 0x76, // push0
+ SIG_MAGICDWORD,
+ 0x38, SIG_SELECTOR16(setLoop), // pushi "setLoop"
+ 0x78, // push1
+ 0x39, 0x03, // pushi 3 (loop 3, view only has 3 loops)
+ SIG_END
+};
+
+static const uint16 laurabow1PatchEasterEggViewFix[] = {
+ PATCH_ADDTOOFFSET(+7),
+ 0x02, // change loop to 2
+ PATCH_END
+};
+
+// When oiling the armor or opening the visor of the armor, the scripts
+// first check if Laura/ego is near the armor and if she is not, they will move her
+// to the armor. After that further code is executed.
+//
+// The current location is checked by a ego::inRect() call.
+//
+// The given rect for the inRect call inside openVisor::changeState was made larger for Atari ST/Amiga versions.
+// We change the PC version to use the same rect.
+//
+// Additionally the coordinate, that Laura is moved to, is 152, 107 and may not be reachable depending on where
+// Laura/ego was, when "use oil on helmet of armor" / "open visor of armor" got entered.
+// Bad coordinates are for example 82, 110, which then cause collisions and effectively an endless loop.
+// Game will effectively "freeze" and the user is only able to restore a previous game.
+// This also happened, when using the original interpreter.
+// We change the destination coordinate to 152, 110, which seems to be reachable all the time.
+//
+// The following patch fixes the rect for the PC version of the game.
+//
+// Applies to at least: English PC Floppy
+// Responsible method: openVisor::changeState (script 37)
+// Fixes bug: #7119
+static const uint16 laurabow1SignatureArmorOpenVisorFix[] = {
+ 0x39, 0x04, // pushi 04
+ SIG_MAGICDWORD,
+ 0x39, 0x6a, // pushi 6a (106d)
+ 0x38, SIG_UINT16(0x96), // pushi 0096 (150d)
+ 0x39, 0x6c, // pushi 6c (108d)
+ 0x38, SIG_UINT16(0x98), // pushi 0098 (152d)
+ SIG_END
+};
+
+static const uint16 laurabow1PatchArmorOpenVisorFix[] = {
+ PATCH_ADDTOOFFSET(+2),
+ 0x39, 0x68, // pushi 68 (104d) (-2)
+ 0x38, SIG_UINT16(0x94), // pushi 0094 (148d) (-2)
+ 0x39, 0x6f, // pushi 6f (111d) (+3)
+ 0x38, SIG_UINT16(0x9a), // pushi 009a (154d) (+2)
+ PATCH_END
+};
+
+// This here fixes the destination coordinate (exact details are above).
+//
+// Applies to at least: English PC Floppy, English Atari ST Floppy, English Amiga Floppy
+// Responsible method: openVisor::changeState, oiling::changeState (script 37)
+// Fixes bug: #7119
+static const uint16 laurabow1SignatureArmorMoveToFix[] = {
+ SIG_MAGICDWORD,
+ 0x36, // push
+ 0x39, 0x6b, // pushi 6B (107d)
+ 0x38, SIG_UINT16(0x0098), // pushi 98 (152d)
+ 0x7c, // pushSelf
+ 0x81, 0x00, // lag global[0]
+ SIG_END
+};
+
+static const uint16 laurabow1PatchArmorMoveToFix[] = {
+ PATCH_ADDTOOFFSET(+1),
+ 0x39, 0x6e, // pushi 6E (110d) - adjust x, so that no collision can occur anymore
+ PATCH_END
+};
+
+// In some cases like for example when the player oils the arm of the armor, command input stays
+// disabled, even when the player exits fast enough, so that Laura doesn't die.
+//
+// This is caused by the scripts only enabling control (directional movement), but do not enable command input as well.
+//
+// This bug also happens, when using the original interpreter.
+// And it was fixed for the Atari ST + Amiga versions of the game.
+//
+// Applies to at least: English PC Floppy
+// Responsible method: 2nd subroutine in script 37, called by oiling::changeState(7)
+// Fixes bug: #7154
+static const uint16 laurabow1SignatureArmorOilingArmFix[] = {
+ 0x38, SIG_UINT16(0x0089), // pushi 89h
+ 0x76, // push0
+ SIG_MAGICDWORD,
+ 0x72, SIG_UINT16(0x1a5c), // lofsa "Can" - offsets are not skipped to make sure only the PC version gets patched
+ 0x4a, 0x04, // send 04
+ 0x38, SIG_UINT16(0x0089), // pushi 89h
+ 0x76, // push0
+ 0x72, SIG_UINT16(0x19a1), // lofsa "Visor"
+ 0x4a, 0x04, // send 04
+ 0x38, SIG_UINT16(0x0089), // pushi 89h
+ 0x76, // push0
+ 0x72, SIG_UINT16(0x194a), // lofsa "note"
+ 0x4a, 0x04, // send 04
+ 0x38, SIG_UINT16(0x0089), // pushi 89h
+ 0x76, // push0
+ 0x72, SIG_UINT16(0x18f3), // lofsa "valve"
+ 0x4a, 0x04, // send 04
+ 0x8b, 0x34, // lsl local[34h]
+ 0x35, 0x02, // ldi 02
+ 0x1c, // ne?
+ 0x30, SIG_UINT16(0x0014), // bnt [to ret]
+ 0x8b, 0x34, // lsl local[34h]
+ 0x35, 0x05, // ldi 05
+ 0x1c, // ne?
+ 0x30, SIG_UINT16(0x000c), // bnt [to ret]
+ 0x8b, 0x34, // lsl local[34h]
+ 0x35, 0x06, // ldi 06
+ 0x1c, // ne?
+ 0x30, SIG_UINT16(0x0004), // bnt [to ret]
+ // followed by code to call script 0 export to re-enable controls and call setMotion
+ SIG_END
+};
+
+static const uint16 laurabow1PatchArmorOilingArmFix[] = {
+ PATCH_ADDTOOFFSET(+3), // skip over pushi 89h
+ 0x3c, // dup
+ 0x3c, // dup
+ 0x3c, // dup
+ // saves a total of 6 bytes
+ 0x76, // push0
+ 0x72, SIG_UINT16(0x1a59), // lofsa "Can"
+ 0x4a, 0x04, // send 04
+ 0x76, // push0
+ 0x72, SIG_UINT16(0x19a1), // lofsa "Visor"
+ 0x4a, 0x04, // send 04
+ 0x76, // push0
+ 0x72, SIG_UINT16(0x194d), // lofsa "note"
+ 0x4a, 0x04, // send 04
+ 0x76, // push0
+ 0x72, SIG_UINT16(0x18f9), // lofsa "valve" 18f3
+ 0x4a, 0x04, // send 04
+ // new code to enable input as well, needs 9 spare bytes
+ 0x38, SIG_UINT16(0x00e2), // canInput
+ 0x78, // push1
+ 0x78, // push1
+ 0x51, 0x2b, // class User
+ 0x4a, 0x06, // send 06 -> call User::canInput(1)
+ // original code, but changed a bit to save some more bytes
+ 0x8b, 0x34, // lsl local[34h]
+ 0x35, 0x02, // ldi 02
+ 0x04, // sub
+ 0x31, 0x12, // bnt [to ret]
+ 0x36, // push
+ 0x35, 0x03, // ldi 03
+ 0x04, // sub
+ 0x31, 0x0c, // bnt [to ret]
+ 0x78, // push1
+ 0x1a, // eq?
+ 0x2f, 0x08, // bt [to ret]
+ // saves 7 bytes, we only need 3, so waste 4 bytes
+ 0x35, 0x00, // ldi 0
+ 0x35, 0x00, // ldi 0
+ PATCH_END
+};
+
+// script, description, signature patch
+static const SciScriptPatcherEntry laurabow1Signatures[] = {
+ { true, 4, "easter egg view fix", 1, laurabow1SignatureEasterEggViewFix, laurabow1PatchEasterEggViewFix },
+ { true, 37, "armor open visor fix", 1, laurabow1SignatureArmorOpenVisorFix, laurabow1PatchArmorOpenVisorFix },
+ { true, 37, "armor move to fix", 2, laurabow1SignatureArmorMoveToFix, laurabow1PatchArmorMoveToFix },
+ { true, 37, "allowing input, after oiling arm", 1, laurabow1SignatureArmorOilingArmFix, laurabow1PatchArmorOilingArmFix },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
// Laura Bow 2
//
// Moving away the painting in the room with the hidden safe is problematic
@@ -1804,15 +2509,103 @@ static const SciScriptPatcherEntry laurabow2Signatures[] = {
// MG::replay somewhat calculates the savedgame-id used when saving again
// this doesn't work right and we remove the code completely.
// We set the savedgame-id directly right after restoring in kRestoreGame.
+// We also draw the background picture in here instead.
+// This Mixed Up Mother Goose draws the background picture before restoring,
+// instead of doing it properly in MG::replay. This fixes graphic issues,
+// when restoring from GMM.
+//
+// Applies to at least: English SCI1 CD, English SCI1.1 floppy, Japanese FM-Towns
+// Responsible method: MG::replay (script 0)
static const uint16 mothergoose256SignatureReplay[] = {
+ 0x7a, // push2
+ 0x78, // push1
+ 0x5b, 0x00, 0xbe, // lea global[BEh]
+ 0x36, // push
+ 0x43, 0x70, 0x04, // callk MemorySegment
+ 0x7a, // push2
+ 0x5b, 0x00, 0xbe, // lea global[BEh]
+ 0x36, // push
+ 0x76, // push0
+ 0x43, 0x62, 0x04, // callk StrAt
+ 0xa1, 0xaa, // sag global[AAh]
+ 0x7a, // push2
+ 0x5b, 0x00, 0xbe, // lea global[BEh]
+ 0x36, // push
+ 0x78, // push1
+ 0x43, 0x62, 0x04, // callk StrAt
+ 0x36, // push
+ 0x35, 0x20, // ldi 20
+ 0x04, // sub
+ 0xa1, SIG_ADDTOOFFSET(+1), // sag global[57h] -> FM-Towns [9Dh]
+ // 35 bytes
+ 0x39, 0x03, // pushi 03
+ 0x89, SIG_ADDTOOFFSET(+1), // lsg global[1Dh] -> FM-Towns [1Eh]
+ 0x76, // push0
+ 0x7a, // push2
+ 0x5b, 0x00, 0xbe, // lea global[BEh]
+ 0x36, // push
+ 0x7a, // push2
+ 0x43, 0x62, 0x04, // callk StrAt
+ 0x36, // push
+ 0x35, 0x01, // ldi 01
+ 0x04, // sub
+ 0x36, // push
+ 0x43, 0x62, 0x06, // callk StrAt
+ // 22 bytes
+ 0x7a, // push2
+ 0x5b, 0x00, 0xbe, // lea global[BE]
+ 0x36, // push
+ 0x39, 0x03, // pushi 03
+ 0x43, 0x62, 0x04, // callk StrAt
+ // 10 bytes
0x36, // push
0x35, SIG_MAGICDWORD, 0x20, // ldi 20
0x04, // sub
0xa1, 0xb3, // sag global[b3]
+ // 6 bytes
SIG_END
};
static const uint16 mothergoose256PatchReplay[] = {
+ 0x39, 0x06, // pushi 06
+ 0x76, // push0
+ 0x76, // push0
+ 0x38, PATCH_UINT16(200), // pushi 200d
+ 0x38, PATCH_UINT16(320), // pushi 320d
+ 0x76, // push0
+ 0x76, // push0
+ 0x43, 0x15, 0x0c, // callk SetPort -> set picture port to full screen
+ // 15 bytes
+ 0x39, 0x04, // pushi 04
+ 0x3c, // dup
+ 0x76, // push0
+ 0x38, PATCH_UINT16(255), // pushi 255d
+ 0x76, // push0
+ 0x43, 0x6f, 0x08, // callk Palette -> set intensity to 0 for all colors
+ // 11 bytes
+ 0x7a, // push2
+ 0x38, PATCH_UINT16(800), // pushi 800
+ 0x76, // push0
+ 0x43, 0x08, 0x04, // callk DrawPic -> draw picture 800
+ // 8 bytes
+ 0x39, 0x06, // pushi 06
+ 0x39, 0x0c, // pushi 0Ch
+ 0x76, // push0
+ 0x76, // push0
+ 0x38, PATCH_UINT16(200), // push 200
+ 0x38, PATCH_UINT16(320), // push 320
+ 0x78, // push1
+ 0x43, 0x6c, 0x0c, // callk Graph -> send everything to screen
+ // 16 bytes
+ 0x39, 0x06, // pushi 06
+ 0x76, // push0
+ 0x76, // push0
+ 0x38, PATCH_UINT16(156), // pushi 156d
+ 0x38, PATCH_UINT16(258), // pushi 258d
+ 0x39, 0x03, // pushi 03
+ 0x39, 0x04, // pushi 04
+ 0x43, 0x15, 0x0c, // callk SetPort -> set picture port back
+ // 17 bytes
0x34, PATCH_UINT16(0x0000), // ldi 0000 (dummy)
0x34, PATCH_UINT16(0x0000), // ldi 0000 (dummy)
PATCH_END
@@ -1820,6 +2613,9 @@ static const uint16 mothergoose256PatchReplay[] = {
// when saving, it also checks if the savegame ID is below 13.
// we change this to check if below 113 instead
+//
+// Applies to at least: English SCI1 CD, English SCI1.1 floppy, Japanese FM-Towns
+// Responsible method: Game::save (script 994 for SCI1), MG::save (script 0 for SCI1.1)
static const uint16 mothergoose256SignatureSaveLimit[] = {
0x89, SIG_MAGICDWORD, 0xb3, // lsg global[b3]
0x35, 0x0d, // ldi 0d
@@ -1843,6 +2639,50 @@ static const SciScriptPatcherEntry mothergoose256Signatures[] = {
// ===========================================================================
// Police Quest 1 VGA
+
+// When briefing is about to start in room 15, other officers will get into the room too.
+// When one of those officers gets into the way of ego, they will tell the player to sit down.
+// But control will be disabled right at that point. Ego may then go to his seat by himself,
+// or more often than not will just stand there. The player is unable to do anything.
+//
+// Sergeant Dooley will then enter the room. Tell the player to sit down 3 times and after
+// that it's game over.
+//
+// Because the Sergeant is telling the player to sit down, one has to assume that the player
+// is meant to still be in control. Which is why this script patch removes disabling of player control.
+//
+// The script also tries to make ego walk to the chair, but it fails because it gets stuck with other
+// actors. So I guess the safest way is to remove all of that and let the player do it manually.
+//
+// The responsible method seems to use a few hardcoded texts, which is why I have to assume that it's
+// not used anywhere else. I also checked all scripts and couldn't find any other calls to it.
+//
+// This of course also happens when using the original interpreter.
+//
+// Scripts work like this: manX::doit (script 134) triggers gab::changeState, which then triggers rm015::notify
+//
+// Applies to at least: English floppy
+// Responsible method: gab::changeState (script 152)
+// Fixes bug: #5865
+static const uint16 pq1vgaSignatureBriefingGettingStuck[] = {
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // call export 2 of script 0 (disable control)
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi notify
+ 0x76, // push0
+ 0x81, 0x02, // lag global[2] (get current room)
+ 0x4a, 0x04, // send 04
+ SIG_MAGICDWORD,
+ 0x8b, 0x02, // lsl local[2]
+ 0x35, 0x01, // ldi 01
+ 0x02, // add
+ SIG_END
+};
+
+static const uint16 pq1vgaPatchBriefingGettingStuck[] = {
+ 0x33, 0x0a, // jmp to lsl local[2], skip over export 2 and ::notify
+ PATCH_END // rm015::notify would try to make ego walk to the chair
+};
+
// When at the police station, you can put or get your gun from your locker.
// The script, that handles this, is buggy. It disposes the gun as soon as
// you click, but then waits 2 seconds before it also closes the locker.
@@ -1930,10 +2770,11 @@ static const uint16 pq1vgaPatchMapSaveRestoreBug[] = {
PATCH_END
};
-// script, description, signature patch
+// script, description, signature patch
static const SciScriptPatcherEntry pq1vgaSignatures[] = {
- { true, 341, "put gun in locker bug", 1, pq1vgaSignaturePutGunInLockerBug, pq1vgaPatchPutGunInLockerBug },
- { true, 500, "map save/restore bug", 2, pq1vgaSignatureMapSaveRestoreBug, pq1vgaPatchMapSaveRestoreBug },
+ { true, 152, "getting stuck while briefing is about to start", 1, pq1vgaSignatureBriefingGettingStuck, pq1vgaPatchBriefingGettingStuck },
+ { true, 341, "put gun in locker bug", 1, pq1vgaSignaturePutGunInLockerBug, pq1vgaPatchPutGunInLockerBug },
+ { true, 500, "map save/restore bug", 2, pq1vgaSignatureMapSaveRestoreBug, pq1vgaPatchMapSaveRestoreBug },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -2087,21 +2928,30 @@ static const uint16 qfg1vgaPatchMoveToCrusher[] = {
// Same pathfinding bug as above, where Ego is set to move to an impossible
// spot when sneaking. In GuardsTrumpet::changeState, we change the final
-// location where Ego is moved from 111, 111 to 114, 114.
+// location where Ego is moved from 111, 111 to 116, 116.
+// target coordinate is really problematic here.
+//
+// 114, 114 works when the speed slider is all the way up, but doesn't work
+// when the speed slider is not.
+//
+// It seems that this bug was fixed by Sierra for the Macintosh version.
+//
+// Applies to at least: English PC floppy
+// Responsible method: GuardsTrumpet::changeState(8)
// Fixes bug: #6248
static const uint16 qfg1vgaSignatureMoveToCastleGate[] = {
+ 0x51, SIG_ADDTOOFFSET(+1), // class MoveTo
SIG_MAGICDWORD,
- 0x51, 0x1f, // class MoveTo
0x36, // push
- 0x39, 0x6f, // pushi 6f (111 - x)
- 0x3c, // dup (111 - y)
+ 0x39, 0x6f, // pushi 6f (111d)
+ 0x3c, // dup (111d) - coordinates 111, 111
0x7c, // pushSelf
SIG_END
};
static const uint16 qfg1vgaPatchMoveToCastleGate[] = {
PATCH_ADDTOOFFSET(+3),
- 0x39, 0x72, // pushi 72 (114 - x)
+ 0x39, 0x74, // pushi 74 (116d), changes coordinates to 116, 116
PATCH_END
};
@@ -2116,7 +2966,7 @@ static const uint16 qfg1vgaSignatureCheetaurDescription[] = {
0x34, SIG_UINT16(0x01b8), // ldi 01b8
0x1a, // eq?
0x31, 0x16, // bnt 16
- 0x38, SIG_UINT16(0x0127), // pushi 0127
+ 0x38, SIG_SELECTOR16(say), // pushi 0127h (selector "say")
0x39, 0x06, // pushi 06
0x39, 0x03, // pushi 03
0x78, // push1
@@ -2140,6 +2990,7 @@ static const uint16 qfg1vgaPatchCheetaurDescription[] = {
// Local 5 of that room is a timer, that closes the door (object door11).
// Setting it to 1 during happyFace::changeState(0) stops door11::doit from
// calling goTo6::init, so the whole issue is stopped from happening.
+//
// Applies to at least: English floppy
// Responsible method: happyFace::changeState, door11::doit
// Fixes bug #6181
@@ -2166,20 +3017,172 @@ static const uint16 qfg1vgaPatchFunnyRoomFix[] = {
PATCH_END
};
+// The player is able to buy (and also steal) potions in the healer's hut
+// Strangely Sierra delays the actual buy/get potion code for 60 ticks
+// Why they did that is unknown. The code is triggered anyway only after
+// the relevant dialog boxes are closed.
+//
+// This delay causes problems in case the user quickly enters the inventory.
+// That's why we change the amount of ticks to 1, so that the remaining states
+// are executed right after the dialog boxes are closed.
+//
+// Applies to at least: English floppy
+// Responsible method: cueItScript::changeState
+// Fixes bug #6706
+static const uint16 qfg1vgaSignatureHealerHutNoDelay[] = {
+ 0x65, 0x14, // aTop 14 (state)
+ 0x36, // push
+ 0x3c, // dup
+ 0x35, 0x00, // ldi 00
+ 0x1a, // eq?
+ 0x31, 0x07, // bnt 07 [-> next state]
+ SIG_MAGICDWORD,
+ 0x35, 0x3c, // ldi 3c (60 ticks)
+ 0x65, 0x20, // aTop ticks
+ 0x32, // jmp [-> end of method]
+ SIG_END
+};
+
+static const uint16 qfg1vgaPatchHealerHutNoDelay[] = {
+ PATCH_ADDTOOFFSET(+9),
+ 0x35, 0x01, // ldi 01 (1 tick only, so that execution will resume as soon as dialog box is closed)
+ PATCH_END
+};
+
+// When following the white stag, you can actually enter the 2nd room from the mushroom/fairy location,
+// which results in ego entering from the top. When you then throw a dagger at the stag, one animation
+// frame will stay on screen, because of a script bug.
+//
+// Applies to at least: English floppy, Mac floppy
+// Responsible method: stagHurt::changeState
+// Fixes bug #6135
+static const uint16 qfg1vgaSignatureWhiteStagDagger[] = {
+ 0x87, 0x01, // lap param[1]
+ 0x65, 0x14, // aTop state
+ 0x36, // push
+ 0x3c, // dup
+ 0x35, 0x00, // ldi 0
+ 0x1a, // eq?
+ 0x31, 0x16, // bnt [next parameter check]
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // callb export 2 from script 0, 0
+ SIG_MAGICDWORD,
+ 0x38, SIG_SELECTOR16(say), // pushi 0127h (selector "say")
+ 0x39, 0x05, // pushi 05
+ 0x39, 0x03, // pushi 03
+ 0x39, 0x51, // pushi 51h
+ 0x76, // push0
+ 0x76, // push0
+ 0x7c, // pushSelf
+ 0x81, 0x5b, // lag global[5Bh] -> qg1Messager
+ 0x4a, 0x0e, // send 0Eh -> qg1Messager::say(3, 51h, 0, 0, stagHurt)
+ 0x33, 0x12, // jmp -> [ret]
+ 0x3c, // dup
+ 0x35, 0x01, // ldi 1
+ 0x1a, // eq?
+ 0x31, 0x0c, // bnt [ret]
+ 0x38, // pushi...
+ SIG_ADDTOOFFSET(+11),
+ 0x3a, // toss
+ 0x48, // ret
+ SIG_END
+};
+
+static const uint16 qfg1vgaPatchWhiteStagDagger[] = {
+ PATCH_ADDTOOFFSET(+4),
+ 0x2f, 0x05, // bt [next check] (state != 0)
+ // state = 0 code
+ 0x35, 0x01, // ldi 1
+ 0x65, 0x1a, // aTop cycles
+ 0x48, // ret
+ 0x36, // push
+ 0x35, 0x01, // ldi 1
+ 0x1a, // eq?
+ 0x31, 0x16, // bnt [state = 2 code]
+ // state = 1 code
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // callb export 2 from script 0, 0
+ 0x38, PATCH_SELECTOR16(say), // pushi 0127h (selector "say")
+ 0x39, 0x05, // pushi 05
+ 0x39, 0x03, // pushi 03
+ 0x39, 0x51, // pushi 51h
+ 0x76, // push0
+ 0x76, // push0
+ 0x7c, // pushSelf
+ 0x81, 0x5b, // lag global[5Bh] -> qg1Messager
+ 0x4a, 0x0e, // send 0Eh -> qg1Messager::say(3, 51h, 0, 0, stagHurt)
+ 0x48, // ret
+ // state = 2 code
+ PATCH_ADDTOOFFSET(+13),
+ 0x48, // ret (remove toss)
+ PATCH_END
+};
+
// script, description, signature patch
static const SciScriptPatcherEntry qfg1vgaSignatures[] = {
+ { true, 41, "moving to castle gate", 1, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate },
+ { true, 55, "healer's hut, no delay for buy/steal", 1, qfg1vgaSignatureHealerHutNoDelay, qfg1vgaPatchHealerHutNoDelay },
+ { true, 77, "white stag dagger throw animation glitch", 1, qfg1vgaSignatureWhiteStagDagger, qfg1vgaPatchWhiteStagDagger },
+ { true, 96, "funny room script bug fixed", 1, qfg1vgaSignatureFunnyRoomFix, qfg1vgaPatchFunnyRoomFix },
+ { true, 210, "cheetaur description fixed", 1, qfg1vgaSignatureCheetaurDescription, qfg1vgaPatchCheetaurDescription },
{ true, 215, "fight event issue", 1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
{ true, 216, "weapon master event issue", 1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
+ { true, 331, "moving to crusher", 1, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher },
{ true, 814, "window text temp space", 1, qfg1vgaSignatureTempSpace, qfg1vgaPatchTempSpace },
{ true, 814, "dialog header offset", 3, qfg1vgaSignatureDialogHeader, qfg1vgaPatchDialogHeader },
- { true, 331, "moving to crusher", 1, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher },
- { true, 41, "moving to castle gate", 1, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate },
- { true, 210, "cheetaur description fixed", 1, qfg1vgaSignatureCheetaurDescription, qfg1vgaPatchCheetaurDescription },
- { true, 96, "funny room script bug fixed", 1, qfg1vgaSignatureFunnyRoomFix, qfg1vgaPatchFunnyRoomFix },
SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
+
+// This is a very complicated bug.
+// When the player encounters an enemy in the desert while riding a saurus and later
+// tries to get back on it by entering "ride", the game will not give control back
+// to the player.
+//
+// This is caused by script mountSaurus getting triggered twice.
+// Once by entering the command "ride" and then a second time by a proximity check.
+//
+// Both are calling mountSaurus::init() in script 20, this one disables controls
+// then mountSaurus::changeState() from script 660 is triggered
+// mountSaurus::changeState(5) finally calls mountSaurus::dispose(), which is also in script 20
+// which finally re-enables controls
+//
+// A fix is difficult to implement. The code in script 20 is generic and used by multiple objects
+//
+// Originally I decided to change the responsible globals (66h and A1h) during mountSaurus::changeState(5).
+// This worked as far as for controls, but mountSaurus::init changes a few selectors of ego as well, which
+// won't get restored in that situation, which then messes up room changes and other things.
+//
+// I have now decided to change sheepScript::changeState(2) in script 665 instead.
+//
+// This fix could cause issues in case there is a cutscene, where ego is supposed to get onto the saurus using
+// sheepScript.
+//
+// Applies to at least: English PC Floppy, English Amiga Floppy
+// Responsible method: mountSaurus::changeState(), mountSaurus::init(), mountSaurus::dispose()
+// Fixes bug: #5156
+static const uint16 qfg2SignatureSaurusFreeze[] = {
+ 0x3c, // dup
+ 0x35, 0x02, // ldi 5
+ SIG_MAGICDWORD,
+ 0x1a, // eq?
+ 0x30, SIG_UINT16(0x0043), // bnt [ret]
+ 0x76, // push0
+ SIG_ADDTOOFFSET(+61), // skip to dispose code
+ 0x39, SIG_SELECTOR8(dispose), // pushi "dispose"
+ 0x76, // push0
+ 0x54, 0x04, // self 04
+ SIG_END
+};
+
+static const uint16 qfg2PatchSaurusFreeze[] = {
+ 0x81, 0x66, // lag 66h
+ 0x2e, SIG_UINT16(0x0040), // bt [to dispose code]
+ 0x35, 0x00, // ldi 0 (waste 2 bytes)
+ PATCH_END
+};
+
// Script 944 in QFG2 contains the FileSelector system class, used in the
// character import screen. This gets incorrectly called constantly, whenever
// the user clicks on a button in order to refresh the file list. This was
@@ -2211,9 +3214,58 @@ static const uint16 qfg2PatchImportDialog[] = {
PATCH_END
};
-// script, description, signature patch
+// Quest For Glory 2 character import doesn't properly set the character type
+// in versions 1.102 and below, which makes all importerted characters a fighter.
+//
+// Sierra released an official patch. However the fix is really easy to
+// implement on our side, so we also patch the flaw in here in case we find it.
+//
+// The version released on GOG is 1.102 without this patch applied, so us
+// patching it is quite useful.
+//
+// Applies to at least: English Floppy
+// Responsible method: importHero::changeState
+// Fixes bug: inside versions 1.102 and below
+static const uint16 qfg2SignatureImportCharType[] = {
+ 0x35, 0x04, // ldi 04
+ 0x90, SIG_UINT16(0x023b), // lagi global[23Bh]
+ 0x02, // add
+ 0x36, // push
+ 0x35, 0x04, // ldi 04
+ 0x08, // div
+ 0x36, // push
+ 0x35, 0x0d, // ldi 0D
+ 0xb0, SIG_UINT16(0x023b), // sagi global[023Bh]
+ 0x8b, 0x1f, // lsl local[1Fh]
+ 0x35, 0x05, // ldi 05
+ SIG_MAGICDWORD,
+ 0xb0, SIG_UINT16(0x0150), // sagi global[0150h]
+ 0x8b, 0x02, // lsl local[02h]
+ SIG_END
+};
+
+static const uint16 qfg2PatchImportCharType[] = {
+ 0x80, PATCH_UINT16(0x023f), // lag global[23Fh] <-- patched to save 2 bytes
+ 0x02, // add
+ 0x36, // push
+ 0x35, 0x04, // ldi 04
+ 0x08, // div
+ 0x36, // push
+ 0xa8, SIG_UINT16(0x0248), // ssg global[0248h] <-- patched to save 2 bytes
+ 0x8b, 0x1f, // lsl local[1Fh]
+ 0xa8, SIG_UINT16(0x0155), // ssg global[0155h] <-- patched to save 2 bytes
+ // new code, directly from the official sierra patch file
+ 0x83, 0x01, // lal local[01h]
+ 0xa1, 0xbb, // sag global[BBh]
+ 0xa1, 0x73, // sag global[73h]
+ PATCH_END
+};
+
+// script, description, signature patch
static const SciScriptPatcherEntry qfg2Signatures[] = {
- { true, 944, "import dialog continuous calls", 1, qfg2SignatureImportDialog, qfg2PatchImportDialog },
+ { true, 665, "getting back on saurus freeze fix", 1, qfg2SignatureSaurusFreeze, qfg2PatchSaurusFreeze },
+ { true, 805, "import character type fix", 1, qfg2SignatureImportCharType, qfg2PatchImportCharType },
+ { true, 944, "import dialog continuous calls", 1, qfg2SignatureImportDialog, qfg2PatchImportDialog },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -2253,7 +3305,7 @@ static const uint16 qfg3PatchImportDialog[] = {
// Teller::doChild. We jump to this call of hero::solvePuzzle to get that same
// behaviour.
// Applies to at least: English, German, Italian, French, Spanish Floppy
-// Responsible method: unknown
+// Responsible method: uhuraTell::doChild
// Fixes bug: #5172
static const uint16 qfg3SignatureWooDialog[] = {
SIG_MAGICDWORD,
@@ -2282,10 +3334,383 @@ static const uint16 qfg3PatchWooDialog[] = {
PATCH_END
};
-// script, description, signature patch
+// Alternative version, with uint16 offsets, for GOG release of QfG3.
+static const uint16 qfg3SignatureWooDialogAlt[] = {
+ SIG_MAGICDWORD,
+ 0x67, 0x12, // pTos 12 (query)
+ 0x35, 0xb6, // ldi b6
+ 0x1a, // eq?
+ 0x2e, SIG_UINT16(0x0005), // bt 05
+ 0x67, 0x12, // pTos 12 (query)
+ 0x35, 0x9b, // ldi 9b
+ 0x1a, // eq?
+ 0x30, SIG_UINT16(0x000c), // bnt 0c
+ 0x38, SIG_SELECTOR16(solvePuzzle), // pushi 0297
+ 0x7a, // push2
+ 0x38, SIG_UINT16(0x010c), // pushi 010c
+ 0x7a, // push2
+ 0x81, 0x00, // lag 00
+ 0x4a, 0x08, // send 08
+ 0x67, 0x12, // pTos 12 (query)
+ 0x35, 0xb5, // ldi b5
+ SIG_END
+};
+
+static const uint16 qfg3PatchWooDialogAlt[] = {
+ PATCH_ADDTOOFFSET(+0x2C),
+ 0x33, 0x12, // jmp to 0x708, the call to hero::solvePuzzle for 0xFFFC
+ PATCH_END
+};
+
+// When exporting characters at the end of Quest for Glory 3, the underlying
+// code has issues with values, that are above 9999.
+// For further study: https://github.com/Blazingstix/QFGImporter/blob/master/QFGImporter/QFGImporter/QFG3.txt
+//
+// If a value is above 9999, parts or even the whole character file will get corrupted.
+//
+// We are fixing the code because of that. We are patching code, that is calculating the checksum
+// and add extra code to lower such values to 9999.
+//
+// Applies to at least: English, French, German, Italian, Spanish floppy
+// Responsible method: saveHero::changeState
+// Fixes bug #6807
+static const uint16 qfg3SignatureExportChar[] = {
+ 0x35, SIG_ADDTOOFFSET(+1), // ldi 00 / ldi 01 (2 loops, we patch both)
+ 0xa5, 0x00, // sat temp[0] [contains index to data]
+ 0x8d, 0x00, // lst temp[0]
+ SIG_MAGICDWORD,
+ 0x35, 0x2c, // ldi 2c
+ 0x22, // lt? [index above or equal 2Ch (44d)?
+ 0x31, 0x23, // bnt [exit loop]
+ // from this point it's actually useless code, maybe a sci compiler bug
+ 0x8d, 0x00, // lst temp[0]
+ 0x35, 0x01, // ldi 01
+ 0x02, // add
+ 0x9b, 0x00, // lsli local[0] ---------- load local[0 + ACC] onto stack
+ 0x8d, 0x00, // lst temp[0]
+ 0x35, 0x01, // ldi 01
+ 0x02, // add
+ 0xb3, 0x00, // sali local[0] ---------- save stack to local[0 + ACC]
+ // end of useless code
+ 0x8b, SIG_ADDTOOFFSET(+1), // lsl local[36h/37h] ---- load local[36h/37h] onto stack
+ 0x8d, 0x00, // lst temp[0]
+ 0x35, 0x01, // ldi 01
+ 0x02, // add
+ 0x93, 0x00, // lali local[0] ---------- load local[0 + ACC] into ACC
+ 0x02, // add -------------------- add ACC + stack and put into ACC
+ 0xa3, SIG_ADDTOOFFSET(+1), // sal local[36h/37h] ---- save ACC to local[36h/37h]
+ 0x8d, 0x00, // lst temp[0] ------------ temp[0] to stack
+ 0x35, 0x02, // ldi 02
+ 0x02, // add -------------------- add 2 to stack
+ 0xa5, 0x00, // sat temp[0] ------------ save ACC to temp[0]
+ 0x33, 0xd6, // jmp [loop]
+ SIG_END
+};
+
+static const uint16 qfg3PatchExportChar[] = {
+ PATCH_ADDTOOFFSET(+11),
+ 0x85, 0x00, // lat temp[0]
+ 0x9b, 0x01, // lsli local[0] + 1 ------ load local[ ACC + 1] onto stack
+ 0x3c, // dup
+ 0x34, PATCH_UINT16(0x2710), // ldi 2710h (10000d)
+ 0x2c, // ult? ------------------- is value smaller than 10000?
+ 0x2f, 0x0a, // bt [jump over]
+ 0x3a, // toss
+ 0x38, PATCH_UINT16(0x270f), // pushi 270fh (9999d)
+ 0x3c, // dup
+ 0x85, 0x00, // lat temp[0]
+ 0xba, PATCH_UINT16(0x0001), // ssli local[0] + 1 ------ save stack to local[ ACC + 1] (UINT16 to waste 1 byte)
+ // jump offset
+ 0x83, PATCH_GETORIGINALBYTE(+26), // lal local[37h/36h] ---- load local[37h/36h] into ACC
+ 0x02, // add -------------------- add local[37h/36h] + data value
+ PATCH_END
+};
+
+// Quest for Glory 3 doesn't properly import the character type of Quest for Glory 1 character files.
+// This issue was never addressed. It's caused by Sierra reading data directly from the local
+// area, which is only set by Quest For Glory 2 import data, instead of reading the properly set global variable.
+//
+// We fix it, by also directly setting the local variable.
+//
+// Applies to at least: English, French, German, Italian, Spanish floppy
+// Responsible method: importHero::changeState(4)
+static const uint16 qfg3SignatureImportQfG1Char[] = {
+ SIG_MAGICDWORD,
+ 0x82, SIG_UINT16(0x0238), // lal local[0x0238]
+ 0xa0, SIG_UINT16(0x016a), // sag global[0x016a]
+ 0xa1, 0x7d, // sag global[0x7d]
+ 0x35, 0x01, // ldi 01
+ 0x99, 0xfb, // lsgi global[0xfb]
+ SIG_END
+};
+
+static const uint16 qfg3PatchImportQfG1Char[] = {
+ PATCH_ADDTOOFFSET(+8),
+ 0xa3, 0x01, // sal 01 -> also set local[01]
+ 0x89, 0xfc, // lsg global[0xFD] -> save 2 bytes
+ PATCH_END
+};
+
+// The chief in his hut (room 640) is not drawn using the correct priority,
+// which results in a graphical glitch. This is a game bug and also happens
+// in Sierra's SCI. We adjust priority accordingly to fix it.
+//
+// Applies to at least: English, French, German, Italian, Spanish floppy
+// Responsible method: heap in script 640
+// Fixes bug #5173
+static const uint16 qfg3SignatureChiefPriority[] = {
+ SIG_MAGICDWORD,
+ SIG_UINT16(0x0002), // yStep 0x0002
+ SIG_UINT16(0x0281), // view 0x0281
+ SIG_UINT16(0x0000), // loop 0x0000
+ SIG_UINT16(0x0000), // cel 0x0000
+ SIG_UINT16(0x0000), // priority 0x0000
+ SIG_UINT16(0x0000), // underbits 0x0000
+ SIG_UINT16(0x1000), // signal 0x1000
+ SIG_END
+};
+
+static const uint16 qfg3PatchChiefPriority[] = {
+ PATCH_ADDTOOFFSET(+8),
+ PATCH_UINT16(0x000A), // new priority 0x000A (10d)
+ PATCH_ADDTOOFFSET(+2),
+ PATCH_UINT16(0x1010), // signal 0x1010 (set fixed priority flag)
+ PATCH_END
+};
+
+// There are 3 points that can't be achieved in the game. They should've been
+// awarded for telling Rakeesh and Kreesha (room 285) about the Simabni
+// initiation.
+// However the array of posibble messages the hero can tell in that room
+// (local 156) is missing the "Tell about Initiation" message (#31) which
+// awards these points.
+// This patch adds the message to that array, thus allowing the hero to tell
+// that message (after completing the initiation) and gain the 3 points.
+// A side effect of increasing the local156 array is that the next local
+// array is shifted and shrinks in size from 4 words to 3. The patch changes
+// the 2 locations in the script that reference that array, to point to the new
+// location ($aa --> $ab). It is safe to shrink the 2nd array to 3 words
+// because only the first element in it is ever used.
+//
+// Note: You have to re-enter the room in case a saved game was loaded from a
+// previous version of ScummVM and that saved game was made inside that room.
+//
+// Applies to: English, French, German, Italian, Spanish and the GOG release.
+// Responsible method: heap in script 285
+// Fixes bug #7086
+static const uint16 qfg3SignatureMissingPoints1[] = {
+ // local[$9c] = [0 -41 -76 1 -30 -77 -33 -34 -35 -36 -37 -42 -80 999]
+ // local[$aa] = [0 0 0 0]
+ SIG_UINT16(0x0000), // 0 START MARKER
+ SIG_MAGICDWORD,
+ SIG_UINT16(0xFFD7), // -41 "Greet"
+ SIG_UINT16(0xFFB4), // -76 "Say Good-bye"
+ SIG_UINT16(0x0001), // 1 "Tell about Tarna"
+ SIG_UINT16(0xFFE2), // -30 "Tell about Simani"
+ SIG_UINT16(0xFFB3), // -77 "Tell about Prisoner"
+ SIG_UINT16(0xFFDF), // -33 "Dispelled Leopard Lady"
+ SIG_UINT16(0xFFDE), // -34 "Tell about Leopard Lady"
+ SIG_UINT16(0xFFDD), // -35 "Tell about Leopard Lady"
+ SIG_UINT16(0xFFDC), // -36 "Tell about Leopard Lady"
+ SIG_UINT16(0xFFDB), // -37 "Tell about Village"
+ SIG_UINT16(0xFFD6), // -42 "Greet"
+ SIG_UINT16(0xFFB0), // -80 "Say Good-bye"
+ SIG_UINT16(0x03E7), // 999 END MARKER
+ SIG_ADDTOOFFSET(+2), // local[$aa][0]
+ SIG_END
+};
+
+static const uint16 qfg3PatchMissingPoints1[] = {
+ PATCH_ADDTOOFFSET(+14),
+ PATCH_UINT16(0xFFE1), // -31 "Tell about Initiation"
+ PATCH_UINT16(0xFFDE), // -34 "Tell about Leopard Lady"
+ PATCH_UINT16(0xFFDD), // -35 "Tell about Leopard Lady"
+ PATCH_UINT16(0xFFDC), // -36 "Tell about Leopard Lady"
+ PATCH_UINT16(0xFFDB), // -37 "Tell about Village"
+ PATCH_UINT16(0xFFD6), // -42 "Greet"
+ PATCH_UINT16(0xFFB0), // -80 "Say Good-bye"
+ PATCH_UINT16(0x03E7), // 999 END MARKER
+ PATCH_GETORIGINALBYTE(+28), // local[$aa][0].low
+ PATCH_GETORIGINALBYTE(+29), // local[$aa][0].high
+ PATCH_END
+};
+
+static const uint16 qfg3SignatureMissingPoints2a[] = {
+ SIG_MAGICDWORD,
+ 0x35, 0x00, // ldi 0
+ 0xb3, 0xaa, // sali local[$aa]
+ SIG_END
+};
+
+static const uint16 qfg3SignatureMissingPoints2b[] = {
+ SIG_MAGICDWORD,
+ 0x36, // push
+ 0x5b, 0x02, 0xaa, // lea local[$aa]
+ SIG_END
+};
+
+static const uint16 qfg3PatchMissingPoints2[] = {
+ PATCH_ADDTOOFFSET(+3),
+ 0xab, // local[$aa] ==> local[$ab]
+ PATCH_END
+};
+
+
+// Partly WORKAROUND:
+// During combat, the game is not properly throttled. That's because the game uses
+// an inner loop for combat and does not iterate through the main loop.
+// It also doesn't call kGameIsRestarting. This may get fixed properly at some point
+// by rewriting the speed throttler.
+//
+// Additionally Sierra set the cycle speed of the hero to 0. Which explains
+// why the actions of the hero are so incredibly fast. This issue also happened
+// in the original interpreter, when the computer was too powerful.
+//
+// Applies to at least: English, French, German, Italian, Spanish PC floppy
+// Responsible method: combatControls::dispatchEvent (script 550) + WarriorObj in heap
+// Fixes bug #6247
+static const uint16 qfg3SignatureCombatSpeedThrottling1[] = {
+ 0x31, 0x0d, // bnt [skip code]
+ SIG_MAGICDWORD,
+ 0x89, 0xd2, // lsg global[D2h]
+ 0x35, 0x00, // ldi 0
+ 0x1e, // gt?
+ 0x31, 0x06, // bnt [skip code]
+ 0xe1, 0xd2, // -ag global[D2h] (jump skips over this)
+ 0x81, 0x58, // lag global[58h]
+ 0xa3, 0x01, // sal local[01]
+ SIG_END
+};
+
+static const uint16 qfg3PatchCombatSpeedThrottling1[] = {
+ 0x80, 0xd2, // lsg global[D2h]
+ 0x14, // or
+ 0x31, 0x06, // bnt [skip code] - saves 4 bytes
+ 0xe1, 0xd2, // -ag global[D2h]
+ 0x81, 0x58, // lag global[58h]
+ 0xa3, 0x01, // sal local[01] (jump skips over this)
+ // our code
+ 0x76, // push0
+ 0x43, 0x2c, 0x00, // callk GameIsRestarting <-- add this so that our speed throttler is triggered
+ PATCH_END
+};
+
+static const uint16 qfg3SignatureCombatSpeedThrottling2[] = {
+ SIG_MAGICDWORD,
+ SIG_UINT16(12), // priority 12
+ SIG_UINT16(0), // underbits 0
+ SIG_UINT16(0x4010), // signal 4010h
+ SIG_ADDTOOFFSET(+18),
+ SIG_UINT16(0), // scaleSignal 0
+ SIG_UINT16(128), // scaleX
+ SIG_UINT16(128), // scaleY
+ SIG_UINT16(128), // maxScale
+ SIG_UINT16(0), // cycleSpeed
+ SIG_END
+};
+
+static const uint16 qfg3PatchCombatSpeedThrottling2[] = {
+ PATCH_ADDTOOFFSET(+32),
+ PATCH_UINT16(5), // set cycleSpeed to 5
+ PATCH_END
+};
+
+// In room #750, when the hero enters from the top east path (room #755), it
+// could go out of the contained-access polygon bounds, and be able to travel
+// freely in the room.
+// The reason is that the cutoff y value (42) that determines whether the hero
+// enters from the top or bottom path is inaccurate: it's possible to enter the
+// top path from as low as y=45.
+// This patch changes the cutoff to be 50 which should be low enough.
+// It also changes the position in which the hero enters from the top east path
+// as the current location is hidden behind the tree.
+//
+// Applies to: English, French, German, Italian, Spanish and the GOG release.
+// Responsible method: enterEast::changeState (script 750)
+// Fixes bug #6693
+static const uint16 qfg3SignatureRoom750Bounds1[] = {
+ // (if (< (ego y?) 42)
+ 0x76, // push0 ("y")
+ 0x76, // push0
+ 0x81, 0x00, // lag global[0] (ego)
+ 0x4a, 0x04, // send 4
+ SIG_MAGICDWORD,
+ 0x36, // push
+ 0x35, 42, // ldi 42 <-- comparing ego.y with 42
+ 0x22, // lt?
+ SIG_END
+};
+
+static const uint16 qfg3PatchRoom750Bounds1[] = {
+ // (if (< (ego y?) 50)
+ PATCH_ADDTOOFFSET(+8),
+ 50, // 42 --> 50
+ PATCH_END
+};
+
+static const uint16 qfg3SignatureRoom750Bounds2[] = {
+ // (ego x: 294 y: 39)
+ 0x78, // push1 ("x")
+ 0x78, // push1
+ 0x38, SIG_UINT16(294), // pushi 294
+ 0x76, // push0 ("y")
+ 0x78, // push1
+ SIG_MAGICDWORD,
+ 0x39, 29, // pushi 29
+ 0x81, 0x00, // lag global[0] (ego)
+ 0x4a, 0x0c, // send 12
+ SIG_END
+};
+
+static const uint16 qfg3PatchRoom750Bounds2[] = {
+ // (ego x: 320 y: 39)
+ PATCH_ADDTOOFFSET(+3),
+ PATCH_UINT16(320), // 294 --> 320
+ PATCH_ADDTOOFFSET(+3),
+ 39, // 29 --> 39
+ PATCH_END
+};
+
+static const uint16 qfg3SignatureRoom750Bounds3[] = {
+ // (ego setMotion: MoveTo 282 29 self)
+ 0x38, SIG_SELECTOR16(setMotion), // pushi "setMotion" 0x133 in QfG3
+ 0x39, 0x04, // pushi 4
+ 0x51, SIG_ADDTOOFFSET(+1), // class MoveTo
+ 0x36, // push
+ 0x38, SIG_UINT16(282), // pushi 282
+ SIG_MAGICDWORD,
+ 0x39, 29, // pushi 29
+ 0x7c, // pushSelf
+ 0x81, 0x00, // lag global[0] (ego)
+ 0x4a, 0x0c, // send 12
+ SIG_END
+};
+
+static const uint16 qfg3PatchRoom750Bounds3[] = {
+ // (ego setMotion: MoveTo 309 35 self)
+ PATCH_ADDTOOFFSET(+9),
+ PATCH_UINT16(309), // 282 --> 309
+ PATCH_ADDTOOFFSET(+1),
+ 35, // 29 --> 35
+ PATCH_END
+};
+
+// script, description, signature patch
static const SciScriptPatcherEntry qfg3Signatures[] = {
- { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog },
- { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog },
+ { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog },
+ { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog },
+ { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt },
+ { true, 52, "export character save bug", 2, qfg3SignatureExportChar, qfg3PatchExportChar },
+ { true, 54, "import character from QfG1 bug", 1, qfg3SignatureImportQfG1Char, qfg3PatchImportQfG1Char },
+ { true, 640, "chief in hut priority fix", 1, qfg3SignatureChiefPriority, qfg3PatchChiefPriority },
+ { true, 285, "missing points for telling about initiation heap", 1, qfg3SignatureMissingPoints1, qfg3PatchMissingPoints1 },
+ { true, 285, "missing points for telling about initiation script", 1, qfg3SignatureMissingPoints2a, qfg3PatchMissingPoints2 },
+ { true, 285, "missing points for telling about initiation script", 1, qfg3SignatureMissingPoints2b, qfg3PatchMissingPoints2 },
+ { true, 550, "combat speed throttling script", 1, qfg3SignatureCombatSpeedThrottling1, qfg3PatchCombatSpeedThrottling1 },
+ { true, 550, "combat speed throttling heap", 1, qfg3SignatureCombatSpeedThrottling2, qfg3PatchCombatSpeedThrottling2 },
+ { true, 750, "hero goes out of bounds in room 750", 2, qfg3SignatureRoom750Bounds1, qfg3PatchRoom750Bounds1 },
+ { true, 750, "hero goes out of bounds in room 750", 2, qfg3SignatureRoom750Bounds2, qfg3PatchRoom750Bounds2 },
+ { true, 750, "hero goes out of bounds in room 750", 2, qfg3SignatureRoom750Bounds3, qfg3PatchRoom750Bounds3 },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -2345,6 +3770,162 @@ static const uint16 sq4FloppyPatchThrowStuffAtSequelPoliceBug[] = {
PATCH_END
};
+// Right at the start of Space Quest 4 CD, when walking up in the first room, ego will
+// immediately walk down just after entering the upper room.
+//
+// This is caused by the scripts setting ego's vertical coordinate to 189 (BDh), which is the
+// trigger in rooms to walk to the room below it. Sometimes this isn't triggered, because
+// the scripts also initiate a motion to vertical coordinate 188 (BCh). When you lower the game's speed,
+// this bug normally always triggers. And it triggers of course also in the original interpreter.
+//
+// It doesn't happen in PC floppy, because nsRect is not the same as in CD.
+//
+// We fix it by setting ego's vertical coordinate to 188 and we also initiate a motion to 187.
+//
+// Applies to at least: English PC CD
+// Responsible method: rm045::doit
+// Fixes bug: #5468
+static const uint16 sq4CdSignatureWalkInFromBelowRoom45[] = {
+ 0x76, // push0
+ SIG_MAGICDWORD,
+ 0x78, // push1
+ 0x38, SIG_UINT16(0x00bd), // pushi 00BDh
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi [setMotion selector]
+ 0x39, 0x03, // pushi 3
+ 0x51, SIG_ADDTOOFFSET(+1), // class [MoveTo]
+ 0x36, // push
+ 0x78, // push1
+ 0x76, // push0
+ 0x81, 0x00, // lag global[0]
+ 0x4a, 0x04, // send 04 -> get ego::x
+ 0x36, // push
+ 0x38, SIG_UINT16(0x00bc), // pushi 00BCh
+ SIG_END
+};
+
+static const uint16 sq4CdPatchWalkInFromBelowRoom45[] = {
+ PATCH_ADDTOOFFSET(+2),
+ 0x38, PATCH_UINT16(0x00bc), // pushi 00BCh
+ PATCH_ADDTOOFFSET(+15),
+ 0x38, PATCH_UINT16(0x00bb), // pushi 00BBh
+ PATCH_END
+};
+
+// It seems that Sierra forgot to set a script flag, when cleaning out the bank account
+// in Space Quest 4 CD. This was probably caused by the whole bank account interaction
+// getting a rewrite and polish in the CD version.
+//
+// Because of this bug, points for changing back clothes will not get awarded, which
+// makes it impossible to get a perfect point score in the CD version of the game.
+// The points are awarded by rm371::doit in script 371.
+//
+// We fix this. Bug also happened, when using the original interpreter.
+// Bug does not happen for PC floppy.
+//
+// Attention: Some Let's Plays on youtube show that points are in fact awarded. Which is true.
+// But those Let's Plays were actually created by playing a hacked Space Quest 4 version
+// (which is part Floppy, part CD version - we consider it to be effectively pirated)
+// and not the actual CD version of Space Quest 4.
+// It's easy to identify - talkie + store called "Radio Shack" -> is hacked version.
+//
+// Applies to at least: English PC CD
+// Responsible method: but2Script::changeState(2)
+// Fixes bug: #6866
+static const uint16 sq4CdSignatureGetPointsForChangingBackClothes[] = {
+ 0x35, 0x02, // ldi 02
+ SIG_MAGICDWORD,
+ 0x1a, // eq?
+ 0x30, SIG_UINT16(0x006a), // bnt [state 3]
+ 0x76,
+ SIG_ADDTOOFFSET(+46), // jump over "withdraw funds" code
+ 0x33, 0x33, // jmp [end of state 2, set cycles code]
+ SIG_ADDTOOFFSET(+51), // jump over "clean bank account" code
+ 0x35, 0x02, // ldi 02
+ 0x65, 0x1a, // aTop cycles
+ 0x33, 0x0b, // jmp [toss/ret]
+ 0x3c, // dup
+ 0x35, 0x03, // ldi 03
+ 0x1a, // eq?
+ 0x31, 0x05, // bnt [toss/ret]
+ SIG_END
+};
+
+static const uint16 sq4CdPatchGetPointsForChangingBackClothes[] = {
+ PATCH_ADDTOOFFSET(+3),
+ 0x30, PATCH_UINT16(0x0070), // bnt [state 3]
+ PATCH_ADDTOOFFSET(+47), // "withdraw funds" code
+ 0x33, 0x39, // jmp [end of state 2, set cycles code]
+ PATCH_ADDTOOFFSET(+51),
+ 0x78, // push1
+ 0x39, 0x1d, // ldi 1Dh
+ 0x45, 0x07, 0x02, // call export 7 of script 0 (set flag) -> effectively sets global 73h, bit 2
+ 0x35, 0x02, // ldi 02
+ 0x65, 0x1c, // aTop cycles
+ 0x33, 0x05, // jmp [toss/ret]
+ // check for state 3 code removed to save 6 bytes
+ PATCH_END
+};
+
+
+// For Space Quest 4 CD, Sierra added a pick up animation for Roger, when he picks up the rope.
+//
+// When the player is detected by the zombie right at the start of the game, while picking up the rope,
+// scripts bomb out. This also happens, when using the original interpreter.
+//
+// This is caused by code, that's supposed to make Roger face the arriving drone.
+// We fix it, by checking if ego::cycler is actually set before calling that code.
+//
+// Applies to at least: English PC CD
+// Responsible method: droidShoots::changeState(3)
+// Fixes bug: #6076
+static const uint16 sq4CdSignatureGettingShotWhileGettingRope[] = {
+ 0x35, 0x02, // ldi 02
+ 0x65, 0x1a, // aTop cycles
+ 0x32, SIG_UINT16(0x02fa), // jmp [end]
+ SIG_MAGICDWORD,
+ 0x3c, // dup
+ 0x35, 0x02, // ldi 02
+ 0x1a, // eq?
+ 0x31, 0x0b, // bnt [state 3 check]
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // call export 2 of script 0 -> disable controls
+ 0x35, 0x02, // ldi 02
+ 0x65, 0x1a, // aTop cycles
+ 0x32, SIG_UINT16(0x02e9), // jmp [end]
+ 0x3c, // dup
+ 0x35, 0x03, // ldi 03
+ 0x1a, // eq?
+ 0x31, 0x1e, // bnt [state 4 check]
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // call export 2 of script 0 -> disable controls again??
+ 0x7a, // push2
+ 0x89, 0x00, // lsg global[0]
+ 0x72, SIG_UINT16(0x0242), // lofsa deathDroid
+ 0x36, // push
+ 0x45, 0x0d, 0x04, // call export 13 of script 0 -> set heading of ego to face droid
+ SIG_END
+};
+
+static const uint16 sq4CdPatchGettingShotWhileGettingRope[] = {
+ PATCH_ADDTOOFFSET(+11),
+ // this makes state 2 only do the 2 cycles wait, controls should always be disabled already at this point
+ 0x2f, 0xf3, // bt [previous state aTop cycles code]
+ // Now we check for state 3, this change saves us 11 bytes
+ 0x3c, // dup
+ 0x35, 0x03, // ldi 03
+ 0x1a, // eq?
+ 0x31, 0x29, // bnt [state 4 check]
+ // new state 3 code
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // call export 2 of script 0 (disable controls, actually not needed)
+ 0x38, PATCH_SELECTOR16(cycler), // pushi cycler
+ 0x76, // push0
+ 0x81, 0x00, // lag global[0]
+ 0x4a, 0x04, // send 04 (get ego::cycler)
+ 0x30, PATCH_UINT16(10), // bnt [jump over heading call]
+ PATCH_END
+};
+
// The scripts in SQ4CD support simultaneous playing of speech and subtitles,
// but this was not available as an option. The following two patches enable
// this functionality in the game's GUI options dialog.
@@ -2439,8 +4020,11 @@ static const uint16 sq4CdPatchTextOptions[] = {
static const SciScriptPatcherEntry sq4Signatures[] = {
{ true, 298, "Floppy: endless flight", 1, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight },
{ true, 700, "Floppy: throw stuff at sequel police bug", 1, sq4FloppySignatureThrowStuffAtSequelPoliceBug, sq4FloppyPatchThrowStuffAtSequelPoliceBug },
- { true, 818, "CD: Speech and subtitles option", 1, sq4CdSignatureTextOptions, sq4CdPatchTextOptions },
+ { true, 45, "CD: walk in from below for room 45 fix", 1, sq4CdSignatureWalkInFromBelowRoom45, sq4CdPatchWalkInFromBelowRoom45 },
+ { true, 396, "CD: get points for changing back clothes fix",1, sq4CdSignatureGetPointsForChangingBackClothes, sq4CdPatchGetPointsForChangingBackClothes },
+ { true, 701, "CD: getting shot, while getting rope", 1, sq4CdSignatureGettingShotWhileGettingRope, sq4CdPatchGettingShotWhileGettingRope },
{ true, 0, "CD: Babble icon speech and subtitles fix", 1, sq4CdSignatureBabbleIcon, sq4CdPatchBabbleIcon },
+ { true, 818, "CD: Speech and subtitles option", 1, sq4CdSignatureTextOptions, sq4CdPatchTextOptions },
{ true, 818, "CD: Speech and subtitles option button", 1, sq4CdSignatureTextOptionsButton, sq4CdPatchTextOptionsButton },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -2471,6 +4055,29 @@ static const uint16 sq1vgaPatchUlenceFlatsTimepodGfxGlitch[] = {
PATCH_END
};
+// In Ulence Flats, there is a space ship, that you will use at some point.
+// Near that space ship are 2 force field generators.
+// When you look at the top of those generators, the game will crash.
+// This happens also in Sierra SCI. It's caused by a jump, that goes out of bounds.
+// We currently do not know if this was caused by a compiler glitch or if it was a developer error.
+// Anyway we patch this glitchy code, so that the game won't crash anymore.
+//
+// Applies to at least: English Floppy
+// Responsible method: radar1::doVerb
+// Fixes bug: #6816
+static const uint16 sq1vgaSignatureUlenceFlatsGeneratorGlitch[] = {
+ SIG_MAGICDWORD, 0x1a, // eq?
+ 0x30, SIG_UINT16(0xcdf4), // bnt absolute 0xf000
+ SIG_END
+};
+
+static const uint16 sq1vgaPatchUlenceFlatsGeneratorGlitch[] = {
+ PATCH_ADDTOOFFSET(+1),
+ 0x32, PATCH_UINT16(0x0000), // jmp 0x0000 (waste bytes)
+ PATCH_END
+};
+
+// No documentation for this patch (TODO)
static const uint16 sq1vgaSignatureEgoShowsCard[] = {
SIG_MAGICDWORD,
0x38, SIG_SELECTOR16(timesShownID), // push "timesShownID"
@@ -2534,24 +4141,35 @@ static const uint16 sq1vgaSignatureSpiderDroidTiming[] = {
0x30, SIG_UINT16(0x0005), // bnt [further method code]
0x35, 0x00, // ldi 00
0x32, SIG_UINT16(0x0052), // jmp [super-call]
- 0x89, 0xa6, // lsg global[a6]
+ 0x89, 0xa6, // lsg global[a6] <-- flag gets set to 1 when ego went up the skeleton tail, when going down it's set to 2
0x35, 0x01, // ldi 01
0x1a, // eq?
- 0x30, SIG_UINT16(0x0012), // bnt [2nd code], in case global A6 <> 1
+ 0x30, SIG_UINT16(0x0012), // bnt [PChase set code], in case global A6 <> 1
0x81, 0xb5, // lag global[b5]
- 0x30, SIG_UINT16(0x000d), // bnt [2nd code], in case global B5 == 0
+ 0x30, SIG_UINT16(0x000d), // bnt [PChase set code], in case global B5 == 0
0x38, SIG_UINT16(0x008c), // pushi 008c
0x78, // push1
0x72, SIG_UINT16(0x1cb6), // lofsa 1CB6 (moveToPath)
0x36, // push
0x54, 0x06, // self 06
0x32, SIG_UINT16(0x0038), // jmp [super-call]
+ // PChase set call
0x81, 0xb5, // lag global[B5]
0x18, // not
0x30, SIG_UINT16(0x0032), // bnt [super-call], in case global B5 <> 0
+ // followed by:
+ // is spider in current room
+ // is global A6h == 2? -> set PChase
SIG_END
}; // 58 bytes)
+// Global A6h <> 1 (did NOT went up the skeleton)
+// Global B5h = 0 -> set PChase
+// Global B5h <> 0 -> do not do anything
+// Global A6h = 1 (did went up the skeleton)
+// Global B5h = 0 -> set PChase
+// Global B5h <> 0 -> set moveToPath
+
static const uint16 sq1vgaPatchSpiderDroidTiming[] = {
0x63, 0x4e, // pToa script
0x2f, 0x68, // bt [super-call]
@@ -2576,8 +4194,8 @@ static const uint16 sq1vgaPatchSpiderDroidTiming[] = {
0x65, 0x4c, // aTop cycleSpeed
0x65, 0x5e, // aTop moveSpeed
// new code end
- 0x89, 0xb5, // lsg global[B5]
- 0x31, 0x13, // bnt [2nd code chunk]
+ 0x81, 0xb5, // lag global[B5]
+ 0x31, 0x13, // bnt [PChase code chunk]
0x89, 0xa6, // lsg global[A6]
0x35, 0x01, // ldi 01
0x1a, // eq?
@@ -2595,6 +4213,7 @@ static const uint16 sq1vgaPatchSpiderDroidTiming[] = {
// script, description, signature patch
static const SciScriptPatcherEntry sq1vgaSignatures[] = {
{ true, 45, "Ulence Flats: timepod graphic glitch", 1, sq1vgaSignatureUlenceFlatsTimepodGfxGlitch, sq1vgaPatchUlenceFlatsTimepodGfxGlitch },
+ { true, 45, "Ulence Flats: force field generator glitch", 1, sq1vgaSignatureUlenceFlatsGeneratorGlitch, sq1vgaPatchUlenceFlatsGeneratorGlitch },
{ true, 58, "Sarien armory droid zapping ego first time", 1, sq1vgaSignatureEgoShowsCard, sq1vgaPatchEgoShowsCard },
{ true, 704, "spider droid timing issue", 1, sq1vgaSignatureSpiderDroidTiming, sq1vgaPatchSpiderDroidTiming },
SCI_SIGNATUREENTRY_TERMINATOR
@@ -2670,6 +4289,7 @@ ScriptPatcher::ScriptPatcher() {
_selectorIdTable[selectorNr] = -1;
_runtimeTable = NULL;
+ _isMacSci11 = false;
}
ScriptPatcher::~ScriptPatcher() {
@@ -2678,7 +4298,7 @@ ScriptPatcher::~ScriptPatcher() {
}
// will actually patch previously found signature area
-void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset, const bool isMacSci11) {
+void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset) {
const uint16 *patchData = patchEntry->patchData;
byte orgData[PATCH_VALUELIMIT];
int32 offset = signatureOffset;
@@ -2742,7 +4362,7 @@ void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *sc
default:
byte1 = 0; byte2 = 0;
}
- if (!isMacSci11) {
+ if (!_isMacSci11) {
scriptData[offset++] = byte1;
scriptData[offset++] = byte2;
} else {
@@ -2769,102 +4389,107 @@ void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *sc
}
}
-// will return -1 if no match was found, otherwise an offset to the start of the signature match
-int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize, const bool isMacSci11) {
- if (scriptSize < 4) // we need to find a DWORD, so less than 4 bytes is not okay
- return -1;
+bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const byte *scriptData, const uint32 scriptSize) {
+ uint16 sigSelector = 0;
+
+ uint16 sigWord = *signatureData;
+ while (sigWord != SIG_END) {
+ uint16 sigCommand = sigWord & SIG_COMMANDMASK;
+ uint16 sigValue = sigWord & SIG_VALUEMASK;
+ switch (sigCommand) {
+ case SIG_CODE_ADDTOOFFSET: {
+ // add value to offset
+ byteOffset += sigValue;
+ break;
+ }
+ case SIG_CODE_UINT16:
+ case SIG_CODE_SELECTOR16: {
+ if ((byteOffset + 1) < scriptSize) {
+ byte byte1;
+ byte byte2;
- const uint32 magicDWord = runtimeEntry->magicDWord; // is platform-specific BE/LE form, so that the later match will work
- const uint32 searchLimit = scriptSize - 3;
- uint32 DWordOffset = 0;
- // first search for the magic DWORD
- while (DWordOffset < searchLimit) {
- if (magicDWord == READ_UINT32(scriptData + DWordOffset)) {
- // magic DWORD found, check if actual signature matches
- uint32 offset = DWordOffset + runtimeEntry->magicOffset;
- uint32 byteOffset = offset;
- const uint16 *signatureData = patchEntry->signatureData;
- uint16 sigSelector = 0;
-
- uint16 sigWord = *signatureData;
- while (sigWord != SIG_END) {
- uint16 sigCommand = sigWord & SIG_COMMANDMASK;
- uint16 sigValue = sigWord & SIG_VALUEMASK;
switch (sigCommand) {
- case SIG_CODE_ADDTOOFFSET: {
- // add value to offset
- byteOffset += sigValue;
+ case SIG_CODE_UINT16: {
+ byte1 = sigValue & SIG_BYTEMASK;
+ signatureData++; sigWord = *signatureData;
+ if (sigWord & SIG_COMMANDMASK)
+ error("Script-Patcher: signature inconsistent\nFaulty signature: '%s'", signatureDescription);
+ byte2 = sigWord & SIG_BYTEMASK;
break;
}
- case SIG_CODE_UINT16:
case SIG_CODE_SELECTOR16: {
- if ((byteOffset + 1) < scriptSize) {
- byte byte1;
- byte byte2;
-
- switch (sigCommand) {
- case SIG_CODE_UINT16: {
- byte1 = sigValue & SIG_BYTEMASK;
- signatureData++; sigWord = *signatureData;
- if (sigWord & SIG_COMMANDMASK)
- error("Script-Patcher: signature inconsistent\nFaulty patch: '%s'", patchEntry->description);
- byte2 = sigWord & SIG_BYTEMASK;
- break;
- }
- case SIG_CODE_SELECTOR16: {
- sigSelector = _selectorIdTable[sigValue];
- byte1 = sigSelector & 0xFF;
- byte2 = sigSelector >> 8;
- break;
- }
- default:
- byte1 = 0; byte2 = 0;
- }
- if (!isMacSci11) {
- if ((scriptData[byteOffset] != byte1) || (scriptData[byteOffset + 1] != byte2))
- sigWord = SIG_MISMATCH;
- } else {
- // SCI1.1+ on macintosh had uint16s in script in BE-order
- if ((scriptData[byteOffset] != byte2) || (scriptData[byteOffset + 1] != byte1))
- sigWord = SIG_MISMATCH;
- }
- byteOffset += 2;
- } else {
- sigWord = SIG_MISMATCH;
- }
+ sigSelector = _selectorIdTable[sigValue];
+ byte1 = sigSelector & 0xFF;
+ byte2 = sigSelector >> 8;
break;
}
- case SIG_CODE_SELECTOR8: {
- if (byteOffset < scriptSize) {
- sigSelector = _selectorIdTable[sigValue];
- if (sigSelector & 0xFF00)
- error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty patch: '%s'", patchEntry->description);
- if (scriptData[byteOffset] != (sigSelector & 0xFF))
- sigWord = SIG_MISMATCH;
- byteOffset++;
- } else {
- sigWord = SIG_MISMATCH; // out of bounds
- }
- break;
+ default:
+ byte1 = 0; byte2 = 0;
}
- case SIG_CODE_BYTE:
- if (byteOffset < scriptSize) {
- if (scriptData[byteOffset] != sigWord)
- sigWord = SIG_MISMATCH;
- byteOffset++;
- } else {
- sigWord = SIG_MISMATCH; // out of bounds
- }
+ if (!_isMacSci11) {
+ if ((scriptData[byteOffset] != byte1) || (scriptData[byteOffset + 1] != byte2))
+ sigWord = SIG_MISMATCH;
+ } else {
+ // SCI1.1+ on macintosh had uint16s in script in BE-order
+ if ((scriptData[byteOffset] != byte2) || (scriptData[byteOffset + 1] != byte1))
+ sigWord = SIG_MISMATCH;
}
+ byteOffset += 2;
+ } else {
+ sigWord = SIG_MISMATCH;
+ }
+ break;
+ }
+ case SIG_CODE_SELECTOR8: {
+ if (byteOffset < scriptSize) {
+ sigSelector = _selectorIdTable[sigValue];
+ if (sigSelector & 0xFF00)
+ error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty signature: '%s'", signatureDescription);
+ if (scriptData[byteOffset] != (sigSelector & 0xFF))
+ sigWord = SIG_MISMATCH;
+ byteOffset++;
+ } else {
+ sigWord = SIG_MISMATCH; // out of bounds
+ }
+ break;
+ }
+ case SIG_CODE_BYTE:
+ if (byteOffset < scriptSize) {
+ if (scriptData[byteOffset] != sigWord)
+ sigWord = SIG_MISMATCH;
+ byteOffset++;
+ } else {
+ sigWord = SIG_MISMATCH; // out of bounds
+ }
+ }
- if (sigWord == SIG_MISMATCH)
- break;
+ if (sigWord == SIG_MISMATCH)
+ break;
- signatureData++;
- sigWord = *signatureData;
- }
+ signatureData++;
+ sigWord = *signatureData;
+ }
- if (sigWord == SIG_END) // signature fully matched?
+ if (sigWord == SIG_END) // signature fully matched?
+ return true;
+ return false;
+}
+
+// will return -1 if no match was found, otherwise an offset to the start of the signature match
+int32 ScriptPatcher::findSignature(uint32 magicDWord, int magicOffset, const uint16 *signatureData, const char *patchDescription, const byte *scriptData, const uint32 scriptSize) {
+ if (scriptSize < 4) // we need to find a DWORD, so less than 4 bytes is not okay
+ return -1;
+
+ // magicDWord is in platform-specific BE/LE form, so that the later match will work, this was done for performance
+ const uint32 searchLimit = scriptSize - 3;
+ uint32 DWordOffset = 0;
+ // first search for the magic DWORD
+ while (DWordOffset < searchLimit) {
+ if (magicDWord == READ_UINT32(scriptData + DWordOffset)) {
+ // magic DWORD found, check if actual signature matches
+ uint32 offset = DWordOffset + magicOffset;
+
+ if (verifySignature(offset, signatureData, patchDescription, scriptData, scriptSize))
return offset;
}
DWordOffset++;
@@ -2873,22 +4498,147 @@ int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciS
return -1;
}
-// This method calculates the magic DWORD for each entry in the signature table
-// and it also initializes the selector table for selectors used in the signatures/patches of the current game
-void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable, bool isMacSci11) {
- const SciScriptPatcherEntry *curEntry = patchTable;
- SciScriptPatcherRuntimeEntry *curRuntimeEntry;
+int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, const SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize) {
+ return findSignature(runtimeEntry->magicDWord, runtimeEntry->magicOffset, patchEntry->signatureData, patchEntry->description, scriptData, scriptSize);
+}
+
+// Attention: Magic DWord is returned using platform specific byte order. This is done on purpose for performance.
+void ScriptPatcher::calculateMagicDWordAndVerify(const char *signatureDescription, const uint16 *signatureData, bool magicDWordIncluded, uint32 &calculatedMagicDWord, int &calculatedMagicDWordOffset) {
Selector curSelector = -1;
- int step;
int magicOffset;
byte magicDWord[4];
int magicDWordLeft = 0;
- const uint16 *curData;
uint16 curWord;
uint16 curCommand;
uint32 curValue;
byte byte1 = 0;
byte byte2 = 0;
+
+ memset(magicDWord, 0, sizeof(magicDWord));
+
+ curWord = *signatureData;
+ magicOffset = 0;
+ while (curWord != SIG_END) {
+ curCommand = curWord & SIG_COMMANDMASK;
+ curValue = curWord & SIG_VALUEMASK;
+ switch (curCommand) {
+ case SIG_MAGICDWORD: {
+ if (magicDWordIncluded) {
+ if ((calculatedMagicDWord) || (magicDWordLeft))
+ error("Script-Patcher: Magic-DWORD specified multiple times in signature\nFaulty patch: '%s'", signatureDescription);
+ magicDWordLeft = 4;
+ calculatedMagicDWordOffset = magicOffset;
+ } else {
+ error("Script-Patcher: Magic-DWORD sequence found in patch data\nFaulty patch: '%s'", signatureDescription);
+ }
+ break;
+ }
+ case SIG_CODE_ADDTOOFFSET: {
+ magicOffset -= curValue;
+ if (magicDWordLeft)
+ error("Script-Patcher: Magic-DWORD contains AddToOffset command\nFaulty patch: '%s'", signatureDescription);
+ break;
+ }
+ case SIG_CODE_UINT16:
+ case SIG_CODE_SELECTOR16: {
+ // UINT16 or 1
+ switch (curCommand) {
+ case SIG_CODE_UINT16: {
+ signatureData++; curWord = *signatureData;
+ if (curWord & SIG_COMMANDMASK)
+ error("Script-Patcher: signature entry inconsistent\nFaulty patch: '%s'", signatureDescription);
+ if (!_isMacSci11) {
+ byte1 = curValue;
+ byte2 = curWord & SIG_BYTEMASK;
+ } else {
+ byte1 = curWord & SIG_BYTEMASK;
+ byte2 = curValue;
+ }
+ break;
+ }
+ case SIG_CODE_SELECTOR16: {
+ curSelector = _selectorIdTable[curValue];
+ if (curSelector == -1) {
+ curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
+ _selectorIdTable[curValue] = curSelector;
+ }
+ if (!_isMacSci11) {
+ byte1 = curSelector & 0x00FF;
+ byte2 = curSelector >> 8;
+ } else {
+ byte1 = curSelector >> 8;
+ byte2 = curSelector & 0x00FF;
+ }
+ break;
+ }
+ }
+ magicOffset -= 2;
+ if (magicDWordLeft) {
+ // Remember current word for Magic DWORD
+ magicDWord[4 - magicDWordLeft] = byte1;
+ magicDWordLeft--;
+ if (magicDWordLeft) {
+ magicDWord[4 - magicDWordLeft] = byte2;
+ magicDWordLeft--;
+ }
+ if (!magicDWordLeft) {
+ // Magic DWORD is now known, convert to platform specific byte order
+ calculatedMagicDWord = READ_UINT32(magicDWord);
+ }
+ }
+ break;
+ }
+ case SIG_CODE_BYTE:
+ case SIG_CODE_SELECTOR8: {
+ if (curCommand == SIG_CODE_SELECTOR8) {
+ curSelector = _selectorIdTable[curValue];
+ if (curSelector == -1) {
+ curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
+ _selectorIdTable[curValue] = curSelector;
+ if (curSelector != -1) {
+ if (curSelector & 0xFF00)
+ error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty patch: '%s'", signatureDescription);
+ }
+ }
+ curValue = curSelector;
+ }
+ magicOffset--;
+ if (magicDWordLeft) {
+ // Remember current byte for Magic DWORD
+ magicDWord[4 - magicDWordLeft] = (byte)curValue;
+ magicDWordLeft--;
+ if (!magicDWordLeft) {
+ // Magic DWORD is now known, convert to platform specific byte order
+ calculatedMagicDWord = READ_UINT32(magicDWord);
+ }
+ }
+ break;
+ }
+ case PATCH_CODE_GETORIGINALBYTEADJUST: {
+ signatureData++; // skip over extra uint16
+ break;
+ }
+ default:
+ break;
+ }
+ signatureData++;
+ curWord = *signatureData;
+ }
+
+ if (magicDWordLeft)
+ error("Script-Patcher: Magic-DWORD beyond End-Of-Signature\nFaulty patch: '%s'", signatureDescription);
+ if (magicDWordIncluded) {
+ if (!calculatedMagicDWord) {
+ error("Script-Patcher: Magic-DWORD not specified in signature\nFaulty patch: '%s'", signatureDescription);
+ }
+ }
+}
+
+// This method calculates the magic DWORD for each entry in the signature table
+// and it also initializes the selector table for selectors used in the signatures/patches of the current game
+void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable) {
+ const SciScriptPatcherEntry *curEntry = patchTable;
+ SciScriptPatcherRuntimeEntry *curRuntimeEntry;
int patchEntryCount = 0;
// Count entries and allocate runtime data
@@ -2902,120 +4652,14 @@ void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable, bool
curRuntimeEntry = _runtimeTable;
while (curEntry->signatureData) {
// process signature
- memset(magicDWord, 0, sizeof(magicDWord));
-
curRuntimeEntry->active = curEntry->defaultActive;
curRuntimeEntry->magicDWord = 0;
curRuntimeEntry->magicOffset = 0;
- for (step = 0; step < 2; step++) {
- switch (step) {
- case 0: curData = curEntry->signatureData; break;
- case 1: curData = curEntry->patchData; break;
- }
-
- curWord = *curData;
- magicOffset = 0;
- while (curWord != SIG_END) {
- curCommand = curWord & SIG_COMMANDMASK;
- curValue = curWord & SIG_VALUEMASK;
- switch (curCommand) {
- case SIG_MAGICDWORD: {
- if (step == 0) {
- if ((curRuntimeEntry->magicDWord) || (magicDWordLeft))
- error("Script-Patcher: Magic-DWORD specified multiple times in signature\nFaulty patch: '%s'", curEntry->description);
- magicDWordLeft = 4;
- curRuntimeEntry->magicOffset = magicOffset;
- }
- break;
- }
- case SIG_CODE_ADDTOOFFSET: {
- magicOffset -= curValue;
- if (magicDWordLeft)
- error("Script-Patcher: Magic-DWORD contains AddToOffset command\nFaulty patch: '%s'", curEntry->description);
- break;
- }
- case SIG_CODE_UINT16:
- case SIG_CODE_SELECTOR16: {
- // UINT16 or 1
- switch (curCommand) {
- case SIG_CODE_UINT16: {
- curData++; curWord = *curData;
- if (curWord & SIG_COMMANDMASK)
- error("Script-Patcher: signature entry inconsistent\nFaulty patch: '%s'", curEntry->description);
- if (!isMacSci11) {
- byte1 = curValue;
- byte2 = curWord & SIG_BYTEMASK;
- } else {
- byte1 = curWord & SIG_BYTEMASK;
- byte2 = curValue;
- }
- break;
- }
- case SIG_CODE_SELECTOR16: {
- curSelector = _selectorIdTable[curValue];
- if (curSelector == -1) {
- curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
- _selectorIdTable[curValue] = curSelector;
- }
- if (!isMacSci11) {
- byte1 = curSelector & 0x00FF;
- byte2 = curSelector >> 8;
- } else {
- byte1 = curSelector >> 8;
- byte2 = curSelector & 0x00FF;
- }
- break;
- }
- }
- magicOffset -= 2;
- if (magicDWordLeft) {
- // Remember current word for Magic DWORD
- magicDWord[4 - magicDWordLeft] = byte1;
- magicDWordLeft--;
- if (magicDWordLeft) {
- magicDWord[4 - magicDWordLeft] = byte2;
- magicDWordLeft--;
- }
- if (!magicDWordLeft) {
- curRuntimeEntry->magicDWord = READ_LE_UINT32(magicDWord);
- }
- }
- break;
- }
- case SIG_CODE_BYTE:
- case SIG_CODE_SELECTOR8: {
- if (curCommand == SIG_CODE_SELECTOR8) {
- curSelector = _selectorIdTable[curValue];
- if (curSelector == -1) {
- curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
- _selectorIdTable[curValue] = curSelector;
- if (curSelector != -1) {
- if (curSelector & 0xFF00)
- error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty patch: '%s'", curEntry->description);
- }
- }
- curValue = curSelector;
- }
- magicOffset--;
- if (magicDWordLeft) {
- // Remember current byte for Magic DWORD
- magicDWord[4 - magicDWordLeft] = (byte)curValue;
- magicDWordLeft--;
- if (!magicDWordLeft) {
- curRuntimeEntry->magicDWord = READ_LE_UINT32(magicDWord);
- }
- }
- }
- }
- curData++;
- curWord = *curData;
- }
- }
- if (magicDWordLeft)
- error("Script-Patcher: Magic-DWORD beyond End-Of-Signature\nFaulty patch: '%s'", curEntry->description);
- if (!curRuntimeEntry->magicDWord)
- error("Script-Patcher: Magic-DWORD not specified in signature\nFaulty patch: '%s'", curEntry->description);
+ // We verify the signature data and remember the calculated magic DWord from the signature data
+ calculateMagicDWordAndVerify(curEntry->description, curEntry->signatureData, true, curRuntimeEntry->magicDWord, curRuntimeEntry->magicOffset);
+ // We verify the patch data
+ calculateMagicDWordAndVerify(curEntry->description, curEntry->patchData, false, curRuntimeEntry->magicDWord, curRuntimeEntry->magicOffset);
curEntry++; curRuntimeEntry++;
}
@@ -3073,6 +4717,12 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3
case GID_KQ6:
signatureTable = kq6Signatures;
break;
+ case GID_KQ7:
+ signatureTable = kq7Signatures;
+ break;
+ case GID_LAURABOW:
+ signatureTable = laurabow1Signatures;
+ break;
case GID_LAURABOW2:
signatureTable = laurabow2Signatures;
break;
@@ -3120,7 +4770,7 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3
}
if (signatureTable) {
- bool isMacSci11 = (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1);
+ _isMacSci11 = (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1);
if (!_runtimeTable) {
// Abort, in case selectors are not yet initialized (happens for games w/o selector-dictionary)
@@ -3128,7 +4778,7 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3
return;
// signature table needs to get initialized (Magic DWORD set, selector table set)
- initSignature(signatureTable, isMacSci11);
+ initSignature(signatureTable);
// Do additional game-specific initialization
switch (gameId) {
@@ -3163,11 +4813,11 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3
int32 foundOffset = 0;
int16 applyCount = curEntry->applyCount;
do {
- foundOffset = findSignature(curEntry, curRuntimeEntry, scriptData, scriptSize, isMacSci11);
+ foundOffset = findSignature(curEntry, curRuntimeEntry, scriptData, scriptSize);
if (foundOffset != -1) {
// found, so apply the patch
debugC(kDebugLevelScriptPatcher, "Script-Patcher: '%s' on script %d offset %d", curEntry->description, scriptNr, foundOffset);
- applyPatch(curEntry, scriptData, scriptSize, foundOffset, isMacSci11);
+ applyPatch(curEntry, scriptData, scriptSize, foundOffset);
}
applyCount--;
} while ((foundOffset != -1) && (applyCount));