2010-08-06 22:36:46 +00:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
*/
# include "sci/sci.h"
# include "sci/engine/script.h"
2010-08-23 16:33:19 +00:00
# include "sci/engine/state.h"
2010-08-06 22:36:46 +00:00
# include "common/util.h"
namespace Sci {
2010-08-06 22:51:25 +00:00
# define PATCH_END 0xFFFF
2010-09-12 22:09:01 +00:00
# define PATCH_COMMANDMASK 0xF000
# define PATCH_VALUEMASK 0x0FFF
# define PATCH_ADDTOOFFSET 0xE000
# define PATCH_GETORIGINALBYTE 0xD000
# define PATCH_ADJUSTWORD 0xC000
# define PATCH_ADJUSTWORD_NEG 0xB000
2010-08-06 22:51:25 +00:00
# define PATCH_MAGICDWORD(a, b, c, d) CONSTANT_LE_32(a | (b << 8) | (c << 16) | (d << 24))
2010-09-12 22:09:01 +00:00
# define PATCH_VALUELIMIT 4096
2010-08-06 22:51:25 +00:00
2010-08-06 22:36:46 +00:00
struct SciScriptSignature {
uint16 scriptNr ;
const char * description ;
2010-09-06 11:58:28 +00:00
int16 applyCount ;
2010-08-06 22:36:46 +00:00
uint32 magicDWord ;
int magicOffset ;
const byte * data ;
const uint16 * patch ;
} ;
2010-09-06 12:16:21 +00:00
# define SCI_SIGNATUREENTRY_TERMINATOR { 0, NULL, 0, 0, 0, NULL, NULL }
2010-08-24 21:53:06 +00:00
2010-08-06 22:36:46 +00:00
// signatures are built like this:
// - first a counter of the bytes that follow
// - then the actual bytes that need to get matched
// - then another counter of bytes (0 for EOS)
// - if not EOS, an adjust offset and the actual bytes
// - rinse and repeat
2010-08-11 20:39:13 +00:00
// ===========================================================================
2010-08-10 15:18:18 +00:00
// 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
// is also hanging because the cue won't get sent out
// This also happens in sierra sci - ffs. bug #3038387
const byte ecoquest1SignatureStayAndHelp [ ] = {
40 ,
0x3f , 0x01 , // link 01
0x87 , 0x01 , // lap param[1]
0x65 , 0x14 , // aTop state
0x36 , // push
0x3c , // dup
0x35 , 0x00 , // ldi 00
0x1a , // eq?
0x31 , 0x1c , // bnt [next state]
0x76 , // push0
0x45 , 0x01 , 0x00 , // callb export1 from script 0 (switching control off)
0x38 , 0x22 , 0x01 , // pushi 0122
0x78 , // push1
0x76 , // push0
0x81 , 0x00 , // lag global[0]
2010-08-24 22:24:25 +00:00
0x4a , 0x06 , // send 06 - call ego::setMotion(0)
2010-08-10 15:18:18 +00:00
0x39 , 0x6e , // pushi 6e (selector init)
0x39 , 0x04 , // pushi 04
0x76 , // push0
0x76 , // push0
0x39 , 0x17 , // pushi 17
0x7c , // pushSelf
0x51 , 0x82 , // class EcoNarrator
2010-08-24 22:24:25 +00:00
0x4a , 0x0c , // send 0c - call EcoNarrator::init(0, 0, 23, self) (BADLY BROKEN!)
2010-08-10 15:18:18 +00:00
0x33 , // jmp [end]
0
} ;
const uint16 ecoquest1PatchStayAndHelp [ ] = {
0x87 , 0x01 , // lap param[1]
0x65 , 0x14 , // aTop state
0x36 , // push
0x2f , 0x22 , // bt [next state] (this optimization saves 6 bytes)
0x39 , 0x00 , // pushi 0 (wasting 1 byte here)
0x45 , 0x01 , 0x00 , // callb export1 from script 0 (switching control off)
0x38 , 0x22 , 0x01 , // pushi 0122
0x78 , // push1
0x76 , // push0
0x81 , 0x00 , // lag global[0]
2010-08-24 22:24:25 +00:00
0x4a , 0x06 , // send 06 - call ego::setMotion(0)
2010-08-10 15:18:18 +00:00
0x39 , 0x6e , // pushi 6e (selector init)
0x39 , 0x06 , // pushi 06
0x39 , 0x02 , // pushi 02 (additional 2 bytes)
0x76 , // push0
0x76 , // push0
0x39 , 0x17 , // pushi 17
0x7c , // pushSelf
0x38 , 0x80 , 0x02 , // pushi 280 (additional 3 bytes)
0x51 , 0x82 , // class EcoNarrator
2010-08-24 22:24:25 +00:00
0x4a , 0x10 , // send 10 - call EcoNarrator::init(2, 0, 0, 23, self, 640)
2010-08-10 15:18:18 +00:00
PATCH_END
} ;
2010-09-06 11:58:28 +00:00
// script, description, magic DWORD, adjust
2010-08-10 15:18:18 +00:00
const SciScriptSignature ecoquest1Signatures [ ] = {
2010-10-23 15:46:50 +00:00
{ 660 , " CD: bad messagebox and freeze " , 1 , PATCH_MAGICDWORD ( 0x38 , 0x22 , 0x01 , 0x78 ) , - 17 , ecoquest1SignatureStayAndHelp , ecoquest1PatchStayAndHelp } ,
SCI_SIGNATUREENTRY_TERMINATOR
2010-08-10 15:18:18 +00:00
} ;
2010-08-06 22:36:46 +00:00
2010-08-16 15:22:43 +00:00
// ===========================================================================
// doMyThing::changeState (2) is supposed to remove the initial text on the
// ecorder. This is done by reusing temp-space, that was filled on state 1.
// this worked in sierra sci just by accident. In our sci, the temp space
// is resetted every time, which means the previous text isn't available
// anymore. We have to patch the code because of that ffs. bug #3035386
const byte ecoquest2SignatureEcorder [ ] = {
35 ,
0x31 , 0x22 , // bnt [next state]
0x39 , 0x0a , // pushi 0a
0x5b , 0x04 , 0x1e , // lea temp[1e]
0x36 , // push
0x39 , 0x64 , // pushi 64
0x39 , 0x7d , // pushi 7d
0x39 , 0x32 , // pushi 32
0x39 , 0x66 , // pushi 66
0x39 , 0x17 , // pushi 17
0x39 , 0x69 , // pushi 69
0x38 , 0x31 , 0x26 , // pushi 2631
0x39 , 0x6a , // pushi 6a
0x39 , 0x64 , // pushi 64
0x43 , 0x1b , 0x14 , // call kDisplay
0x35 , 0x0a , // ldi 0a
0x65 , 0x20 , // aTop ticks
0x33 , // jmp [end]
+ 1 , 5 , // [skip 1 byte]
0x3c , // dup
0x35 , 0x03 , // ldi 03
0x1a , // eq?
0x31 , // bnt [end]
0
} ;
const uint16 ecoquest2PatchEcorder [ ] = {
0x2f , 0x02 , // bt [to pushi 07]
0x3a , // toss
0x48 , // ret
0x38 , 0x07 , 0x00 , // pushi 07 (parameter count) (waste 1 byte)
0x39 , 0x0b , // push (FillBoxAny)
0x39 , 0x1d , // pushi 29d
0x39 , 0x73 , // pushi 115d
0x39 , 0x5e , // pushi 94d
0x38 , 0xd7 , 0x00 , // pushi 215d
0x78 , // push1 (visual screen)
0x38 , 0x17 , 0x00 , // pushi 17 (color) (waste 1 byte)
0x43 , 0x6c , 0x0e , // call kGraph
0x38 , 0x05 , 0x00 , // pushi 05 (parameter count) (waste 1 byte)
0x39 , 0x0c , // pushi 12d (UpdateBox)
0x39 , 0x1d , // pushi 29d
0x39 , 0x73 , // pushi 115d
0x39 , 0x5e , // pushi 94d
0x38 , 0xd7 , 0x00 , // pushi 215d
0x43 , 0x6c , 0x0a , // call kGraph
PATCH_END
} ;
2010-12-21 15:31:59 +00:00
// ===========================================================================
// Same patch as above for the ecorder introduction. Fixes bug #3092115.
// Two workarounds are needed for this patch in workarounds.cpp (when calling
// kGraphFillBoxAny and kGraphUpdateBox), as there isn't enough space to patch
// the function otherwise.
const byte ecoquest2SignatureEcorderTutorial [ ] = {
36 ,
0x30 , 0x23 , 0x00 , // bnt [next state]
0x39 , 0x0a , // pushi 0a
0x5b , 0x04 , 0x1f , // lea temp[1f]
0x36 , // push
0x39 , 0x64 , // pushi 64
0x39 , 0x7d , // pushi 7d
0x39 , 0x32 , // pushi 32
0x39 , 0x66 , // pushi 66
0x39 , 0x17 , // pushi 17
0x39 , 0x69 , // pushi 69
0x38 , 0x31 , 0x26 , // pushi 2631
0x39 , 0x6a , // pushi 6a
0x39 , 0x64 , // pushi 64
0x43 , 0x1b , 0x14 , // call kDisplay
0x35 , 0x1e , // ldi 1e
0x65 , 0x20 , // aTop ticks
0x32 , // jmp [end]
// 2 extra bytes, jmp offset
0
} ;
const uint16 ecoquest2PatchEcorderTutorial [ ] = {
0x31 , 0x23 , // bnt [next state] (save 1 byte)
2011-06-20 00:59:48 +02:00
// The parameter count below should be 7, but we're out of bytes
2010-12-21 15:31:59 +00:00
// to patch! A workaround has been added because of this
0x78 , // push1 (parameter count)
//0x39, 0x07, // pushi 07 (parameter count)
0x39 , 0x0b , // push (FillBoxAny)
0x39 , 0x1d , // pushi 29d
0x39 , 0x73 , // pushi 115d
0x39 , 0x5e , // pushi 94d
0x38 , 0xd7 , 0x00 , // pushi 215d
0x78 , // push1 (visual screen)
0x39 , 0x17 , // pushi 17 (color)
0x43 , 0x6c , 0x0e , // call kGraph
2011-06-20 00:59:48 +02:00
// The parameter count below should be 5, but we're out of bytes
2010-12-21 15:31:59 +00:00
// to patch! A workaround has been added because of this
0x78 , // push1 (parameter count)
//0x39, 0x05, // pushi 05 (parameter count)
0x39 , 0x0c , // pushi 12d (UpdateBox)
0x39 , 0x1d , // pushi 29d
0x39 , 0x73 , // pushi 115d
0x39 , 0x5e , // pushi 94d
0x38 , 0xd7 , 0x00 , // pushi 215d
0x43 , 0x6c , 0x0a , // call kGraph
// We are out of bytes to patch at this point,
// so we skip 494 (0x1EE) bytes to reuse this code:
// ldi 1e
// aTop 20
// jmp 030e (jump to end)
0x32 , 0xee , 0x01 , // skip 494 (0x1EE) bytes
PATCH_END
} ;
2010-09-06 11:58:28 +00:00
// script, description, magic DWORD, adjust
2010-08-16 15:22:43 +00:00
const SciScriptSignature ecoquest2Signatures [ ] = {
2010-10-23 15:46:50 +00:00
{ 50 , " initial text not removed on ecorder " , 1 , PATCH_MAGICDWORD ( 0x39 , 0x64 , 0x39 , 0x7d ) , - 8 , ecoquest2SignatureEcorder , ecoquest2PatchEcorder } ,
2010-12-21 15:31:59 +00:00
{ 333 , " initial text not removed on ecorder tutorial " , 1 , PATCH_MAGICDWORD ( 0x39 , 0x64 , 0x39 , 0x7d ) , - 9 , ecoquest2SignatureEcorderTutorial , ecoquest2PatchEcorderTutorial } ,
2010-10-23 15:46:50 +00:00
SCI_SIGNATUREENTRY_TERMINATOR
2010-08-16 15:22:43 +00:00
} ;
2010-12-28 21:05:30 +00:00
// ===========================================================================
// 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 #3038870.
const byte fanmadeSignatureInfiniteLoop [ ] = {
13 ,
0x38 , 0x4c , 0x00 , // pushi 004c
0x39 , 0x00 , // pushi 00
0x87 , 0x01 , // lap 01
0x4b , 0x04 , // send 04
0x18 , // not
0x30 , 0x2f , 0x00 , // bnt 002f [06a5] --> jmp ffbc [0664] --> BUG! infinite loop
0
} ;
const uint16 fanmadePatchInfiniteLoop [ ] = {
PATCH_ADDTOOFFSET | + 10 ,
0x30 , 0x32 , 0x00 , // bnt 0032 [06a8] --> pushi 004c
PATCH_END
} ;
// script, description, magic DWORD, adjust
const SciScriptSignature fanmadeSignatures [ ] = {
{ 999 , " infinite loop on typo " , 1 , PATCH_MAGICDWORD ( 0x18 , 0x30 , 0x2f , 0x00 ) , - 9 , fanmadeSignatureInfiniteLoop , fanmadePatchInfiniteLoop } ,
SCI_SIGNATUREENTRY_TERMINATOR
} ;
2010-08-24 22:24:25 +00:00
// ===========================================================================
2010-09-05 20:04:46 +00:00
// 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
// sound disposal. This somewhat worked in sierra sci, because the sample
// was already playing in the sound driver. In our case we would also stop
// the sample from playing, so we patch it out
// The "score" code is already buggy and sets volume to 0 when playing
const byte freddypharkasSignatureScoreDisposal [ ] = {
10 ,
0x67 , 0x32 , // pTos 32 (selector theAudCount)
0x78 , // push1
0x39 , 0x0d , // pushi 0d
0x43 , 0x75 , 0x02 , // call kDoAudio
0x1c , // ne?
0x31 , // bnt (-> to skip disposal)
0
} ;
const uint16 freddypharkasPatchScoreDisposal [ ] = {
0x34 , 0x00 , 0x00 , // ldi 0000
0x34 , 0x00 , 0x00 , // ldi 0000
0x34 , 0x00 , 0x00 , // ldi 0000
PATCH_END
} ;
2010-09-06 11:58:28 +00:00
// script 235 of freddy pharkas rm235::init and sEnterFrom500::changeState
2011-02-21 14:32:21 -05:00
// disable icon 7+8 of iconbar (CD only). When picking up the canister after
2010-09-06 11:58:28 +00:00
// placing it down, the scripts will disable all the other icons. This results
// in IconBar::disable doing endless loops even in sierra sci, because there
// is no enabled icon left. We remove disabling of icon 8 (which is help),
// this fixes the issue.
2011-02-21 14:32:21 -05:00
const byte freddypharkasSignatureCanisterHang [ ] = {
2010-09-06 11:58:28 +00:00
12 ,
0x38 , 0xf1 , 0x00 , // pushi f1 (selector disable)
0x7a , // push2
0x39 , 0x07 , // pushi 07
0x39 , 0x08 , // pushi 08
0x81 , 0x45 , // lag 45
0x4a , 0x08 , // send 08 - call IconBar::disable(7, 8)
0
} ;
2011-02-21 14:32:21 -05:00
const uint16 freddypharkasPatchCanisterHang [ ] = {
2010-09-06 11:58:28 +00:00
PATCH_ADDTOOFFSET | + 3 ,
0x78 , // push1
PATCH_ADDTOOFFSET | + 2 ,
0x33 , 0x00 , // ldi 00 (waste 2 bytes)
PATCH_ADDTOOFFSET | + 3 ,
0x06 , // send 06 - call IconBar::disable(7)
PATCH_END
} ;
2010-08-24 22:24:25 +00:00
// script 215 of freddy pharkas lowerLadder::doit and highLadder::doit actually
// process keyboard-presses when the ladder is on the screen in that room.
// They strangely also call kGetEvent. Because the main User::doit also calls
// kGetEvent, it's pure luck, where the event will hit. It's the same issue
// as in QfG1VGA and if you turn dos-box to max cycles, and click around for
// ego, sometimes clicks also won't get registered. Strangely it's not nearly
// as bad as in our sci, but these differences may be caused by timing.
// We just reuse the active event, thus removing the duplicate kGetEvent call.
const byte freddypharkasSignatureLadderEvent [ ] = {
21 ,
0x39 , 0x6d , // pushi 6d (selector new)
0x76 , // push0
0x38 , 0xf5 , 0x00 , // pushi f5 (selector curEvent)
0x76 , // push0
0x81 , 0x50 , // lag global[50]
0x4a , 0x04 , // send 04 - read User::curEvent
0x4a , 0x04 , // send 04 - call curEvent::new
0xa5 , 0x00 , // sat temp[0]
0x38 , 0x94 , 0x00 , // pushi 94 (selector localize)
0x76 , // push0
0x4a , 0x04 , // send 04 - call curEvent::localize
0
} ;
const uint16 freddypharkasPatchLadderEvent [ ] = {
0x34 , 0x00 , 0x00 , // ldi 0000 (waste 3 bytes, overwrites first 2 pushes)
PATCH_ADDTOOFFSET | + 8 ,
0xa5 , 0x00 , // sat temp[0] (waste 2 bytes, overwrites 2nd send)
PATCH_ADDTOOFFSET | + 2 ,
0x34 , 0x00 , 0x00 , // ldi 0000
0x34 , 0x00 , 0x00 , // ldi 0000 (waste 6 bytes, overwrites last 3 opcodes)
PATCH_END
} ;
2010-09-06 11:58:28 +00:00
// script, description, magic DWORD, adjust
2010-08-24 22:24:25 +00:00
const SciScriptSignature freddypharkasSignatures [ ] = {
2010-10-23 15:46:50 +00:00
{ 0 , " CD: score early disposal " , 1 , PATCH_MAGICDWORD ( 0x39 , 0x0d , 0x43 , 0x75 ) , - 3 , freddypharkasSignatureScoreDisposal , freddypharkasPatchScoreDisposal } ,
2012-01-14 21:01:48 +02:00
{ 235 , " CD: canister pickup hang " , 3 , PATCH_MAGICDWORD ( 0x39 , 0x07 , 0x39 , 0x08 ) , - 4 , freddypharkasSignatureCanisterHang , freddypharkasPatchCanisterHang } ,
2010-10-23 15:46:50 +00:00
{ 320 , " ladder event issue " , 2 , PATCH_MAGICDWORD ( 0x6d , 0x76 , 0x38 , 0xf5 ) , - 1 , freddypharkasSignatureLadderEvent , freddypharkasPatchLadderEvent } ,
SCI_SIGNATUREENTRY_TERMINATOR
2010-08-24 22:24:25 +00:00
} ;
// ===========================================================================
2010-08-09 21:43:31 +00:00
// daySixBeignet::changeState (4) is called when the cop goes out and sets cycles to 220.
2010-08-09 18:42:07 +00:00
// this is not enough time to get to the door, so we patch that to 23 seconds
const byte gk1SignatureDay6PoliceBeignet [ ] = {
4 ,
0x35 , 0x04 , // ldi 04
0x1a , // eq?
0x30 , // bnt [next state check]
+ 2 , 5 , // [skip 2 bytes, offset of bnt]
0x38 , 0x93 , 0x00 , // pushi 93 (selector dispose)
0x76 , // push0
0x72 , // lofsa deskSarg
+ 2 , 9 , // [skip 2 bytes, offset of lofsa]
0x4a , 0x04 , 0x00 , // send 04
0x34 , 0xdc , 0x00 , // ldi 220
0x65 , 0x1a , // aTop cycles
0x32 , // jmp [end]
0
} ;
const uint16 gk1PatchDay6PoliceBeignet [ ] = {
PATCH_ADDTOOFFSET | + 16 ,
0x34 , 0x17 , 0x00 , // ldi 23
0x65 , 0x1c , // aTop seconds
PATCH_END
} ;
2010-08-09 21:43:31 +00:00
// sargSleeping::changeState (8) is called when the cop falls asleep and sets cycles to 220.
// this is not enough time to get to the door, so we patch it to 42 seconds
2010-08-09 18:42:07 +00:00
const byte gk1SignatureDay6PoliceSleep [ ] = {
4 ,
0x35 , 0x08 , // ldi 08
0x1a , // eq?
0x31 , // bnt [next state check]
2010-08-09 21:50:21 +00:00
+ 1 , 6 , // [skip 1 byte, offset of bnt]
2010-08-09 18:42:07 +00:00
0x34 , 0xdc , 0x00 , // ldi 220
0x65 , 0x1a , // aTop cycles
0x32 , // jmp [end]
0
} ;
const uint16 gk1PatchDay6PoliceSleep [ ] = {
PATCH_ADDTOOFFSET | + 5 ,
0x34 , 0x2a , 0x00 , // ldi 42
0x65 , 0x1c , // aTop seconds
PATCH_END
} ;
2010-08-09 21:43:31 +00:00
// startOfDay5::changeState (20h) - when gabriel goes to the phone the script will hang
const byte gk1SignatureDay5PhoneFreeze [ ] = {
5 ,
0x35 , 0x03 , // ldi 03
0x65 , 0x1a , // aTop cycles
0x32 , // jmp [end]
+ 2 , 3 , // [skip 2 bytes, offset of jmp]
0x3c , // dup
0x35 , 0x21 , // ldi 21
0
} ;
const uint16 gk1PatchDay5PhoneFreeze [ ] = {
0x35 , 0x06 , // ldi 06
0x65 , 0x20 , // aTop ticks
PATCH_END
} ;
2012-01-14 21:01:48 +02:00
// Floppy version: Interrogation::dispose() compares an object reference
// (stored in the view selector) with a number, leading to a crash (this kind
// of comparison was not used in SCI32). The view selector is used to store
// both a view number (in some cases), and a view reference (in other cases).
// In the floppy version, the checks are in the wrong order, so there is a
// comparison between a number an an object. In the CD version, the checks are
// in the correct order, thus the comparison is correct, thus we use the code
// from the CD version in the floppy one.
const byte gk1SignatureInterrogationBug [ ] = {
43 ,
0x65 , 0x4c , // aTop 4c
0x67 , 0x50 , // pTos 50
0x34 , 0x10 , 0x27 , // ldi 2710
0x1e , // gt?
0x31 , 0x08 , // bnt 08 [05a0]
0x67 , 0x50 , // pTos 50
0x34 , 0x10 , 0x27 , // ldi 2710
0x04 , // sub
0x65 , 0x50 , // aTop 50
0x63 , 0x50 , // pToa 50
0x31 , 0x15 , // bnt 15 [05b9]
0x39 , 0x0e , // pushi 0e
0x76 , // push0
0x4a , 0x04 , 0x00 , // send 0004
0xa5 , 0x00 , // sat 00
0x38 , 0x93 , 0x00 , // pushi 0093
0x76 , // push0
0x63 , 0x50 , // pToa 50
0x4a , 0x04 , 0x00 , // send 0004
0x85 , 0x00 , // lat 00
0x65 , 0x50 , // aTop 50
0
} ;
const uint16 gk1PatchInterrogationBug [ ] = {
0x65 , 0x4c , // aTop 4c
0x63 , 0x50 , // pToa 50
0x31 , 0x15 , // bnt 15 [05b9]
0x39 , 0x0e , // pushi 0e
0x76 , // push0
0x4a , 0x04 , 0x00 , // send 0004
0xa5 , 0x00 , // sat 00
0x38 , 0x93 , 0x00 , // pushi 0093
0x76 , // push0
0x63 , 0x50 , // pToa 50
0x4a , 0x04 , 0x00 , // send 0004
0x85 , 0x00 , // lat 00
0x65 , 0x50 , // aTop 50
0x67 , 0x50 , // pTos 50
0x34 , 0x10 , 0x27 , // ldi 2710
0x1e , // gt?
0x31 , 0x08 , // bnt 08 [05b9]
0x67 , 0x50 , // pTos 50
0x34 , 0x10 , 0x27 , // ldi 2710
0x04 , // sub
0x65 , 0x50 , // aTop 50
PATCH_END
} ;
2010-09-06 11:58:28 +00:00
// script, description, magic DWORD, adjust
2010-08-09 18:42:07 +00:00
const SciScriptSignature gk1Signatures [ ] = {
2012-01-14 21:01:48 +02:00
{ 51 , " interrogation bug " , 1 , PATCH_MAGICDWORD ( 0x65 , 0x4c , 0x67 , 0x50 ) , 0 , gk1SignatureInterrogationBug , gk1PatchInterrogationBug } ,
2010-10-23 15:46:50 +00:00
{ 212 , " day 5 phone freeze " , 1 , PATCH_MAGICDWORD ( 0x35 , 0x03 , 0x65 , 0x1a ) , 0 , gk1SignatureDay5PhoneFreeze , gk1PatchDay5PhoneFreeze } ,
{ 230 , " day 6 police beignet timer issue " , 1 , PATCH_MAGICDWORD ( 0x34 , 0xdc , 0x00 , 0x65 ) , - 16 , gk1SignatureDay6PoliceBeignet , gk1PatchDay6PoliceBeignet } ,
{ 230 , " day 6 police sleep timer issue " , 1 , PATCH_MAGICDWORD ( 0x34 , 0xdc , 0x00 , 0x65 ) , - 5 , gk1SignatureDay6PoliceSleep , gk1PatchDay6PoliceSleep } ,
SCI_SIGNATUREENTRY_TERMINATOR
2010-08-09 18:42:07 +00:00
} ;
2010-08-11 20:39:13 +00:00
// ===========================================================================
// at least during harpy scene export 29 of script 0 is called in kq5cd and
// has an issue for those calls, where temp 3 won't get inititialized, but
// is later used to set master volume. This issue makes sierra sci set
// the volume to max. We fix the export, so volume won't get modified in
// those cases.
const byte kq5SignatureCdHarpyVolume [ ] = {
34 ,
0x80 , 0x91 , 0x01 , // lag global[191h]
0x18 , // not
0x30 , 0x2c , 0x00 , // bnt [jump further] (jumping, if global 191h is 1)
0x35 , 0x01 , // ldi 01
0xa0 , 0x91 , 0x01 , // sag global[191h] (setting global 191h to 1)
0x38 , 0x7b , 0x01 , // pushi 017b
0x76 , // push0
0x81 , 0x01 , // lag global[1]
2010-08-24 22:24:25 +00:00
0x4a , 0x04 , // send 04 - read KQ5::masterVolume
2010-08-11 20:39:13 +00:00
0xa5 , 0x03 , // sat temp[3] (store volume in temp 3)
0x38 , 0x7b , 0x01 , // pushi 017b
0x76 , // push0
0x81 , 0x01 , // lag global[1]
2010-08-24 22:24:25 +00:00
0x4a , 0x04 , // send 04 - read KQ5::masterVolume
2010-08-11 20:39:13 +00:00
0x36 , // push
0x35 , 0x04 , // ldi 04
0x20 , // ge? (followed by bnt)
0
} ;
const uint16 kq5PatchCdHarpyVolume [ ] = {
0x38 , 0x2f , 0x02 , // pushi 022f (selector theVol) (3 new bytes)
0x76 , // push0 (1 new byte)
0x51 , 0x88 , // class SpeakTimer (2 new bytes)
0x4a , 0x04 , // send 04 (2 new bytes) -> read SpeakTimer::theVol
0xa5 , 0x03 , // sat temp[3] (2 new bytes) -> write to temp 3
0x80 , 0x91 , 0x01 , // lag global[191h]
// saving 1 byte due optimization
0x2e , 0x23 , 0x00 , // bt [jump further] (jumping, if global 191h is 1)
0x35 , 0x01 , // ldi 01
0xa0 , 0x91 , 0x01 , // sag global[191h] (setting global 191h to 1)
0x38 , 0x7b , 0x01 , // pushi 017b
0x76 , // push0
0x81 , 0x01 , // lag global[1]
2010-08-24 22:24:25 +00:00
0x4a , 0x04 , // send 04 - read KQ5::masterVolume
2010-08-11 20:39:13 +00:00
0xa5 , 0x03 , // sat temp[3] (store volume in temp 3)
// saving 8 bytes due removing of duplicate code
0x39 , 0x04 , // pushi 04 (saving 1 byte due swapping)
0x22 , // lt? (because we switched values)
PATCH_END
} ;
2011-03-11 18:05:13 +02:00
// This is a heap patch, and it modifies the properties of an object, instead
// of patching script code.
2011-03-11 23:09:13 +01:00
//
// The witchCage object in script 200 is broken and claims to have 12
// variables instead of the 8 it should have because it is a Cage.
// Additionally its top,left,bottom,right properties are set to 0 rather
// than the right values. We fix the object by setting the right values.
// If they are all zero, this causes an impossible position check in
// witch::cantBeHere and an infinite loop when entering room 22 (bug #3034714).
//
// This bug is accidentally not triggered in SSCI because the invalid number
// of variables effectively hides witchCage::doit, causing this position check
// to be bypassed entirely.
// See also the warning+comment in Object::initBaseObject
2011-03-11 18:05:13 +02:00
const byte kq5SignatureWitchCageInit [ ] = {
16 ,
0x00 , 0x00 , // top
0x00 , 0x00 , // left
0x00 , 0x00 , // bottom
0x00 , 0x00 , // right
0x00 , 0x00 , // extra property #1
0x7a , 0x00 , // extra property #2
0xc8 , 0x00 , // extra property #3
0xa3 , 0x00 , // extra property #4
0
} ;
const uint16 kq5PatchWitchCageInit [ ] = {
0x00 , 0x00 , // top
0x7a , 0x00 , // left
0xc8 , 0x00 , // bottom
0xa3 , 0x00 , // right
PATCH_END
} ;
2010-09-06 11:58:28 +00:00
// script, description, magic DWORD, adjust
2010-08-11 20:39:13 +00:00
const SciScriptSignature kq5Signatures [ ] = {
2010-10-23 15:46:50 +00:00
{ 0 , " CD: harpy volume change " , 1 , PATCH_MAGICDWORD ( 0x80 , 0x91 , 0x01 , 0x18 ) , 0 , kq5SignatureCdHarpyVolume , kq5PatchCdHarpyVolume } ,
2011-03-11 18:05:13 +02:00
{ 200 , " CD: witch cage init " , 1 , PATCH_MAGICDWORD ( 0x7a , 0x00 , 0xc8 , 0x00 ) , - 10 , kq5SignatureWitchCageInit , kq5PatchWitchCageInit } ,
2010-10-23 15:46:50 +00:00
SCI_SIGNATUREENTRY_TERMINATOR
2010-08-11 20:39:13 +00:00
} ;
2010-08-06 22:36:46 +00:00
2011-02-24 00:10:01 +02:00
// ===========================================================================
// When giving the milk bottle to one of the babies in the garden in KQ6 (room
// 480), script 481 starts a looping baby cry sound. However, that particular
// script also has an overriden check method (cryMusic::check). This method
// explicitly restarts the sound, even if it's set to be looped, thus the same
// sound is played twice, squelching all other sounds. We just rip the
// unnecessary cryMusic::check method out, thereby stopping the sound from
// constantly restarting (since it's being looped anyway), thus the normal
// game speech can work while the baby cry sound is heard. Fixes bug #3034579.
const byte kq6SignatureDuplicateBabyCry [ ] = {
10 ,
0x83 , 0x00 , // lal 00
0x31 , 0x1e , // bnt 1e [07f4]
0x78 , // push1
0x39 , 0x04 , // pushi 04
0x43 , 0x75 , 0x02 , // callk DoAudio[75] 02
0
} ;
const uint16 kq6PatchDuplicateBabyCry [ ] = {
0x48 , // ret
PATCH_END
} ;
// script, description, magic DWORD, adjust
const SciScriptSignature kq6Signatures [ ] = {
{ 481 , " duplicate baby cry " , 1 , PATCH_MAGICDWORD ( 0x83 , 0x00 , 0x31 , 0x1e ) , 0 , kq6SignatureDuplicateBabyCry , kq6PatchDuplicateBabyCry } ,
SCI_SIGNATUREENTRY_TERMINATOR
} ;
2011-03-12 23:48:56 +02:00
// ===========================================================================
// 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),
// and the function that is used to show each letter is called twice. The
// second time that the function is called, the second parameter passed to
// the function is undefined, thus kStrCat() that is called inside the function
// reads a random pointer and crashes. We patch all of the 5 function calls
// (one for each letter typed from "R", "O", "B", "I", "N") so that they are
// the same as the English version. Fixes bug #3048054.
const byte longbowSignatureShowHandCode [ ] = {
3 ,
0x78 , // push1
0x78 , // push1
0x72 , // lofsa
+ 2 , 2 , // skip 2 bytes, offset of lofsa (the letter typed)
0x36 , // push
0x40 , // call
+ 2 , 3 , // skip 2 bytes, offset of call
0x02 , // perform the call above with 2 parameters
0x36 , // push
0x40 , // call
+ 2 , 8 , // skip 2 bytes, offset of call
0x02 , // perform the call above with 2 parameters
0x38 , 0x1c , 0x01 , // pushi 011c (setMotion)
0x39 , 0x04 , // pushi 04 (x)
0x51 , 0x1e , // class MoveTo
0
} ;
const uint16 longbowPatchShowHandCode [ ] = {
0x39 , 0x01 , // pushi 1 (combine the two push1's in one, like in the English version)
PATCH_ADDTOOFFSET | + 3 , // leave the lofsa call untouched
// The following will remove the duplicate call
0x32 , 0x02 , 0x00 , // jmp 02 - skip 2 bytes (the remainder of the first call)
0x48 , // ret (dummy, should never be reached)
0x48 , // ret (dummy, should never be reached)
PATCH_END
} ;
// script, description, magic DWORD, adjust
const SciScriptSignature longbowSignatures [ ] = {
{ 210 , " hand code crash " , 5 , PATCH_MAGICDWORD ( 0x02 , 0x38 , 0x1c , 0x01 ) , - 14 , longbowSignatureShowHandCode , longbowPatchShowHandCode } ,
SCI_SIGNATUREENTRY_TERMINATOR
} ;
2010-08-11 20:39:13 +00:00
// ===========================================================================
2010-08-06 22:36:46 +00:00
// 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
// This patch moves the title text around, so this overflow
// doesn't happen anymore. We would otherwise get a crash
// calling for invalid views (this happens of course also
// in sierra sci)
const byte larry6SignatureDeathDialog [ ] = {
7 ,
0x3e , 0x33 , 0x01 , // link 0133 (offset 0x20)
0x35 , 0xff , // ldi ff
0xa3 , 0x00 , // sal 00
+ 255 , 0 ,
+ 255 , 0 ,
+ 170 , 12 , // [skip 680 bytes]
0x8f , 0x01 , // lsp 01 (offset 0x2cf)
0x7a , // push2
0x5a , 0x04 , 0x00 , 0x0e , 0x01 , // lea 0004 010e
0x36 , // push
0x43 , 0x7c , 0x0e , // kMessage[7c] 0e
+ 90 , 10 , // [skip 90 bytes]
0x38 , 0xd6 , 0x00 , // pushi 00d6 (offset 0x335)
0x78 , // push1
0x5a , 0x04 , 0x00 , 0x0e , 0x01 , // lea 0004 010e
0x36 , // push
+ 76 , 11 , // [skip 76 bytes]
0x38 , 0xcd , 0x00 , // pushi 00cd (offset 0x38b)
0x39 , 0x03 , // pushi 03
0x5a , 0x04 , 0x00 , 0x0e , 0x01 , // lea 0004 010e
0x36 ,
0
} ;
const uint16 larry6PatchDeathDialog [ ] = {
0x3e , 0x00 , 0x02 , // link 0200
PATCH_ADDTOOFFSET | + 687 ,
0x5a , 0x04 , 0x00 , 0x40 , 0x01 , // lea 0004 0140
PATCH_ADDTOOFFSET | + 98 ,
0x5a , 0x04 , 0x00 , 0x40 , 0x01 , // lea 0004 0140
PATCH_ADDTOOFFSET | + 82 ,
0x5a , 0x04 , 0x00 , 0x40 , 0x01 , // lea 0004 0140
PATCH_END
} ;
2010-09-06 11:58:28 +00:00
// script, description, magic DWORD, adjust
2010-08-06 22:36:46 +00:00
const SciScriptSignature larry6Signatures [ ] = {
2010-10-23 15:46:50 +00:00
{ 82 , " death dialog memory corruption " , 1 , PATCH_MAGICDWORD ( 0x3e , 0x33 , 0x01 , 0x35 ) , 0 , larry6SignatureDeathDialog , larry6PatchDeathDialog } ,
SCI_SIGNATUREENTRY_TERMINATOR
2010-08-06 22:36:46 +00:00
} ;
2010-08-12 13:45:39 +00:00
// ===========================================================================
2011-02-28 22:33:45 +02:00
// rm560::doit was supposed to close the painting, when Heimlich enters the
2010-08-12 13:45:39 +00:00
// room. The code is buggy, so it actually closes the painting, when heimlich
2010-08-12 14:01:28 +00:00
// is not in the room. We fix that.
2010-08-12 13:45:39 +00:00
const byte laurabow2SignaturePaintingClosing [ ] = {
2010-08-12 14:01:28 +00:00
17 ,
2010-08-24 22:24:25 +00:00
0x4a , 0x04 , // send 04 - read aHeimlich::room
2010-08-12 13:45:39 +00:00
0x36 , // push
0x81 , 0x0b , // lag global[11d] -> current room
0x1c , // ne?
0x31 , 0x0e , // bnt [don't close]
0x35 , 0x00 , // ldi 00
0xa3 , 0x00 , // sal local[0]
0x38 , 0x92 , 0x00 , // pushi 0092
0x78 , // push1
2010-08-12 14:01:28 +00:00
0x72 , // lofsa sDumpSafe
2010-08-12 13:45:39 +00:00
0
} ;
const uint16 laurabow2PatchPaintingClosing [ ] = {
PATCH_ADDTOOFFSET | + 6 ,
0x2f , 0x0e , // bt [don't close]
PATCH_END
} ;
2010-09-06 11:58:28 +00:00
// script, description, magic DWORD, adjust
2010-08-12 13:45:39 +00:00
const SciScriptSignature laurabow2Signatures [ ] = {
2010-10-23 15:46:50 +00:00
{ 560 , " painting closing immediately " , 1 , PATCH_MAGICDWORD ( 0x36 , 0x81 , 0x0b , 0x1c ) , - 2 , laurabow2SignaturePaintingClosing , laurabow2PatchPaintingClosing } ,
SCI_SIGNATUREENTRY_TERMINATOR
2010-08-12 13:45:39 +00:00
} ;
2010-08-23 16:33:19 +00:00
// ===========================================================================
// Mother Goose SCI1/SCI1.1
2011-06-20 00:59:48 +02:00
// MG::replay somewhat calculates the savedgame-id used when saving again
2010-08-23 16:33:19 +00:00
// this doesn't work right and we remove the code completely.
// We set the savedgame-id directly right after restoring in kRestoreGame.
const byte mothergoose256SignatureReplay [ ] = {
6 ,
0x36 , // push
0x35 , 0x20 , // ldi 20
0x04 , // sub
0xa1 , 0xb3 , // sag global[b3]
0
} ;
const uint16 mothergoose256PatchReplay [ ] = {
0x34 , 0x00 , 0x00 , // ldi 0000 (dummy)
0x34 , 0x00 , 0x00 , // ldi 0000 (dummy)
PATCH_END
} ;
2012-05-14 11:14:10 +03:00
// when saving, it also checks if the savegame ID is below 13.
2010-08-23 16:33:19 +00:00
// we change this to check if below 113 instead
const byte mothergoose256SignatureSaveLimit [ ] = {
5 ,
0x89 , 0xb3 , // lsg global[b3]
0x35 , 0x0d , // ldi 0d
0x20 , // ge?
0
} ;
const uint16 mothergoose256PatchSaveLimit [ ] = {
PATCH_ADDTOOFFSET | + 2 ,
0x35 , 0x0d + SAVEGAMEID_OFFICIALRANGE_START , // ldi 113d
PATCH_END
} ;
2010-09-06 11:58:28 +00:00
// script, description, magic DWORD, adjust
2010-08-23 16:33:19 +00:00
const SciScriptSignature mothergoose256Signatures [ ] = {
2010-10-23 15:46:50 +00:00
{ 0 , " replay save issue " , 1 , PATCH_MAGICDWORD ( 0x20 , 0x04 , 0xa1 , 0xb3 ) , - 2 , mothergoose256SignatureReplay , mothergoose256PatchReplay } ,
{ 0 , " save limit dialog (SCI1.1) " , 1 , PATCH_MAGICDWORD ( 0xb3 , 0x35 , 0x0d , 0x20 ) , - 1 , mothergoose256SignatureSaveLimit , mothergoose256PatchSaveLimit } ,
{ 994 , " save limit dialog (SCI1) " , 1 , PATCH_MAGICDWORD ( 0xb3 , 0x35 , 0x0d , 0x20 ) , - 1 , mothergoose256SignatureSaveLimit , mothergoose256PatchSaveLimit } ,
SCI_SIGNATUREENTRY_TERMINATOR
2010-08-24 21:53:06 +00:00
} ;
// ===========================================================================
// 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
// will hit. It's the same issue as in freddy pharkas and if you turn dos-box
// to max cycles, sometimes clicks also won't get registered. Strangely it's
// not nearly as bad as in our sci, but these differences may be caused by
// timing.
// We just reuse the active event, thus removing the duplicate kGetEvent call.
const byte qfg1vgaSignatureFightEvents [ ] = {
25 ,
0x39 , 0x6d , // pushi 6d (selector new)
0x76 , // push0
0x51 , 0x07 , // class Event
2010-08-24 22:24:25 +00:00
0x4a , 0x04 , // send 04 - call Event::new
2010-08-24 21:53:06 +00:00
0xa5 , 0x00 , // sat temp[0]
0x78 , // push1
0x76 , // push0
2010-08-24 22:24:25 +00:00
0x4a , 0x04 , // send 04 - read Event::x
2010-08-24 21:53:06 +00:00
0xa5 , 0x03 , // sat temp[3]
0x76 , // push0 (selector y)
0x76 , // push0
0x85 , 0x00 , // lat temp[0]
2010-08-24 22:24:25 +00:00
0x4a , 0x04 , // send 04 - read Event::y
2010-08-24 21:53:06 +00:00
0x36 , // push
0x35 , 0x0a , // ldi 0a
0x04 , // sub (poor mans localization) ;-)
0
} ;
const uint16 qfg1vgaPatchFightEvents [ ] = {
0x38 , 0x5a , 0x01 , // pushi 15a (selector curEvent)
0x76 , // push0
0x81 , 0x50 , // lag global[50]
2010-08-24 22:24:25 +00:00
0x4a , 0x04 , // send 04 - read User::curEvent -> needs one byte more than previous code
2010-08-24 21:53:06 +00:00
0xa5 , 0x00 , // sat temp[0]
0x78 , // push1
0x76 , // push0
2010-08-24 22:24:25 +00:00
0x4a , 0x04 , // send 04 - read Event::x
2010-08-24 21:53:06 +00:00
0xa5 , 0x03 , // sat temp[3]
0x76 , // push0 (selector y)
0x76 , // push0
0x85 , 0x00 , // lat temp[0]
2010-08-24 22:24:25 +00:00
0x4a , 0x04 , // send 04 - read Event::y
2010-08-24 21:53:06 +00:00
0x39 , 0x00 , // pushi 00
0x02 , // add (waste 3 bytes) - we don't need localization, User::doit has already done it
PATCH_END
} ;
2012-10-13 19:43:40 +03:00
// Script 814 of QFG1VGA is responsible for showing dialogs. However, the death
// screen message shown when the hero dies in room 64 (ghost room) is too large
// (254 chars long). Since the window header and main text are both stored in
// temp space, this is an issue, as the scripts read the window header, then the
// window text, which erases the window header text because of its length. To
// fix that, we allocate more temp space and move the pointer used for the
// window header a little bit, wherever it's used in script 814.
// Fixes bug #3568431.
// Patch 1: Increase temp space
const byte qfg1vgaSignatureTempSpace [ ] = {
4 ,
0x3f , 0xba , // link 0xba
0x87 , 0x00 , // lap 0
0
} ;
const uint16 qfg1vgaPatchTempSpace [ ] = {
0x3f , 0xca , // link 0xca
PATCH_END
} ;
// Patch 2: Move the pointer used for the window header a little bit
const byte qfg1vgaSignatureDialogHeader [ ] = {
4 ,
0x5b , 0x04 , 0x80 , // lea temp[0x80]
0x36 , // push
0
} ;
const uint16 qfg1vgaPatchDialogHeader [ ] = {
0x5b , 0x04 , 0x90 , // lea temp[0x90]
PATCH_END
} ;
2013-01-24 23:22:31 +02:00
// When clicking on the crusher in room 331, Ego approaches him to talk to him,
// an action that is handled by moveToCrusher::changeState in script 331. The
// scripts set Ego to move close to the crusher, but when Ego is running instead
// of walking, the target coordinates specified by script 331 are never reached,
// as Ego is making larger steps, and never reaches the required spot. This is an
// edge case that can occur when Ego is set to run. Normally, when clicking on
// the crusher, ego is supposed to move close to position 79, 165. We change it
// to 85, 165, which is not an edge case thus the freeze is avoided.
// Fixes bug #3585189.
2013-01-24 22:21:29 +02:00
const byte qfg1vgaSignatureMoveToCrusher [ ] = {
9 ,
0x51 , 0x1f , // class Motion
0x36 , // push
0x39 , 0x4f , // pushi 4f (79 - x)
0x38 , 0xa5 , 0x00 , // pushi 00a5 (165 - y)
0x7c , // pushSelf
0
} ;
const uint16 qfg1vgaPatchMoveToCrusher [ ] = {
PATCH_ADDTOOFFSET | + 3 ,
0x39 , 0x55 , // pushi 55 (85 - x)
PATCH_END
} ;
2010-09-06 11:58:28 +00:00
// script, description, magic DWORD, adjust
2010-08-24 21:53:06 +00:00
const SciScriptSignature qfg1vgaSignatures [ ] = {
2010-11-30 00:26:00 +00:00
{ 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 } ,
2012-10-13 19:43:40 +03:00
{ 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 } ,
2013-01-24 22:21:29 +02:00
{ 331 , " moving to crusher " , 1 , PATCH_MAGICDWORD ( 0x51 , 0x1f , 0x36 , 0x39 ) , 0 , qfg1vgaSignatureMoveToCrusher , qfg1vgaPatchMoveToCrusher } ,
2010-10-23 15:46:50 +00:00
SCI_SIGNATUREENTRY_TERMINATOR
2010-08-23 16:33:19 +00:00
} ;
2010-11-21 23:43:14 +00:00
// ===========================================================================
// 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
// probably done because it would be easier to refresh the list whenever the
// user inserted a new floppy disk, or changed directory. The problem is that
// the script has a bug, and it invalidates the text of the entries in the
// list. This has a high probability of breaking, as the user could change the
// list very quickly, or the garbage collector could kick in and remove the
// deleted entries. We don't allow the user to change the directory, thus the
// contents of the file list are constant, so we can avoid the constant file
// and text entry refreshes whenever a button is pressed, and prevent possible
// crashes because of these constant quick object reallocations. Fixes bug
// #3037996.
const byte qfg2SignatureImportDialog [ ] = {
16 ,
0x63 , 0x20 , // pToa text
0x30 , 0x0b , 0x00 , // bnt [next state]
0x7a , // push2
0x39 , 0x03 , // pushi 03
0x36 , // push
0x43 , 0x72 , 0x04 , // callk Memory 4
0x35 , 0x00 , // ldi 00
0x65 , 0x20 , // aTop text
0
} ;
const uint16 qfg2PatchImportDialog [ ] = {
PATCH_ADDTOOFFSET | + 5 ,
0x48 , // ret
PATCH_END
} ;
// script, description, magic DWORD, adjust
const SciScriptSignature qfg2Signatures [ ] = {
2010-11-22 00:20:00 +00:00
{ 944 , " import dialog continuous calls " , 1 , PATCH_MAGICDWORD ( 0x20 , 0x30 , 0x0b , 0x00 ) , - 1 , qfg2SignatureImportDialog , qfg2PatchImportDialog } ,
SCI_SIGNATUREENTRY_TERMINATOR
} ;
// ===========================================================================
// Patch for the import screen in QFG3, same as the one for QFG2 above
const byte qfg3SignatureImportDialog [ ] = {
15 ,
0x63 , 0x2a , // pToa text
0x31 , 0x0b , // bnt [next state]
0x7a , // push2
0x39 , 0x03 , // pushi 03
0x36 , // push
0x43 , 0x72 , 0x04 , // callk Memory 4
0x35 , 0x00 , // ldi 00
0x65 , 0x2a , // aTop text
0
} ;
const uint16 qfg3PatchImportDialog [ ] = {
PATCH_ADDTOOFFSET | + 4 ,
0x48 , // ret
PATCH_END
} ;
2012-10-07 12:24:16 +02:00
// ===========================================================================
// Patch for the Woo dialog option in Uhura's conversation. Bug #3040722
// Problem: The Woo dialog option (0xffb5) is negative, and therefore
// treated as an option opening a submenu. This leads to uhuraTell::doChild
// being called, which calls hero::solvePuzzle and then proceeds with
// Teller::doChild to open the submenu. However, there is no actual submenu
// defined for option -75 since -75 does not show up in uhuraTell::keys.
// This will cause Teller::doChild to run out of bounds while scanning through
// uhuraTell::keys.
// Strategy: there is another conversation option in uhuraTell::doChild calling
// hero::solvePuzzle (0xfffc) which does a ret afterwards without going to
// Teller::doChild. We jump to this call of hero::solvePuzzle to get that same
// behaviour.
const byte qfg3SignatureWooDialog [ ] = {
30 ,
0x67 , 0x12 , // pTos 12 (query)
0x35 , 0xb6 , // ldi b6
0x1a , // eq?
0x2f , 0x05 , // bt 05
0x67 , 0x12 , // pTos 12 (query)
0x35 , 0x9b , // ldi 9b
0x1a , // eq?
0x31 , 0x0c , // bnt 0c
0x38 , 0x97 , 0x02 , // pushi 0297
0x7a , // push2
0x38 , 0x0c , 0x01 , // pushi 010c
0x7a , // push2
0x81 , 0x00 , // lag 00
0x4a , 0x08 , // send 08
0x67 , 0x12 , // pTos 12 (query)
0x35 , 0xb5 , // ldi b5
0
} ;
const uint16 qfg3PatchWooDialog [ ] = {
PATCH_ADDTOOFFSET | + 0x29 ,
0x33 , 0x11 , // jmp to 0x6a2, the call to hero::solvePuzzle for 0xFFFC
PATCH_END
} ;
2010-11-22 00:20:00 +00:00
// script, description, magic DWORD, adjust
const SciScriptSignature qfg3Signatures [ ] = {
2010-11-30 00:26:00 +00:00
{ 944 , " import dialog continuous calls " , 1 , PATCH_MAGICDWORD ( 0x2a , 0x31 , 0x0b , 0x7a ) , - 1 , qfg3SignatureImportDialog , qfg3PatchImportDialog } ,
2012-10-07 12:24:16 +02:00
{ 440 , " dialog crash when asking about Woo " , 1 , PATCH_MAGICDWORD ( 0x67 , 0x12 , 0x35 , 0xb5 ) , - 26 , qfg3SignatureWooDialog , qfg3PatchWooDialog } ,
2010-11-21 23:43:14 +00:00
SCI_SIGNATUREENTRY_TERMINATOR
} ;
2010-08-12 22:25:15 +00:00
// ===========================================================================
// script 298 of sq4/floppy has an issue. object "nest" uses another property
// which isn't included in property count. We return 0 in that case, the game
// adds it to nest::x. The problem is that the script also checks if x exceeds
// we never reach that of course, so the pterodactyl-flight will go endlessly
// we could either calculate property count differently somehow fixing this
2010-08-12 22:27:28 +00:00
// but I think just patching it out is cleaner (ffs. bug #3037938)
2010-08-12 22:25:15 +00:00
const byte sq4FloppySignatureEndlessFlight [ ] = {
8 ,
0x39 , 0x04 , // pushi 04 (selector x)
0x78 , // push1
0x67 , 0x08 , // pTos 08 (property x)
0x63 , 0x44 , // pToa 44 (invalid property)
0x02 , // add
0
} ;
2010-11-16 22:36:06 +00:00
// Similar to the above, for the German version (ffs. bug #3110215)
const byte sq4FloppySignatureEndlessFlightGerman [ ] = {
8 ,
0x39 , 0x04 , // pushi 04 (selector x)
0x78 , // push1
0x67 , 0x08 , // pTos 08 (property x)
0x63 , 0x4c , // pToa 4c (invalid property)
0x02 , // add
0
} ;
2010-08-12 22:25:15 +00:00
const uint16 sq4FloppyPatchEndlessFlight [ ] = {
PATCH_ADDTOOFFSET | + 5 ,
0x35 , 0x03 , // ldi 03 (which would be the content of the property)
PATCH_END
} ;
2010-12-02 13:37:39 +00:00
// 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.
// Patch 1: iconTextSwitch::show, called when the text options button is shown.
// This is patched to add the "Both" text resource (i.e. we end up with
// "Speech", "Text" and "Both")
const byte sq4CdSignatureTextOptionsButton [ ] = {
11 ,
0x35 , 0x01 , // ldi 0x01
0xa1 , 0x53 , // sag 0x53
0x39 , 0x03 , // pushi 0x03
0x78 , // push1
0x39 , 0x09 , // pushi 0x09
0x54 , 0x06 , // self 0x06
0
} ;
const uint16 sq4CdPatchTextOptionsButton [ ] = {
PATCH_ADDTOOFFSET | + 7 ,
0x39 , 0x0b , // pushi 0x0b
PATCH_END
} ;
2012-06-28 03:27:34 +03:00
// Patch 2: Adjust a check in babbleIcon::init, which handles the babble icon
// (e.g. the two guys from Andromeda) shown when dying/quitting.
// Fixes bug #3538418.
const byte sq4CdSignatureBabbleIcon [ ] = {
7 ,
0x89 , 0x5a , // lsg 5a
0x35 , 0x02 , // ldi 02
0x1a , // eq?
0x31 , 0x26 , // bnt 26 [02a7]
0
} ;
const uint16 sq4CdPatchBabbleIcon [ ] = {
0x89 , 0x5a , // lsg 5a
0x35 , 0x01 , // ldi 01
0x1a , // eq?
0x2f , 0x26 , // bt 26 [02a7]
PATCH_END
} ;
// Patch 3: Add the ability to toggle among the three available options,
2010-12-02 13:37:39 +00:00
// when the text options button is clicked: "Speech", "Text" and "Both".
// Refer to the patch above for additional details.
// iconTextSwitch::doit (called when the text options button is clicked)
const byte sq4CdSignatureTextOptions [ ] = {
32 ,
0x89 , 0x5a , // lsg 0x5a (load global 90 to stack)
0x3c , // dup
0x35 , 0x01 , // ldi 0x01
0x1a , // eq? (global 90 == 1)
0x31 , 0x06 , // bnt 0x06 (0x0691)
0x35 , 0x02 , // ldi 0x02
0xa1 , 0x5a , // sag 0x5a (save acc to global 90)
0x33 , 0x0a , // jmp 0x0a (0x69b)
0x3c , // dup
0x35 , 0x02 , // ldi 0x02
0x1a , // eq? (global 90 == 2)
0x31 , 0x04 , // bnt 0x04 (0x069b)
0x35 , 0x01 , // ldi 0x01
0xa1 , 0x5a , // sag 0x5a (save acc to global 90)
0x3a , // toss
0x38 , 0xd9 , 0x00 , // pushi 0x00d9
0x76 , // push0
0x54 , 0x04 , // self 0x04
0x48 , // ret
0
} ;
const uint16 sq4CdPatchTextOptions [ ] = {
0x89 , 0x5a , // lsg 0x5a (load global 90 to stack)
0x3c , // dup
0x35 , 0x03 , // ldi 0x03 (acc = 3)
0x1a , // eq? (global 90 == 3)
0x2f , 0x07 , // bt 0x07
0x89 , 0x5a , // lsg 0x5a (load global 90 to stack again)
0x35 , 0x01 , // ldi 0x01 (acc = 1)
0x02 , // add: acc = global 90 (on stack) + 1 (previous acc value)
0x33 , 0x02 , // jmp 0x02
0x35 , 0x01 , // ldi 0x01 (reset acc to 1)
0xa1 , 0x5a , // sag 0x5a (save acc to global 90)
0x33 , 0x03 , // jmp 0x03 (jump over the wasted bytes below)
0x34 , 0x00 , 0x00 , // ldi 0x0000 (waste 3 bytes)
0x3a , // toss
// (the rest of the code is the same)
PATCH_END
} ;
2010-09-06 11:58:28 +00:00
// script, description, magic DWORD, adjust
2010-08-12 22:25:15 +00:00
const SciScriptSignature sq4Signatures [ ] = {
2010-11-16 22:36:06 +00:00
{ 298 , " Floppy: endless flight " , 1 , PATCH_MAGICDWORD ( 0x67 , 0x08 , 0x63 , 0x44 ) , - 3 , sq4FloppySignatureEndlessFlight , sq4FloppyPatchEndlessFlight } ,
{ 298 , " Floppy (German): endless flight " , 1 , PATCH_MAGICDWORD ( 0x67 , 0x08 , 0x63 , 0x4c ) , - 3 , sq4FloppySignatureEndlessFlightGerman , sq4FloppyPatchEndlessFlight } ,
2010-12-02 13:37:39 +00:00
{ 818 , " CD: Speech and subtitles option " , 1 , PATCH_MAGICDWORD ( 0x89 , 0x5a , 0x3c , 0x35 ) , 0 , sq4CdSignatureTextOptions , sq4CdPatchTextOptions } ,
2012-06-28 03:27:34 +03:00
{ 0 , " CD: Babble icon speech and subtitles fix " , 1 , PATCH_MAGICDWORD ( 0x89 , 0x5a , 0x35 , 0x02 ) , 0 , sq4CdSignatureBabbleIcon , sq4CdPatchBabbleIcon } ,
2010-12-02 13:37:39 +00:00
{ 818 , " CD: Speech and subtitles option button " , 1 , PATCH_MAGICDWORD ( 0x35 , 0x01 , 0xa1 , 0x53 ) , 0 , sq4CdSignatureTextOptionsButton , sq4CdPatchTextOptionsButton } ,
2010-10-23 15:46:50 +00:00
SCI_SIGNATUREENTRY_TERMINATOR
2010-08-12 22:25:15 +00:00
} ;
2011-04-02 19:53:26 +02:00
const byte sq1vgaSignatureEgoShowsCard [ ] = {
25 ,
0x38 , 0x46 , 0x02 , // push 0x246 (set up send frame to set timesShownID)
0x78 , // push1
0x38 , 0x46 , 0x02 , // push 0x246 (set up send frame to get timesShownID)
0x76 , // push0
0x51 , 0x7c , // class DeltaurRegion
0x4a , 0x04 , // send 0x04 (get timesShownID)
0x36 , // push
0x35 , 0x01 , // ldi 1
0x02 , // add
0x36 , // push
0x51 , 0x7c , // class DeltaurRegion
0x4a , 0x06 , // send 0x06 (set timesShownID)
0x36 , // push (wrong, acc clobbered by class, above)
0x35 , 0x03 , // ldi 0x03
0x22 , // lt?
0 } ;
// Note that this script patch is merely a reordering of the
// instructions in the original script.
const uint16 sq1vgaPatchEgoShowsCard [ ] = {
0x38 , 0x46 , 0x02 , // push 0x246 (set up send frame to get timesShownID)
0x76 , // push0
0x51 , 0x7c , // class DeltaurRegion
0x4a , 0x04 , // send 0x04 (get timesShownID)
0x36 , // push
0x35 , 0x01 , // ldi 1
0x02 , // add
0x36 , // push (this push corresponds to the wrong one above)
0x38 , 0x46 , 0x02 , // push 0x246 (set up send frame to set timesShownID)
0x78 , // push1
0x36 , // push
0x51 , 0x7c , // class DeltaurRegion
0x4a , 0x06 , // send 0x06 (set timesShownID)
0x35 , 0x03 , // ldi 0x03
0x22 , // lt?
PATCH_END } ;
// script, description, magic DWORD, adjust
const SciScriptSignature sq1vgaSignatures [ ] = {
2011-06-20 00:59:48 +02:00
{ 58 , " Sarien armory droid zapping ego first time " , 1 , PATCH_MAGICDWORD ( 0x72 , 0x88 , 0x15 , 0x36 ) , - 70 ,
2011-04-02 19:53:26 +02:00
sq1vgaSignatureEgoShowsCard , sq1vgaPatchEgoShowsCard } ,
SCI_SIGNATUREENTRY_TERMINATOR } ;
2010-08-06 22:36:46 +00:00
// will actually patch previously found signature area
void Script : : applyPatch ( const uint16 * patch , byte * scriptData , const uint32 scriptSize , int32 signatureOffset ) {
2010-09-12 22:09:01 +00:00
byte orgData [ PATCH_VALUELIMIT ] ;
2010-08-06 22:36:46 +00:00
int32 offset = signatureOffset ;
uint16 patchWord = * patch ;
2010-09-12 22:09:01 +00:00
// Copy over original bytes from script
uint32 orgDataSize = scriptSize - offset ;
if ( orgDataSize > PATCH_VALUELIMIT )
orgDataSize = PATCH_VALUELIMIT ;
memcpy ( & orgData , & scriptData [ offset ] , orgDataSize ) ;
2010-08-06 22:36:46 +00:00
while ( patchWord ! = PATCH_END ) {
2010-09-12 22:09:01 +00:00
uint16 patchValue = patchWord & PATCH_VALUEMASK ;
switch ( patchWord & PATCH_COMMANDMASK ) {
case PATCH_ADDTOOFFSET :
// add value to offset
offset + = patchValue & ~ PATCH_ADDTOOFFSET ;
break ;
case PATCH_GETORIGINALBYTE :
// get original byte from script
if ( patchValue > = orgDataSize )
error ( " patching: can not get requested original byte from script " ) ;
scriptData [ offset ] = orgData [ patchValue ] ;
offset + + ;
break ;
case PATCH_ADJUSTWORD : {
// Adjust word right before current position
byte * adjustPtr = & scriptData [ offset - 2 ] ;
uint16 adjustWord = READ_LE_UINT16 ( adjustPtr ) ;
adjustWord + = patchValue ;
WRITE_LE_UINT16 ( adjustPtr , adjustWord ) ;
break ;
}
case PATCH_ADJUSTWORD_NEG : {
// Adjust word right before current position (negative way)
byte * adjustPtr = & scriptData [ offset - 2 ] ;
uint16 adjustWord = READ_LE_UINT16 ( adjustPtr ) ;
adjustWord - = patchValue ;
WRITE_LE_UINT16 ( adjustPtr , adjustWord ) ;
break ;
}
default :
scriptData [ offset ] = patchValue & 0xFF ;
2010-08-06 22:36:46 +00:00
offset + + ;
}
patch + + ;
patchWord = * patch ;
2011-06-20 00:59:48 +02:00
}
2010-08-06 22:36:46 +00:00
}
// will return -1 if no match was found, otherwise an offset to the start of the signature match
int32 Script : : findSignature ( const SciScriptSignature * signature , 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 ;
const uint32 magicDWord = signature - > 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 ) {
2010-09-05 11:10:49 +00:00
if ( magicDWord = = READ_UINT32 ( scriptData + DWordOffset ) ) {
2010-08-06 22:36:46 +00:00
// magic DWORD found, check if actual signature matches
uint32 offset = DWordOffset + signature - > magicOffset ;
uint32 byteOffset = offset ;
const byte * signatureData = signature - > data ;
byte matchAdjust = 1 ;
while ( matchAdjust ) {
byte matchBytesCount = * signatureData + + ;
if ( ( byteOffset + matchBytesCount ) > scriptSize ) // Out-Of-Bounds?
break ;
if ( memcmp ( signatureData , & scriptData [ byteOffset ] , matchBytesCount ) ) // Byte-Mismatch?
break ;
// those bytes matched, adjust offsets accordingly
signatureData + = matchBytesCount ;
byteOffset + = matchBytesCount ;
// get offset...
matchAdjust = * signatureData + + ;
byteOffset + = matchAdjust ;
}
if ( ! matchAdjust ) // all matches worked?
return offset ;
}
DWordOffset + + ;
}
// nothing found
return - 1 ;
}
void Script : : matchSignatureAndPatch ( uint16 scriptNr , byte * scriptData , const uint32 scriptSize ) {
const SciScriptSignature * signatureTable = NULL ;
2010-09-05 11:30:40 +00:00
switch ( g_sci - > getGameId ( ) ) {
case GID_ECOQUEST :
2010-08-10 15:18:18 +00:00
signatureTable = ecoquest1Signatures ;
2010-09-05 11:30:40 +00:00
break ;
case GID_ECOQUEST2 :
2010-08-16 15:22:43 +00:00
signatureTable = ecoquest2Signatures ;
2010-09-05 11:30:40 +00:00
break ;
2010-12-28 21:05:30 +00:00
case GID_FANMADE :
signatureTable = fanmadeSignatures ;
break ;
2010-09-05 11:30:40 +00:00
case GID_FREDDYPHARKAS :
2010-08-24 22:24:25 +00:00
signatureTable = freddypharkasSignatures ;
2010-09-05 11:30:40 +00:00
break ;
case GID_GK1 :
2010-08-09 18:42:07 +00:00
signatureTable = gk1Signatures ;
2010-09-05 11:30:40 +00:00
break ;
case GID_KQ5 :
2010-08-11 20:39:13 +00:00
signatureTable = kq5Signatures ;
2010-09-05 11:30:40 +00:00
break ;
2011-02-24 00:10:01 +02:00
case GID_KQ6 :
signatureTable = kq6Signatures ;
break ;
2010-09-05 11:30:40 +00:00
case GID_LAURABOW2 :
2010-08-12 13:45:39 +00:00
signatureTable = laurabow2Signatures ;
2010-09-05 11:30:40 +00:00
break ;
2011-03-12 23:48:56 +02:00
case GID_LONGBOW :
signatureTable = longbowSignatures ;
break ;
2010-09-05 11:30:40 +00:00
case GID_LSL6 :
2010-08-06 22:36:46 +00:00
signatureTable = larry6Signatures ;
2010-09-05 11:30:40 +00:00
break ;
case GID_MOTHERGOOSE256 :
2010-08-23 16:33:19 +00:00
signatureTable = mothergoose256Signatures ;
2010-09-05 11:30:40 +00:00
break ;
case GID_QFG1VGA :
2010-08-24 21:53:06 +00:00
signatureTable = qfg1vgaSignatures ;
2010-09-05 11:30:40 +00:00
break ;
2010-11-21 23:43:14 +00:00
case GID_QFG2 :
signatureTable = qfg2Signatures ;
break ;
2010-11-22 00:20:00 +00:00
case GID_QFG3 :
signatureTable = qfg3Signatures ;
break ;
2011-04-02 19:53:26 +02:00
case GID_SQ1 :
signatureTable = sq1vgaSignatures ;
break ;
2010-09-05 11:30:40 +00:00
case GID_SQ4 :
2010-08-12 22:25:15 +00:00
signatureTable = sq4Signatures ;
2010-09-05 11:30:40 +00:00
break ;
default :
break ;
}
2010-08-06 22:36:46 +00:00
if ( signatureTable ) {
while ( signatureTable - > data ) {
if ( scriptNr = = signatureTable - > scriptNr ) {
2010-09-06 11:58:28 +00:00
int32 foundOffset = 0 ;
int16 applyCount = signatureTable - > applyCount ;
do {
foundOffset = findSignature ( signatureTable , scriptData , scriptSize ) ;
if ( foundOffset ! = - 1 ) {
// found, so apply the patch
2011-02-08 23:07:11 +00:00
debugC ( kDebugLevelScripts , " matched and patched %s on script %d offset %d " , signatureTable - > description , scriptNr , foundOffset ) ;
2010-09-06 11:58:28 +00:00
applyPatch ( signatureTable - > patch , scriptData , scriptSize , foundOffset ) ;
}
applyCount - - ;
} while ( ( foundOffset ! = - 1 ) & & ( applyCount ) ) ;
2010-08-06 22:36:46 +00:00
}
signatureTable + + ;
}
}
}
} // End of namespace Sci