diff options
| author | Willem Jan Palenstijn | 2015-07-22 22:37:40 +0200 | 
|---|---|---|
| committer | Willem Jan Palenstijn | 2015-07-22 22:43:42 +0200 | 
| commit | 6ec9c81b575f13b2c4b30aeac592ebf2557b5890 (patch) | |
| tree | 503d50902bad2d800165593039d08d5ccf0c98ab /engines/sci/engine/script_patches.cpp | |
| parent | 5ec05f6b647c5ea41418be7ed19ad381f97cabd8 (diff) | |
| parent | 4e5c8d35f7e133e2e72a846fdbd54900c91eeb73 (diff) | |
| download | scummvm-rg350-6ec9c81b575f13b2c4b30aeac592ebf2557b5890.tar.gz scummvm-rg350-6ec9c81b575f13b2c4b30aeac592ebf2557b5890.tar.bz2 scummvm-rg350-6ec9c81b575f13b2c4b30aeac592ebf2557b5890.zip | |
Merge branch 'master' into mm
Conflicts:
	engines/access/access.cpp
	engines/access/asurface.h
	engines/access/bubble_box.cpp
	engines/access/bubble_box.h
	engines/access/martian/martian_game.cpp
	engines/access/player.cpp
	engines/access/player.h
	engines/access/resources.cpp
	engines/access/screen.cpp
	engines/access/screen.h
	engines/access/sound.cpp
	engines/access/sound.h
Diffstat (limited to 'engines/sci/engine/script_patches.cpp')
| -rw-r--r-- | engines/sci/engine/script_patches.cpp | 743 | 
1 files changed, 633 insertions, 110 deletions
| diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 03cd1d06e9..6915e12a0e 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -94,6 +94,8 @@ 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 @@ -119,6 +121,8 @@ enum ScriptPatcherSelectors {  	SELECTOR_deskSarg,  	SELECTOR_localize,  	SELECTOR_put, +	SELECTOR_say, +	SELECTOR_contains,  	SELECTOR_solvePuzzle,  	SELECTOR_timesShownID,  	SELECTOR_startText, @@ -562,12 +566,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  }; @@ -1421,6 +1425,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 +1479,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  }; @@ -2087,21 +2123,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 +2161,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 +2185,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 +2212,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 +2409,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  }; @@ -2282,10 +2529,158 @@ 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 +}; + +//          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 },  	SCI_SIGNATUREENTRY_TERMINATOR  }; @@ -2345,6 +2740,102 @@ 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 +}; +  // 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 +2930,10 @@ 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,     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 +2964,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" @@ -2595,6 +3111,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 +3187,7 @@ ScriptPatcher::ScriptPatcher() {  		_selectorIdTable[selectorNr] = -1;  	_runtimeTable = NULL; +	_isMacSci11 = false;  }  ScriptPatcher::~ScriptPatcher() { @@ -2678,7 +3196,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 +3260,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,8 +3287,94 @@ void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, byte *sc  	}  } +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; + +				switch (sigCommand) { +				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_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; +			} +			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; + +		signatureData++; +		sigWord = *signatureData; +	} + +	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(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize, const bool isMacSci11) { +int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, 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; @@ -2782,89 +3386,8 @@ int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciS  		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; -					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; -					} -					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; -				} -				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; - -				signatureData++; -				sigWord = *signatureData; -			} -			if (sigWord == SIG_END) // signature fully matched? +			if (verifySignature(offset, patchEntry->signatureData, patchEntry->description, scriptData, scriptSize))  				return offset;  		}  		DWordOffset++; @@ -2875,7 +3398,7 @@ int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciS  // 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) { +void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable) {  	const SciScriptPatcherEntry *curEntry = patchTable;  	SciScriptPatcherRuntimeEntry *curRuntimeEntry;  	Selector curSelector = -1; @@ -2943,7 +3466,7 @@ void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable, bool  						curData++; curWord = *curData;  						if (curWord & SIG_COMMANDMASK)  							error("Script-Patcher: signature entry inconsistent\nFaulty patch: '%s'", curEntry->description); -						if (!isMacSci11) { +						if (!_isMacSci11) {  							byte1 = curValue;  							byte2 = curWord & SIG_BYTEMASK;  						} else { @@ -2958,7 +3481,7 @@ void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable, bool  							curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);  							_selectorIdTable[curValue] = curSelector;  						} -						if (!isMacSci11) { +						if (!_isMacSci11) {  							byte1 = curSelector & 0x00FF;  							byte2 = curSelector >> 8;  						} else { @@ -3120,7 +3643,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 +3651,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 +3686,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)); | 
