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.cpp274
1 files changed, 264 insertions, 10 deletions
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index d4dddb6faf..d2c3356d54 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -59,6 +59,58 @@ struct SciScriptSignature {
// - rinse and repeat
// ===========================================================================
+// Conquests of Camelot
+// At the bazaar in Jerusalem, it's possible to see a girl taking a shower.
+// If you get too close, you get warned by the father - if you don't get away,
+// he will kill you.
+// Instead of walking there manually, it's also possible to enter "look window"
+// and ego will automatically walk to the window. It seems that this is something
+// that wasn't properly implemented, because instead of getting killed, you will
+// get an "Oops" message in Sierra SCI.
+//
+// This is caused by peepingTom in script 169 not getting properly initialized.
+// peepingTom calls the object behind global b9h. This global variable is
+// properly initialized, when walking there manually (method fawaz::doit).
+// When you instead walk there automatically (method fawaz::handleEvent), that
+// global isn't initialized, which then results in the Oops-message in Sierra SCI
+// and an error message in ScummVM/SCI.
+//
+// We fix the script by patching in a jump to the proper code inside fawaz::doit.
+// Responsible method: fawaz::handleEvent
+// Fixes bug #3614969
+const byte camelotSignaturePeepingTom[] = {
+ 5,
+ 0x72, 0x7e, 0x07, // lofsa fawaz <-- start of proper initializion code
+ 0xa1, 0xb9, // sag b9h
+ +255, 0,
+ +255, 0,
+ +61, 19, // skip 571 bytes
+ 0x39, 0x7a, // pushi 7a <-- initialization code when walking automatically
+ 0x78, // push1
+ 0x7a, // push2
+ 0x38, 0xa9, 0x00, // pushi 00a9 - script 169
+ 0x78, // push1
+ 0x43, 0x02, 0x04, // call kScriptID
+ 0x36, // push
+ 0x81, 0x00, // lag 00
+ 0x4a, 0x06, // send 06
+ 0x32, 0x20, 0x05, // jmp [end of fawaz::handleEvent]
+ 0
+};
+
+const uint16 camelotPatchPeepingTom[] = {
+ PATCH_ADDTOOFFSET | +576,
+ 0x32, 0xbd, 0xfd, // jmp to fawaz::doit / properly init peepingTom code
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature camelotSignatures[] = {
+ { 62, "fix peepingTom Sierra bug", 1, PATCH_MAGICDWORD(0x7e, 0x07, 0xa1, 0xb9), -1, camelotSignaturePeepingTom, camelotPatchPeepingTom },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
// stayAndHelp::changeState (0) is called when ego swims to the left or right
// boundaries of room 660. Normally a textbox is supposed to get on screen
// but the call is wrong, so not only do we get an error message the script
@@ -742,6 +794,48 @@ const SciScriptSignature longbowSignatures[] = {
};
// ===========================================================================
+// Leisure Suit Larry 2
+// On the plane, Larry is able to wear the parachute. This grants 4 points.
+// In early versions of LSL2, it was possible to get "unlimited" points by
+// simply wearing it multiple times.
+// They fixed it in later versions by remembering, if the parachute was already
+// used before.
+// But instead of adding it properly, it seems they hacked the script / forgot
+// to replace script 0 as well, which holds information about how many global
+// variables are allocated at the start of the game.
+// The script tries to read an out-of-bounds global variable, which somewhat
+// "worked" in SSCI, but ScummVM/SCI doesn't allow that.
+// That's why those points weren't granted here at all.
+// We patch the script to use global 90, which seems to be unused in the whole game.
+// Responsible method: rm63Script::handleEvent
+// Fixes bug #3614419
+const byte larry2SignatureWearParachutePoints[] = {
+ 16,
+ 0x35, 0x01, // ldi 01
+ 0xa1, 0x8e, // sag 8e
+ 0x80, 0xe0, 0x01, // lag 1e0
+ 0x18, // not
+ 0x30, 0x0f, 0x00, // bnt [don't give points]
+ 0x35, 0x01, // ldi 01
+ 0xa0, 0xe0, 0x01, // sag 1e0
+ 0
+};
+
+const uint16 larry2PatchWearParachutePoints[] = {
+ PATCH_ADDTOOFFSET | +4,
+ 0x80, 0x5a, 0x00, // lag 5a (global 90)
+ PATCH_ADDTOOFFSET | +6,
+ 0xa0, 0x5a, 0x00, // sag 5a (global 90)
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature larry2Signatures[] = {
+ { 63, "plane: no points for wearing plane", 1, PATCH_MAGICDWORD(0x8e, 0x80, 0xe0, 0x01), -3, larry2SignatureWearParachutePoints, larry2PatchWearParachutePoints },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
// this is called on every death dialog. Problem is at least the german
// version of lsl6 gets title text that is far too long for the
// available temp space resulting in temp space corruption
@@ -868,6 +962,68 @@ const SciScriptSignature mothergoose256Signatures[] = {
};
// ===========================================================================
+// Police Quest 1 VGA
+// 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.
+// Problem is that it's possible to click again, which then results in a
+// disposed object getting accessed. This happened to work by pure luck in
+// SSCI.
+// This patch changes the code, so that the gun is actually given away
+// when the 2 seconds have passed and the locker got closed.
+// Responsible method: putGun::changeState (script 341)
+// Fixes bug #3036933 / #3303802
+const byte pq1vgaSignaturePutGunInLockerBug[] = {
+ 5,
+ 0x35, 0x00, // ldi 00
+ 0x1a, // eq?
+ 0x31, 0x25, // bnt [next state check]
+ +22, 29, // [skip 22 bytes]
+ 0x38, 0x5f, 0x01, // pushi 15fh
+ 0x78, // push1
+ 0x76, // push0
+ 0x81, 0x00, // lag 00
+ 0x4a, 0x06, // send 06 - ego::put(0)
+ 0x35, 0x02, // ldi 02
+ 0x65, 0x1c, // aTop 1c (set timer to 2 seconds)
+ 0x33, 0x0e, // jmp [end of method]
+ 0x3c, // dup --- next state check target
+ 0x35, 0x01, // ldi 01
+ 0x1a, // eq?
+ 0x31, 0x08, // bnt [end of method]
+ 0x39, 0x6f, // pushi 6fh
+ 0x76, // push0
+ 0x72, 0x88, 0x00, // lofsa 0088
+ 0x4a, 0x04, // send 04 - locker::dispose
+ 0
+};
+
+const uint16 pq1vgaPatchPutGunInLockerBug[] = {
+ PATCH_ADDTOOFFSET | +3,
+ 0x31, 0x1c, // bnt [next state check]
+ PATCH_ADDTOOFFSET | +22,
+ 0x35, 0x02, // ldi 02
+ 0x65, 0x1c, // aTop 1c (set timer to 2 seconds)
+ 0x33, 0x17, // jmp [end of method]
+ 0x3c, // dup --- next state check target
+ 0x35, 0x01, // ldi 01
+ 0x1a, // eq?
+ 0x31, 0x11, // bnt [end of method]
+ 0x38, 0x5f, 0x01, // pushi 15fh
+ 0x78, // push1
+ 0x76, // push0
+ 0x81, 0x00, // lag 00
+ 0x4a, 0x06, // send 06 - ego::put(0)
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature pq1vgaSignatures[] = {
+ { 341, "put gun in locker bug", 1, PATCH_MAGICDWORD(0x38, 0x5f, 0x01, 0x78), -27, pq1vgaSignaturePutGunInLockerBug, pq1vgaPatchPutGunInLockerBug },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
// script 215 of qfg1vga pointBox::doit actually processes button-presses
// during fighting with monsters. It strangely also calls kGetEvent. Because
// the main User::doit also calls kGetEvent it's pure luck, where the event
@@ -995,14 +1151,75 @@ const uint16 qfg1vgaPatchMoveToCastleGate[] = {
PATCH_END
};
+// Typo in the original Sierra scripts
+// Looking at a cheetaur resulted in a text about a Saurus Rex
+// The code treats both monster types the same.
+// Responsible method: smallMonster::doVerb
+// Fixes bug #3604943.
+const byte qfg1vgaSignatureCheetaurDescription[] = {
+ 16,
+ 0x34, 0xb8, 0x01, // ldi 01b8
+ 0x1a, // eq?
+ 0x31, 0x16, // bnt 16
+ 0x38, 0x27, 0x01, // pushi 0127
+ 0x39, 0x06, // pushi 06
+ 0x39, 0x03, // pushi 03
+ 0x78, // push1
+ 0x39, 0x12, // pushi 12 -> monster type Saurus Rex
+ 0
+};
+
+const uint16 qfg1vgaPatchCheetaurDescription[] = {
+ PATCH_ADDTOOFFSET | +14,
+ 0x39, 0x11, // pushi 11 -> monster type cheetaur
+ PATCH_END
+};
+
+// In the "funny" room (Yorick's room) in QfG1 VGA, pulling the chain and
+// then pressing the button on the right side of the room results in
+// a broken game. This also happens in SSCI.
+// Problem is that the Sierra programmers forgot to disable the door, that
+// gets opened by pulling the chain. So when ego falls down and then
+// rolls through the door, one method thinks that the player walks through
+// it and acts that way and the other method is still doing the roll animation.
+// 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.
+// Responsible method: happyFace::changeState, door11::doit
+// Fixes bug #3585793
+const byte qfg1vgaSignatureFunnyRoomFix[] = {
+ 14,
+ 0x65, 0x14, // aTop 14 (state)
+ 0x36, // push
+ 0x3c, // dup
+ 0x35, 0x00, // ldi 00
+ 0x1a, // eq?
+ 0x30, 0x25, 0x00, // bnt 0025 [-> next state]
+ 0x35, 0x01, // ldi 01
+ 0xa3, 0x4e, // sal 4e
+ 0
+};
+
+const uint16 qfg1vgaPatchFunnyRoomFix[] = {
+ PATCH_ADDTOOFFSET | +3,
+ 0x2e, 0x29, 0x00, // bt 0029 [-> next state] - saves 4 bytes
+ 0x35, 0x01, // ldi 01
+ 0xa3, 0x4e, // sal 4e
+ 0xa3, 0x05, // sal 05 (sets local 5 to 1)
+ 0xa3, 0x05, // and again to make absolutely sure (actually to waste 2 bytes)
+ PATCH_END
+};
+
// script, description, magic DWORD, adjust
const SciScriptSignature qfg1vgaSignatures[] = {
- { 215, "fight event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
- { 216, "weapon master event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
- { 814, "window text temp space", 1, PATCH_MAGICDWORD(0x3f, 0xba, 0x87, 0x00), 0, qfg1vgaSignatureTempSpace, qfg1vgaPatchTempSpace },
- { 814, "dialog header offset", 3, PATCH_MAGICDWORD(0x5b, 0x04, 0x80, 0x36), 0, qfg1vgaSignatureDialogHeader, qfg1vgaPatchDialogHeader },
- { 331, "moving to crusher", 1, PATCH_MAGICDWORD(0x51, 0x1f, 0x36, 0x39), 0, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher },
- { 41, "moving to castle gate", 1, PATCH_MAGICDWORD(0x51, 0x1f, 0x36, 0x39), 0, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate },
+ { 215, "fight event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
+ { 216, "weapon master event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
+ { 814, "window text temp space", 1, PATCH_MAGICDWORD(0x3f, 0xba, 0x87, 0x00), 0, qfg1vgaSignatureTempSpace, qfg1vgaPatchTempSpace },
+ { 814, "dialog header offset", 3, PATCH_MAGICDWORD(0x5b, 0x04, 0x80, 0x36), 0, qfg1vgaSignatureDialogHeader, qfg1vgaPatchDialogHeader },
+ { 331, "moving to crusher", 1, PATCH_MAGICDWORD(0x51, 0x1f, 0x36, 0x39), 0, qfg1vgaSignatureMoveToCrusher, qfg1vgaPatchMoveToCrusher },
+ { 41, "moving to castle gate", 1, PATCH_MAGICDWORD(0x51, 0x1f, 0x36, 0x39), 0, qfg1vgaSignatureMoveToCastleGate, qfg1vgaPatchMoveToCastleGate },
+ { 210, "cheetaur description fixed", 1, PATCH_MAGICDWORD(0x34, 0xb8, 0x01, 0x1a), 0, qfg1vgaSignatureCheetaurDescription, qfg1vgaPatchCheetaurDescription },
+ { 96, "funny room script bug fixed", 1, PATCH_MAGICDWORD(0x35, 0x01, 0xa3, 0x4e), -10, qfg1vgaSignatureFunnyRoomFix, qfg1vgaPatchFunnyRoomFix },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -1250,6 +1467,32 @@ const SciScriptSignature sq4Signatures[] = {
SCI_SIGNATUREENTRY_TERMINATOR
};
+// ===========================================================================
+// When you leave Ulence Flats, another timepod is supposed to appear.
+// On fast machines, that timepod appears fully immediately and then
+// starts to appear like it should be. That first appearance is caused
+// by the scripts setting an invalid cel number and the machine being
+// so fast that there is no time for another script to actually fix
+// the cel number. On slower machines, the cel number gets fixed
+// by the cycler and that's why only fast machines are affected.
+// The same issue happens in Sierra SCI.
+// We simply set the correct starting cel number to fix the bug.
+// Responsible method: robotIntoShip::changeState(9)
+const byte sq1vgaSignatureUlenceFlatsTimepodGfxGlitch[] = {
+ 8,
+ 0x39, 0x07, // pushi 07 (ship::cel)
+ 0x78, // push1
+ 0x39, 0x0a, // pushi 0x0a (set ship::cel to 10)
+ 0x38, 0xa0, 0x00, // pushi 0x00a0 (ship::setLoop)
+ 0
+};
+
+const uint16 sq1vgaPatchUlenceFlatsTimepodGfxGlitch[] = {
+ PATCH_ADDTOOFFSET | +3,
+ 0x39, 0x09, // pushi 0x09 (set ship::cel to 9)
+ PATCH_END
+};
+
const byte sq1vgaSignatureEgoShowsCard[] = {
25,
0x38, 0x46, 0x02, // push 0x246 (set up send frame to set timesShownID)
@@ -1267,7 +1510,8 @@ const byte sq1vgaSignatureEgoShowsCard[] = {
0x36, // push (wrong, acc clobbered by class, above)
0x35, 0x03, // ldi 0x03
0x22, // lt?
- 0};
+ 0
+};
// Note that this script patch is merely a reordering of the
// instructions in the original script.
@@ -1287,13 +1531,14 @@ const uint16 sq1vgaPatchEgoShowsCard[] = {
0x4a, 0x06, // send 0x06 (set timesShownID)
0x35, 0x03, // ldi 0x03
0x22, // lt?
- PATCH_END};
+ PATCH_END
+};
// script, description, magic DWORD, adjust
const SciScriptSignature sq1vgaSignatures[] = {
- { 58, "Sarien armory droid zapping ego first time", 1, PATCH_MAGICDWORD( 0x72, 0x88, 0x15, 0x36 ), -70,
- sq1vgaSignatureEgoShowsCard, sq1vgaPatchEgoShowsCard },
+ { 45, "Ulence Flats: timepod graphic glitch", 1, PATCH_MAGICDWORD( 0x07, 0x78, 0x39, 0x0a ), -1, sq1vgaSignatureUlenceFlatsTimepodGfxGlitch, sq1vgaPatchUlenceFlatsTimepodGfxGlitch },
+ { 58, "Sarien armory droid zapping ego first time", 1, PATCH_MAGICDWORD( 0x72, 0x88, 0x15, 0x36 ), -70, sq1vgaSignatureEgoShowsCard, sq1vgaPatchEgoShowsCard },
SCI_SIGNATUREENTRY_TERMINATOR};
@@ -1389,6 +1634,9 @@ int32 Script::findSignature(const SciScriptSignature *signature, const byte *scr
void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uint32 scriptSize) {
const SciScriptSignature *signatureTable = NULL;
switch (g_sci->getGameId()) {
+ case GID_CAMELOT:
+ signatureTable = camelotSignatures;
+ break;
case GID_ECOQUEST:
signatureTable = ecoquest1Signatures;
break;
@@ -1420,12 +1668,18 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin
case GID_LONGBOW:
signatureTable = longbowSignatures;
break;
+ case GID_LSL2:
+ signatureTable = larry2Signatures;
+ break;
case GID_LSL6:
signatureTable = larry6Signatures;
break;
case GID_MOTHERGOOSE256:
signatureTable = mothergoose256Signatures;
break;
+ case GID_PQ1:
+ signatureTable = pq1vgaSignatures;
+ break;
case GID_QFG1VGA:
signatureTable = qfg1vgaSignatures;
break;